阿里开源富容器引擎 PouchContainer 的 network 连接机制

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: PouchContainer 是阿里巴巴集团开源的高效、轻量级企业级富容器引擎技术,拥有隔离性强、可移植性高、资源占用少等特性。可以帮助企业快速实现存量业务容器化,同时提高超大规模下数据中心的物理资源利用率。

作者:酥蕊

引言

PouchContainer 是阿里巴巴集团开源的高效、轻量级企业级富容器引擎技术,拥有隔离性强、可移植性高、资源占用少等特性。可以帮助企业快速实现存量业务容器化,同时提高超大规模下数据中心的物理资源利用率

PouchContainer 源自阿里巴巴内部场景,诞生初期,在如何为互联网应用保驾护航方面,倾尽了阿里巴巴工程师们的设计心血。PouchContainer 的强隔离、富容器等技术特性是最好的证明。在阿里巴巴的体量规模下,PouchContainer 对业务的支撑得到双 11 史无前例的检验,开源之后,阿里容器成为一项普惠技术,定位于「助力企业快速实现存量业务容器化」。

本文将给大家介绍 PouchContainer 实现 network 的机制以及将容器连接到 network 上的原理。为了充分阐述 network 的连接机制,本文将以Connect方法为例,叙述如何动态地将一个 container 连接到一个已存在的 network 上。

1. PouchContainer 实现 network 的机制

在目前的容器网络虚拟化技术中,Docker 推行的 CNM (Container Network Model)模型是一种通用的解决方案,CNM 构建了一种成熟的容器虚拟化网络模型,并定义了多种供开发者调用的标准化接口。PouchContainer 沿用了 CNM 模型,基于 libnetwork 来实现容器间通信。下面先对 Sandbox、Endpoint 和 Network 这三个 CNM 中的核心组件进行介绍。

Sandbox

Sandbox 一词在不同的机制里,被分别赋予了不同的定义。例如,在 CRI(container runtime interface)里面 sandbox 就代表着pod的概念。而在 CNM 模型里,Sandbox 代表着一个容器的网络栈配置,包含管理容器的网卡,路由表以及 DNS 设置。Sandbox 的具体实现可以通过 linux 系统的 network namespace,一个 FreeBSD Jail 或者其他类似的概念。一个 Sandbox 可以包含多个 Endpoints。

Endpoint

一个 Endpoint 将 Sandbox 连接到 Network 上。一个 Endpoint 的实现可以通过 veth pair,Open vSwitch internal port 或者其他的方式。比较常见的方法是用 veth pair,顾名思义,veth pair一定是成对出现的,因此会存在 veth0 和 veth1 两块网卡。创建容器时,其中一块会被设置到容器内部,充当容器内部的eth0,所有目的地址为容器 IP 的数据包都要经过 eth0 网卡;另一块(以下称为 veth 设备)则会被连接到宿主机的网桥上。从 veth 设备出去的数据包,会转发到对应的 eth0 设备上,当数据包的目的地址为 eth0 设备的 IP 时,就能被内核协议栈处理。用 veth pair 来连接两个 network namespace,从而建立网络连通关系。一个 Endpoint 只能属于一个 Network,也只能属于一个 Sandbox。

Network

一个 Network 是一组可以相互通信的 Endpoints 的集合。一个 Network 的实现可以通过 Linux bridge,vlan 或者其他方式。值得一提的是,一个 Network 中可以包含很多个 Endpoints。

可以看到,在如下图所示的结构下,Container A 和Container B 同属于 Backend Network,这两个 Container通过各自紫色的 Endpoint 构成 Network 连接;Container B和 Container C 同属于 Frontend Network,通过蓝色的 Endpoint 构成 Network 连接。因此Container A和Container B之间可以通信,Container B和Container C之间也可以通信。

接下来重点看一下 Container B 内部的两个 Endpoints,虽然 Backend Network 和 Frontend Network 在Container B 内都有各自对应的 Endpoint,但紫色 Endpoint 和蓝色 Endpoint 间不构成通信。因此 Backend Network 和 Frontend Network 是两个完全隔离的 Network,并不因为连接同一个 Container 而产生连通。显而易见,Container A 和 Container C 间其实是无法通信的。

net.jpg | center | 600x295.16129032258067

2. PouchContainer 内置的 network 模式

2.1 bridge 模式

bridge 模式是 PouchContainer 默认的网络模式,在创建容器不指定 network 模式,即不写--net参数,该容器就会以 bridge 模式创建。pouchd启动的时候,会自动在主机上创建一个虚拟网桥 p0。后续以 bridge 模式创建容器时,pouchd从 p0 网桥所在的 IP 网段中选取一个未使用的 IP 分配给容器的 eth0 网卡,p0 的 IP 是这些容器的默认网关。


<div id="u6leqm" data-type="image" data-display="block" data-align="center" data-src="https://cdn.nlark.com/lark/0/2018/jpeg/95844/1533279811022-5e81e8e8-ec32-48c4-bf68-57130ee51964.jpeg" data-width="400">
  <img src="https://cdn.nlark.com/lark/0/2018/jpeg/95844/1533279811022-5e81e8e8-ec32-48c4-bf68-57130ee51964.jpeg" width="400" />
