https://www.nuagenetworks.net/blog/container-networking-standards/
https://xuxinkun.github.io/2016/07/22/cni-cnm/
虚拟机是一整套操作系统层级的虚拟化,它还会虚拟出虚拟网卡(virtual network interface cards (NIC)),这些虚拟网卡会和真正的物理机网卡相连接。
docker 实质上就是一个进程,被 container runtime 统一管理,还会共享一个 Linux kernel。所以,容器有更灵活的网络解决方案:
和宿主机是共享同一个network interface和 network namespace
连接另外的虚拟网络,使用这个虚拟网络的network interface和 network namespace
早期的容器网络设计把重点放在了如何连接一个宿主机上的容器,让他们可以和外界进行交互。
在”host”模式中,运行在一个宿主机上的容器使用 host 的network namespace,和宿主机一个ip。为了暴露容器,容器会占用宿主机上的一个端口,通过这个端口和外界通信。所以,你需要手动维护端口的分配,不要使不同的容器服务运行在一个端口上。
“bridge”模式,在”host”模式的基础上做了些改进。在该模式里面,容器被分配到了一个虚拟的局域网里,在这个network namespace 获得分配到的 ip 地址,由于 ip 地址是独立的,就解决了”host”模式中不同容器服务不能运行在同一端口的问题。不过还是有一个问题,如果容器想要和外界通信的话,还是需要使用 host 的 ip 地址。这时候需要用到 NAT 将 host-ip:port 转换成 private-ip:port。这一部分的 NAT 规则表示用Linux Iptables 维护的,这会在一定程度上影响性能(虽然不大)。
上述的两种模式都没有解决一个问题:多 host 网络解决方案。
CNI 和 CNM 的对比
CNM: Docker 公司(docker container runtime 背后的公司)提出。
CNI:CoreOS公司提出。
Kubernetes 在处理网络上,没有选择自己再独立创造一个,而是选择了其中的 CNI作为了自己的网络插件。(至于为什么不选择 CNM,可以看看这篇官方的解释:Why Kubernetes doesn’t use libnetwork)。不使用 CNM 最关键的一点,是 k8s 考虑到CNM 在一定程度上和 container runtime 联系相对比较紧密,不好解耦。
有了 k8s 这种巨无霸的选择之后,后来的很多项目都在 CNM 和 CNI 之间选择了 CNI。
下面是两种网络模型的详细对比:
CNM
CNM 的 api 包含了两部分:IPAM 插件和网络插件。IPAM 插件负责创建/删除 address pools、分配网络地址,network 插件负责创建/删除 networks、以及分配或收回容器的 ip 地址。事实上,这两种插件都可以实现所有的 API,你可以选择用 IPAM,也可以选择用 network,或者 BOTH。但是,container runtime 会在不同情况下使用到不同的插件,这带来了复杂性。还有就是,CNM 需要使用分布式存储系统来保存网络配置信息,例如 etcd。
CNI
CNI 对外暴露了从一个网络里面添加和剔除容器的接口。CNI 使用一个 json 配置文件保存网络配置信息。和 CNM 不一样,CNI 不需要一个额外的分布式存储引擎。
总结
CNI目前已经获得了众多开源项目的青睐,比如 K8S、Memos、Cloud Foundry。同时被Cloud Native Computing Foundation所认可。CNCF 背后有众多的科技大亨,所以可以预见,CNI 将会成为未来容器网络的标准。
Container Network Model(CNM)
CNM是一个被 Docker 提出的规范。现在已经被Cisco Contiv, Kuryr, Open Virtual Networking (OVN), Project Calico, VMware 和 Weave 这些公司和项目所采纳。
Libnetwork是CNM的原生实现。它为Docker daemon和网络驱动程序之间提供了接口。网络控制器负责将驱动和一个网络进行对接。每个驱动程序负责管理它所拥有的网络以及为该网络提供的各种服务,例如IPAM等等。由多个驱动支撑的多个网络可以同时并存。网络驱动可以按提供方被划分为原生驱动(libnetwork内置的或Docker支持的)或者远程驱动 (第三方插件)。原生驱动包括 none, bridge, overlay 以及 MACvlan。驱动也可以被按照适用范围被划分为本地(单主机)的和全局的 (多主机)。
『Network Sandbox』- 一个容器内部的网络栈。
『Endpoint』- 一个通常成对出现的网络接口。一端在网络容器内,另一端在网络内。 一个Endpoints可以加入一个网络。一个容器可以有多个endpoints。
『Network』- 一个endpoints的集合。该集合内的所有endpoints可以互联互通。
最后,CNM还支持标签(labels)。Lable是以key-value对定义的元数据。用户可以通过定义label这样的元数据来自定义libnetwork和驱动的行为。
Container Network Interface(CNI)
CNI是由CoreOS提出的一个容器网络规范。已采纳改规范的包括Apache Mesos, Cloud Foundry, Kubernetes, Kurma 和 rkt。另外 Contiv Networking, Project Calico 和 Weave这些项目也为CNI提供插件。
CNI 的规范比较小巧。它规定了一个容器runtime和网络插件之间的简单的契约。这个契约通过JSON的语法定义了CNI插件所需要提供的输入和输出。
一个容器可以被加入到被不同插件所驱动的多个网络之中。一个网络有自己对应的插件和唯一的名称。CNI 插件需要提供两个命令:一个用来将网络接口加入到指定网络,另一个用来将其移除。这两个接口分别在容器被创建和销毁的时候被调用。
CNI Flow
容器runtime首先需要分配一个网络命名空间以及一个容器ID。然后连同一些CNI配置参数传给网络驱动。接着网络驱动会将该容器连接到网络并将分配的IP地址以JSON的格式返回给容器runtime。
Mesos 是最新的加入CNI支持的项目。Cloud Foundry的支持也正在开发中。当前的Mesos网络使用宿主机模式,也就是说容器共享了宿主机的IP地址。Mesos正在尝试为每个容器提供一个自己的IP地址。这样做的目的是使得IT人员可以自行选择适合自己的组网方式。
目前,CNI的功能涵盖了IPAM, L2 和 L3。端口映射(L4)则由容器runtime自己负责。CNI也没有规定端口映射的规则。这样比较简化的设计对于Mesos来讲有些问题。端口映射是其中之一。另外一个问题是:当CNI的配置被改变时,容器的行为在规范中是没有定义的。为此,Mesos在CNI agent重启的时候,会使用该容器与CNI关联是的配置。
CNI和CNM
这两种方案都使用了驱动模型或者插件模型来为容器创建网络栈。这样的设计使得用户可以自由选择。两者都支持多个网络驱动被同时使用,也允许容器加入一个或多个网络。两者也都允许容器runtime在它自己的命名空间中启动网络。
这种模块化驱动的方式可与说对运维人员更有吸引力。因为运维人员可以比较灵活的选择适合现有模式的驱动。两种方案都提供了独立的扩展点,也就是插件的接口。这使得网络驱动可以创建、配置和连接网络。也使得IPAM可以配置,发现和管理IP地址。这种分离让编排变得容易。
CNM 模式下的网络驱动不能访问容器的网络命名空间。这样做的好处是libnetwork可以为冲突解决提供仲裁。一个例子是:两个独立的网络驱动提供同样的静态路由配置,但是却指向不同的下一跳IP地址。与此不同,CNI允许驱动访问容器的网络命名空间。CNI正在研究在类似情况下如何提供仲裁。
CNI支持与第三方IPAM的集成,可以用于任何容器runtime。CNM从设计上就仅仅支持Docker。由于CNI简单的设计,许多人认为编写CNI插件会比编写CNM插件来得简单。
这些模型增进了系统的模块化,增加了用户的选择。也促进了第三方网络插件以创新来提供更高级的网络功能。
总结
容器网络领域随着供应商和各种项目的不断变化而变化。有些被合并,例如Docker兼并SocketPlan;Flannel到Tigera的迁移 - Tigera的Canal将Calico和Flannel两个项目组合在一起了。 CoreOS会继续为Flannel提供支持,并且会将Canal集成在它们的企业级Kubernetes方案 - Tectonic 里面。同时,新的发布版本也带来新的变化。Docker 1.12中包括的underlay和load-balancing等新特性,也将该项目的网络支持提升到新的高度。
目前围绕着docker的网络,目前有两种比较主流的声音,docker主导的Container network model(CNM)和社区主导的Container network interface(CNI)。本文就针对两者模型进行分别介绍。
Container Networking Interface
概述
Container Networking Interface(CNI)提供了一种linux的应用容器的插件化网络解决方案。最初是由rkt Networking Proposal发展而来。也就是说,CNI本身并不完全针对docker的容器,而是提供一种普适的容器网络解决方案。因此他的模型只涉及两个概念:
容器(container) : 容器是拥有独立linux网络命名空间的独立单元。比如rkt/docker创建出来的容器。
这里很关键的是容器需要拥有自己的linux网络命名空间。这也是加入网络的必要条件。
网络(network): 网络指代了可以相互联系的一组实体。这些实体拥有各自独立唯一的ip。这些实体可以是容器,是物理机,或者其他网络设备(比如路由器)等。
接口及实现
CNI的接口设计的非常简洁,只有两个接口ADD/DELETE。
以 ADD接口为例
Add container to network
参数主要包括:
Version. CNI版本号
Container ID. 这是一个可选的参数,提供容器的id
Network namespace path. 容器的命名空间的路径,比如 /proc/[pid]/ns/net。
Network configuration. 这是一个json的文档,具体可以参看network-configuration
Extra arguments. 其他参数
Name of the interface inside the container. 容器内的网卡名
返回值:
IPs assigned to the interface. ipv4或者ipv6地址
DNS information. DNS相关信息
调用实现
CNI的调用方式是通过一个可执行文件进行的。这里以calico为例,说明CNI插件的调用方式。
首先,calico进行插件注册
mkdir -p /etc/cni/net.d
$ cat >/etc/cni/net.d/10-calico.conf «EOF { “name”: “calico-k8s-network”, “type”: “calico”, “etcd_authority”: “
k8s的DefaultCNIDir是/opt/cni/bin。因为注册的type是calico,所以k8s会从/opt/cni/bin中搜索一个calico的可执行文件,然后进行执行。
执行的时候传递参数有两种方式,一种是通过环境变量进行传递,比如上文中的Version、Container ID等;另外一种是通过执行calico作为执行的参数传进去,这个主要就是Network configuration的部分,通过json将其打包传入。
同样判断是否执行成功,是通过执行文件的返回值获取的。0为成功,1为版本版本不匹配,2为存在不符合的字段。如果执行成功,返回值将通过stdout返回。
Container Network Model
概述
相较于CNI,CNM是docker公司力推的网络模型。
Sandbox
Sandbox包含了一个容器的网络栈。包括了管理容器的网卡,路由表以及DNS设置。一种Sandbox的实现是通过linux的网络命名空间,一个FreeBSD Jail 或者其他类似的概念。一个Sandbox可以包含多个endpoints。
Endpoint
一个endpoint将Sandbox连接到network上。一个endpoint的实现可以通过veth pair,Open vSwitch internal port 或者其他的方式。一个endpoint只能属于一个network,也只能属于一个sandbox。
Network
一个network是一组可以相互通信的endpoints组成。一个network的实现可以是linux bridge,vlan或者其他方式。一个网络中可以包含很多个endpoints。
接口
CNM的接口相较于CNI模型,较为复杂。其提供了remote plugin的方式,进行插件化开发。remote plugin相较与CNI的命令行,更加友好一些,是通过http请求进行的。remote plugin监听一个指定的端口,docker daemon直接通过这个端口与remote plugin进行交互。
鉴于CNM的接口较多,这里就不一一展开解释了。这里主要介绍下在进行docker的操作中,docker daemon是如何同CNM插件繁盛交互。
调用过程
Create Network
这一系列调用发生在使用docker network create的过程中。
/IpamDriver.RequestPool: 创建subnetpool用于分配IP
/IpamDriver.RequestAddress: 为gateway获取IP
/NetworkDriver.CreateNetwork: 创建neutron network和subnet
Create Container
这一系列调用发生在使用docker run,创建一个contain的过程中。当然,也可以通过docker network connect触发。
/IpamDriver.RequestAddress: 为容器获取IP
/NetworkDriver.CreateEndpoint: 创建neutron port
/NetworkDriver.Join: 为容器和port绑定
/NetworkDriver.ProgramExternalConnectivity:
/NetworkDriver.EndpointOperInfo
Delete Container
这一系列调用发生在使用docker delete,删除一个contain的过程中。当然,也可以通过docker network disconnect触发。
/NetworkDriver.RevokeExternalConnectivity
/NetworkDriver.Leave: 容器和port解绑
/NetworkDriver.DeleteEndpoint
/IpamDriver.ReleaseAddress: 删除port并释放IP
Delete Network
这一系列调用发生在使用docker network delete的过程中。
/NetworkDriver.DeleteNetwork: 删除network
/IpamDriver.ReleaseAddress: 释放gateway的IP
/IpamDriver.ReleasePool: 删除subnetpool
CNI与CNM的转化
CNI和CNM并非是完全不可调和的两个模型。二者可以进行转化。比如calico项目就是直接支持两种接口模型。
从模型中来看,CNI中的container应与CNM的sandbox概念一致,CNI中的network与CNM中的network一致。在CNI中,CNM中的endpoint被隐含在了ADD/DELETE的操作中。CNI接口更加简洁,把更多的工作托管给了容器的管理者和网络的管理者。从这个角度来说,CNI的ADD/DELETE接口其实只是实现了docker network connect和docker network disconnect两个命令。
kubernetes/contrib项目提供了一种从CNI向CNM转化的过程。其中原理很简单,就是直接通过shell脚本执行了docker network connect和docker network disconnect命令,来实现从CNI到CNM的转化。
https://github.com/kubernetes-retired/contrib/tree/master/cni-plugins/to_docker
模型
容器技术已经火遍全球,业界迫切需要一个统一的容器网络模型,此时Docker提出了Container
Network Model(CNM),Kubernetes提出了Container Network Interface(CNI)。
CNM
Libnetwork是CNM的实现。它为Docker daemon和网络驱动程序之间提供了接口。网络控
制器负责将驱动和一个网络进行对接。每个驱动程序负责管理它所拥有的网络,以及为
该网络提供各种服务,例如IPAM负责ip分配。支持多个驱动程序多个网络同时共存。
网络驱动可以按提供方划分为原生驱动(libnetwork内置的或者Docker支持的)或者远
程驱动(第三方插件)。原生驱动包括none,bridge,overlay一粒macvlan。驱动也可
以按照适用范围划分为本地(单主机)的和全局的(多主机)
Network Sandbox
包含了一个容器的网络栈。包括了管理容器的网卡,路由表以及DNS设置。一种
Sandbox实现是通过linux的网络命名空间,一个FreeBSD Jail或者其他类似的概念。
一个Sandbox可以包含多个endpoints。
Endpoint
一个endpoint将Sandbox连接到network上。一个endpint的实现可以通过veth pair,
Open vSwitch internal port或者其他的方式。一个endpint只能属于一个network,
也只能属于一个sandbox。
Network
一个network是一组可以互相通信的endpoints组成。一个network的实现可以是linux
bridge,vlan或者其他方式。一个网络中可以包含多个endpoints。
CNM的接口对比CNI模型较为复杂。提供了remote plugin方式,进行插件化开发。
remote plugin相较与CNI的命令行,更加友好一些,是通过http请求进行的。remote
plugin监听一个指定的端口,docker daemon直接通过这个端口和remote plugin进行
操作。
调用过程
Create Network
这一系列调用发生再调用docker network create过程中。
/IpamDriver.RequestPool: 创建subnetpool用于分配IP
/IpamDriver.RequestAddress: 为gateway获取IP
/NetworkDriver.CreateNetwork: 创建neutron network和subnet
Create Container
这一系列调用发生再适用docker run,创建一个container过程中,也可以通过
docker network connect 触发。
/IpamDriver.RequestAddress: 为容器获取IP
/NetworkDriver.CreateEndpiont: 创建neutron port
/NetworkDriver.Join: 为容器和port绑定
/NetworkDriver.ProgramExternalConnectivity:
/NetworkDriver.EndpointOperInfo
Delete Network
这一系列调用发生再使用 docker network delete 的过程中。
/NetworkDriver.DeleteNetwork: 删除network
/IpamDriver.ReleaseAddress: 释放gateway的IP
/IpamDriver.ReleasePool: 删除subnetpool
CNI
CNI是由CoreOS提出的一种容器网络规范。已采纳该规范的包括Apache Mesos, Cloud
Foundry, Kubernetes,Kurma和rkt。另外Calico 和Weave这些项目也为CNI提供插件
CNI的规范比较小巧。它规定了一个容器runtime和网络插件之间的简单的契约。这个契
约通过JSON的语法定义了CNI插件所需要提供的输入和输出。
一个容器可以被加入到不同插件所驱动的多个网络之中。一个网络有自己对应的插件和
唯一的名称。CNI插件需要提供两个命令:一个用来将网络接口加入到指定网络,另一
个用来将其移除。这两个接口分别再容器被创建和销毁时调用。
CNI工作流程
docker 已经支持安装CNI插件。https://docs.docker.com/ee/ucp/kubernetes/install-cni-plugin/
容器runtime(e.g. kubernetes)首先需要分配一个网络命名空间以及一个容器ID。然后连同一些CNI配置
参数传给网络驱动。接着网络驱动会将容器间接入网络,并将分配的IP地址以JSON格
式返回给容器runtime。
目前,CNI的功能涵盖了IPAM, L2 和 L3。端口映射(L4)则由容器runtime自己负责。
CNI也没有规定端口映射的规则。
CNI支持与第三方IPAM的集成,可以用于任何容器runtime。CNM从设计上就仅仅支持
Docker。由于CNI简单的设计,许多人认为编写CNI插件会比编写CNM插件来得简单。
CNI和CNM的转化
CNI和CNM并非完全不可调和的两个模型。两者概念上有很多相似点,因此可以相互转化,
比如calico项目就支持两种接口模型。
CNI中的container与CNM的sandbox概念一致,CNI中的network与CNM中的network一致。
再CNI中,CNM中的endpoint被隐含在了ADD/DELETE操作中。CNI接口更加简洁,把更多
的工作托管给了容器的管理者和网络的管理者。从这个角度来说,CNI的ADD/DELETE接
口只实现了 docker network connect 和 docker network disconnet 两个命令。
kubernetes/contrib项目提供了一种从CNI向CNM转化的过程。其中原理很简单,就是直
接通过shell脚本执行 docker network connect 和 docker network disconnect
命令,来是心啊从CNI到CNM的转化。
Reference
https://github.com/containernetworking/cni
https://thenewstack.io/container-networking-landscape-cni-coreos-cnm-docker/
http://dockone.io/article/1974
http://www.cnblogs.com/xuxinkun/p/5707687.html
https://blog.firemiles.top/2018/05/27/%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA%E5%AE%B9%E5%99%A8%E7%BD%91%E7%BB%9C%EF%BC%88%E4%B8%80%EF%BC%89/
https://cizixs.com/2017/05/23/container-network-cni/