暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

容器网络系列—CNI初探

深夜IT研究猿 2021-11-29
1743


 随着容器技术在企业生产系统中的逐步落地,用户对容器云的网络特性需求也在不断变化。已不再仅仅局限于跨主机容器间的网络互通,现在对固定容器IP地址、与SDN适配、ACL控制策略、多子网隔离等技术也有更高的要求。

Docker原生的网络中,Docker0网桥仅仅是相当于一个二层的交换机设备,通过简单的学习和转发,使用Iptables、Nat、路由这些技术在宿主间通信,这种模式下已经不足以应付这些复杂的需求,同样容器不断重启变化的Ip地址,使得网络策略也要经常变更。
目前成熟的容器网络方案,主要是:
Docker提出的Container Network Model(CNM);
CoreOS提出的Container NetWork Interface(CNI);

1.CNM模型

CNM模型是由Docker公司提出的容器网络模型,现在已经被Cisco ContivKuryrOpen Virtual NetworkingOVN)、ProjectCalicoVMwareWeavePlumgrid等项目所采纳。另外,WeaveProjectCalicoKuryrPlumgrid等项目也为CNM提供了网络插件的具体实现。


CNM模型主要通过Network Sandbox、Endpoint和Network这3个组件进行实现。

 Network Sandbox:容器内部的网络栈,包括网络接口、路由表、DNS等配置的管理。Sandbox可用Linux网络命名空间、FreeBSD Jail等机制进行实现。一个Sandbox可以包含多个Endpoint。

 Endpoint:用于将容器内的Sandbox与外部网络相连的网络接口。可以使用veth对、OpenvSwitch的内部Port等技术进行实现。一个Endpoint仅能够加入一个Network。

 Network:可以直接互连的Endpoint的集合。可以通过Linux网桥、VLAN等技术进行实现。一个Network包含多个Endpoint。


2.CNI模型


CNI,全称是 Container Network Interface,即容器网络的 API 接口。是Coreos提出的一种容器网络规范,并被ApacheMeos、CloudFoundry、Kubernetes、Kurma和Rkt等项目所采纳。另外,由Contiv Networking、Project Calico和Weave、SR-IOV、Cilium、Infoblox、Multus、Romana、Plumgrid和Midokura等项目也为CNI提供网络插件的具体实现。

CNI定义的是容器运行环境与网络插件之间的简单接口规范,通过一个JSON Schema定义CNI插件提供的输入和输出参数。



CNI连接了两个组件:容器管理系统和网络插件。它们之间通过 JSON 格式的文件进行通信,实现容器的网络功能。具体的事情都是插件来实现的,包括:创建容器网络空间(Network Namespace)、把网络接口(Interface)放到对应的网络空间、给网络接口分配 IP 等等。
在Kubernetes中,Kubelet通过这个标准的 API 来调用不同的网络插件以实现不同的网络配置方式,通过具体调用插件的ADD/DEL这样的操作方法完成不同的网络配置。

3.模型如何选择 

了解了CNI模型和CNM模型,我们再来对比观察:

CNI的Container和CNM的Sandbox概念一致(容器内部网络栈管理),Net-work与Network一致(Endpoint的集合),Endpoint被隐藏在了Cni的ADD/DEL操作(Cni主要实现的接口方法)中,从效果上CNI更加简洁,工作托管给了容器管理者和网络管理者。

那么该如何选择呢?在选择的阶段,经历了很长的一段故事……

但是由于CNM插件对Docker的依赖性更强,CNI对开发者的约束更少,同时CNM插件在Docker网络模型中的一些设计还需要做一些额外部署和实现,最后在两个模型的选择过程中,Kubernetes最终选择了CNI模型。

Before going further, it's important to remember that Kubernetes is a system that supports multiple container runtimes, of which Docker is just one.
Kubernetes支持多容器的运行环境,Docker只是其中一种,其中不使用 CNM 最关键的一点,是 k8s 考虑到CNM 在一定程度上和 Container runtime 联系相对比较紧密,不好解耦,有了 k8s 这种巨无霸的选择之后,后来的很多项目都在 CNM 和 CNI 之间选择了 CNI。