</div>


2.2 host 模式

在启动容器的时候,选择 host 模式,那么容器将不会获得独立的 network namespace,而是和主机共享 network namespace。因此,这个容器也就没有自己的网卡和 IP 配置,会使用主机的 IP 和端口,但 fs 和 pid 等与主机还是隔离的。

host.jpg | center | 400x272.5274725274725

2.3 container 模式

以 container 模式创建的容器,会和已经存在的容器共享一个 network namespace,直接沿用其 veth 设备对。

container.jpg | center | 400x358.5185185185185

2.4 none 模式

使用 none 模式创建的容器,拥有独立的 network namespace,但是不会对容器进行任何的网络配置。因此,可以认为 none 模式下的容器,是不和其它容器通信的。不过,在容器创建后,可以再给它添加网卡、配置 IP,这样就可以与同一个 network 下的容器通信了。

none.jpg | center | 400x272.5274725274725

2.5 CNM 与 network 模式的概念交叉

一个 Network 是一个唯一的、可识别的 Endpoint 组,组内的 Endpoint 可以相互通讯。对比 CNM 来看,Endpoint 可以简单理解成 veth 设备对,容器的 Sandbox 里可以有多个 Endpoints,每个 Endpoint 代表和一个特定 Network 的连接关系。

3. network connect 的流程分析

// daemon/mgr/container.go

// Connect is used to connect a container to a network.
func (mgr *ContainerManager) Connect(ctx context.Context, name string, networkIDOrName string, epConfig *types.EndpointSettings) error {
​    ……
​    if err := mgr.updateNetworkConfig(c, n.Name, epConfig); err != nil {
​        return err
​    } else 
    if err := mgr.connectToNetwork(ctx, c, networkIDOrName, epConfig); err != nil {
​        return err
​    }
​    return c.Write(mgr.Store)
}

可以看到在Connect函数里,首先根据传入的参数获取到具体的 container 和 network。而epConfig参数里面,存放的是在 CLI 端通过 flag 传入的参数,如 container 在特定 network 中的别名、指定的 IP 范围等。

查看c.State.Status来判断 container 此时的状态,dead 状态的 container 是无法执行 connect 操作的。对于非 running 但是还 live的container,只是简单地调用updateNetworkConfig()来更新 container 的网络配置,将传入的epConfig加入到容器的 network 配置中。在这种情况下,不会为 container 分配网卡,因此 container 并没有成功连通到 network 中。对于 running 状态的 container,调用connectToNetwork()来进行后续的操作,connectToNetwork()会根据给定的 network 和 container 进行网卡的配置,再在主机上分配一个网卡,最后将网卡加入到 container 的 sandbox 里面。这样,container 就成功地连接到 network 上了!具体的流程会在后续进行解析。

c.Write(mgr.Store)的作用,是将 container 连接到 network 上的一系列配置写入 container 的 metadata 里面,这样就保证了数据的持久化。否则,建立的 network 连接只是一次性的,所有的数据和相关配置在pouchd重启后都会丢失。

// daemon/mgr/container.go

func (mgr *ContainerManager) connectToNetwork(ctx context.Context, container *Container, networkIDOrName string, epConfig *types.EndpointSettings) (err error) {
​    ……
​    endpoint := mgr.buildContainerEndpoint(container)
    ……
​    if _, err := mgr.NetworkMgr.EndpointCreate(ctx, endpoint); err != nil {
​        ……
​    }
​    return mgr.updateNetworkConfig(container, networkIDOrName, endpoint.EndpointConfig)
}

endpoint 里面包含三部分的信息,一部分的信息来自于 container,一部分的信息来自 network,最后一部分信息是 connect 命令里 flag 中的配置。buildContainerEndpoint()的逻辑比较简单,就是获取到 endpoint 需要的 container 相关信息。随后调用了NetworkMgrEndpointCreate()来进行具体的构建。

// daemon/mgr/network.go

// EndpointCreate is used to create network endpoint.
func (nm *NetworkManager) EndpointCreate(ctx context.Context, endpoint *types.Endpoint) (string, error) {
​    ……
​    // create endpoint
​    epOptions, err := endpointOptions(n, endpoint)
    ……
​    endpointName := containerID[:8]
​    ep, err := n.CreateEndpoint(endpointName, epOptions...)
​    ……

​    // create sandbox
​    sb := nm.getNetworkSandbox(containerID)
​    if sb == nil {
​        sandboxOptions, err := buildSandboxOptions(nm.config, endpoint)
​        ……
​        sb, err = nm.controller.NewSandbox(containerID, sandboxOptions...)
​        ……
​    }

​    // endpoint joins into sandbox
​    joinOptions, err := joinOptions(endpoint)
​    ……
​    if err := ep.Join(sb, joinOptions...); err != nil {
​        return "", fmt.Errorf("failed to join sandbox(%v)", err)
​    }

​    // update endpoint settings
​    epInfo := ep.Info()
​    if epInfo.Gateway() != nil {
​        endpointConfig.Gateway = epInfo.Gateway().String()
​    }
​    if epInfo.GatewayIPv6().To16() != nil {
​        endpointConfig.IPV6Gateway = epInfo.GatewayIPv6().String()
​    }
    endpoint.ID = ep.ID()
​    endpointConfig.EndpointID = ep.ID()
​    endpointConfig.NetworkID = n.ID()
​    iface := epInfo.Iface()
    ……
​    return endpointName, nil

}