4.CNI插件

CNI插件:实现了CNI接口的插件就是CNI 插件。
常见的 CNI插件包括 Calico、Flannel、Terway、Weave Net 、Cilium以及Contiv。
先来看看CNI插件的组成部分:



其中一个配置文件NetworkConfig存储在/etc/cni/net.d目录下,是CNI的配置文件,在不同CNI插件中表现为NetworkConfig结构体的差异。

 而可执行的CNI插件的二进制文件存放在 /opt/cni/bin目录,包括原生CNI自带的插件,以及各自特定CNI插件的二进制文件。
 而在创建容器网时,就会读取环境变量,包括需要执行的操作,需要操作的网络namespace信息,容器网卡的配置信息,最后在/var/lib/cni/目录下,存储运行容器的网络配置信息,之后配置的信息会作为参数变量传递到对应的CNI执行脚本。
最后对于两个接口操作,在CNI模型中,设计的初衷是创建框架,配置和销毁容器时动态配置适当的网络配置和资源。所以CNI模型主要需要实现的接口有两个:

分别是在创建容器时调用的配置网络接口:

AddNetwork(net*NetworkConfig ,rt* RuntimeConf)(types.Result,error)
和删除容器时调用的清理网络接口:

DelNetwork(net*NetworkConfig,rt * RuntimeConf)

观察下入参,都是两个:网络配置和runtime配置。


看完CNI插件的组成后思考,那么CNI插件和CNI的接口是怎么集成的呢?

原生CNI插件自带docker-run.sh和exec-plugins.sh,通过调用

./docker-run.sh ifconfig 使用ifconfig作为参数传递给docker-run.sh

例如./docker-run.sh –rm image /sbin/ifconfig  

这个sh脚本会实际去调用exec-plugins.sh执行具体的cni插件

而./Exec-plugins.sh 会通过 jq来获取 /etc/cni/net.d目录下的Netconf 配置,还有runtimeConf中传递的具体执行的操作、网络命名空间 (add/del ,conidnetns)、插件使用的type等参数。 

例如:

./Exec-plugins.sh add $contid $netnspath创建容器网络

./Exec-plugins.sh del  6e3b681a422f5174/var/run/netns/6

来完成容器网络的创建删除工作。

所有的CNI插件都是通过这种方式完成与CNI接口的集成,这一点也体现了CNI插件最大的价值  不管是什么第三方插件 使用一致的API。


官方还有一个sh脚本
Priv-net-run.sh 是在私有网络命名空间下执行的脚本依然会调用exec-plugins.sh,代码与docker-run.sh基本一致,但是priv-net-run.sh是又创建了一个单独的网络命名空间,ip netns add $contid,这个id是随机的,这个id是分配给网络命名空间的,仅在空间内有效,内核将在netlink的某些消息中使用这些id。
这个自建的netns 默认看不到 /var/run/netns下做软链接,你使用ip netns list才可以看到对应netns,方便管理。


5.CNI模型如何选择

主要参考以下方面:


6.Kubernetes中如何使用CNI插件
Kubernetes中如果使用CNI插,需要在kubelet的启动参数中配置--network-plugin,--cni-conf-dir,--cni-bin-dir
其中配置network-plugin=cni
--cni-conf-dir是cni配置文件的目录,默认/etc/cni/net.d
--cni-bin-dir是cni插件的可执行文件目录,默认是/opt/cni/bin
那么Kubernetes怎么使用CNI插件配置容器网络呢。
基本的使用方法为:
首先在每个Node上配置 CNI 配置文件(/etc/cni/net.d/xxnet.conf),其中 xxnet.conf 是某一个网络配置文件的名称;
安装 CNI 配置文件中所对应的二进制插件;(Flannel、Calico ,/opt/cni目录下)
在这个节点上创建 Pod 之后,Kubelet 就会根据 CNI 配置文件执行前两步所安装的 CNI 插件;
在集群里面创建一个 Pod 的时候,首先会通过 Apiserver 将 Pod 的配置写入。Apiserver 的一些管控组件(比如 Scheduler)会调度到某个具体的节点上去。Kubelet 监听到这个 Pod 的创建之后,会在本地进行一些创建的操作。当执行到创建网络这一步骤时,它首先会读取刚才我们所说的配置目录中的配置文件,配置文件里面会声明所使用的是哪一个插件,然后去执行具体的 CNI 插件的二进制文件,再由 CNI 插件进入 Pod 的网络空间去配置 Pod 的网络。配置完成之后,Kuberlet 也就完成了整个 Pod 的创建过程,这个 Pod 就在线了。
7.自定义CNI插件

那么假如我们要自己实现CNI插件,我们都需要做些什么呢?(k8s Pod为例)
  1. 给Pod插上网线,创建veth虚拟网卡对,将一端放入pod内。

  2. 给Pod分配ip地址,给Pod分配集群中唯一的ip,一般CNI插件会按照分配给Node的网段里去做划分。

  3. 配置Pod的ip和路由,给Pod的虚拟网卡配置分配的IP,配置对应的路由,在宿主机上配置到虚拟网卡的路由。

  4. 给Pod间连上网络,使用CNI daemon进程,学习到其余pod的Ip和路由信息,通过监听k8s的API server,并配置新节点创建时候通知到每个daemon进程。拿到Pod、Node信息后再使用Overlay、VPC路由表、BGP路由等方式打通路由。最后将pod的Ip地址和打通的通道关联起来,具体的实现可以通过Linux路由、fdb转发表、ovs流表来实现。


8.NetworkPolicy项目
在容器网络安全方面,不同的CNI插件各自扩展了Kubernetes的网络策略配置,实现了更加细粒度的容器网络控制,也使得API碎片化和复杂化,所以Kubernetes引入了Network policy项目,目的是完善容器网络安全。 
定义容器网络接口(CNI)提供程序的网络策略一致性。
项目地址:
https://kubernetes.io/blog/2021/04/20/defining-networkpolicy-conformance-cni-providers/


9.CNITOOL
CNITOOL是一个执行 CNI 配置的简单程序。它将在已创建的网络命名空间中添加或删除接口。
是模拟cni插件实现的一个小项目。便于配置,可以用来学习理解cni插件的实现。

地址: 

https://github.com/containernetworking/cni/tree/master/cnitool


10.总结
第一部分初探我们先对CNI模型和CNI插件有一个简单的了解,接下来小猿们会结合一些主流的CNI插件实例学习来帮助大家更深入理解CNI网络模型,敬请期待。


参考文献

1、从零开始入门 K8s | 理解 CNI 和 CNI 插件,阿里巴巴云原生小助手,

https://developer.aliyun.com/article/748866

2、Why Kubernetes doesn’t use libnetwork,Tim Hockin,https://kubernetes.io/blog/2016/01/why-kubernetes-doesnt-use-libnetwork/

3、容器网络模型:CNI vs CNM,廖长江,https://zhuanlan.zhihu.com/p/59655534

4、容器网络——从CNI到Calico,郝一昕http://dockone.io/article/2578

5、Kubernetes网络权威指南:基础、原理与实践,杜军,电子工业出版社

6、Kubernetes权威指南第四版,龚正 ,吴治辉 , 王伟等,电子工业出版社

7、Defining Network Policy Conformance for Container Network Interface (CNI) providers,Matt Fenwick , Jay Vyas , Ricardo Katz, 

https://kubernetes.io/blog/2021/04/20/defining-networkpolicy-conformance-cni-providers/

8、https://github.com/containernetworking/cni(2020)

9、https://github.com/containernetworking/cni/tree/master/cnitool(2020)



文章转载自深夜IT研究猿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论