创建 endpoint 的整个过程,都是调用 libnetwork 来实现的。首先调用endpointOptions()来构建接口要求的EndpointOption参数,这个 setter 函数类型的参数能将不同的 option 传递给 network 和 endpoint 的接口。随后调用 libnetwork 的
CreateEndpoint()接口来进行具体的构建。CreateEndpoint()执行的实际工作包括为这个 Endpoint 分配 IP 和接口(Iface),对应的配置会被应用到 Endpoint 中,其中包括 iptables 的配置规则和端口信息等。

sandbox 所代表的就是 container 独有的 network namespace,其创建也是基于 libnetwork。sandbox 里面包含 container 建立网络通信的标志性信息,如 IP 地址、Mac 地址、路由和 DNS 等配置。会对已存在的 sandbox 进行遍历,判断是否存在相应的 sandbox,存在的话就直接返回对应的 sandbox。在 none 模式下,container 沿用主机的 namespace,返回的 sandbox 为空,这时候会创建一个新的 sandbox。sandbox 的创建过程,就是调用 namespace 和 cgroup 来创建一个独立 sandbox 空间。

将 endpoint 加入到 sandbox 的操作,实际上就是将网卡分配给 container 的过程,将 endpoint 分配到的网络资源注入到 sandbox 中。网卡是建立连接的核心,container 通过虚拟网卡连接到 network,从而与其它 container 进行通信。

最后一步,将变化同步更新到 endpoint 的配置里面。

4. 总结

回顾建立 network 连接的整个流程,可以简单的分成几步。container 在通信时需要唯一的 network namespace 来标志自己,那么就要有 sandbox 的创建;通信的实现需要网卡作为基础,那么就要有 endpoint 的创建;最后将endpoint  加入 sandbox,建立容器间通信的基础,连接的建立就成功完成了。
如果想更多了解 PouchContainer,请访问 https://pouchcontainer.io

目录
相关文章
|
11天前
|
消息中间件 人工智能 Kubernetes
解密开源Serverless容器框架:事件驱动篇
Knative是一款基于Kubernetes的开源Serverless框架,提供了云原生、跨平台的Serverless编排标准。作为Serverless中必不可少的事件驱动能力,Knative Eventing提供了云原生的事件驱动能力。
|
1月前
|
Ubuntu 网络安全 容器
KubeSphere 是一个开源的容器平台,提供丰富的功能和便捷的操作界面,适用于企业容器化部署和管理
KubeSphere 是一个开源的容器平台,提供丰富的功能和便捷的操作界面,适用于企业容器化部署和管理。本文详细介绍了如何在 Ubuntu 22.04 上安装 KubeSphere,包括系统要求、安装依赖项、设置防火墙、下载安装脚本、选择安装选项、验证安装结果等步骤,并提供了常见问题的解决方法。希望本文能为读者提供实用的参考和帮助。
37 3
|
1月前
|
Kubernetes 安全 容器
关于K8s,不错的开源工具
【10月更文挑战第12天】
|
3月前
|
Linux pouch 容器
CentOS7部署阿里巴巴开源的pouch容器管理工具实战
关于如何在CentOS 7.6操作系统上安装和使用阿里巴巴开源的Pouch容器管理工具的实战教程。
141 2
|
4月前
|
运维 Kubernetes 监控
|
18天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
161 77
|
26天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
4天前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
41 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
|
10天前
|
关系型数据库 应用服务中间件 PHP
实战~如何组织一个多容器项目docker-compose
本文介绍了如何使用Docker搭建Nginx、PHP和MySQL的环境。首先启动Nginx容器并查看IP地址,接着启动Alpine容器并安装curl测试连通性。通过`--link`方式或`docker-compose`配置文件实现服务间的通信。最后展示了Nginx配置文件和PHP代码示例,验证了各服务的正常运行。
31 3
实战~如何组织一个多容器项目docker-compose
|
19天前
|
数据建模 应用服务中间件 nginx
docker替换宿主与容器的映射端口和文件路径
通过正确配置 Docker 的端口和文件路径映射,可以有效地管理容器化应用程序,确保其高效运行和数据持久性。在生产环境中,动态替换映射配置有助于灵活应对各种需求变化。以上方法和步骤提供了一种可靠且易于操作的方案,帮助您轻松管理 Docker 容器的端口和路径映射。
63 3

相关产品

  • 容器服务Kubernetes版