网络插件的目的?
通过某种方法,把不同主机上的网络进行连通,从而达到跨主机网络通信的目的。
什么是CNI网桥?
Kubernetes通过一个CNI接口,维护了单独的网桥代替docker0,该网桥就是CNI网桥,默认是cni0。
CNI网络插件的思想是?
Kubernetes在启动Infra容器之后,可以直接调用CNI网络插件,为这个Infra容器的Network Namespace,配置符合预期的网络栈。
kubernetes-cni包的作用是?
在Kubernetes中,有一个步骤是安装kubenetes-cni包,目的是在宿主机上安装CNI插件所需的基础可执行文件,安装完成后可以在/opt/cni/bin目录下看到,如下图:
CNI可执行文件的分类
主要分为三类:
实现一个容器网络方案需要做哪些工作
CNI插件的安装
Flannel的CNI插件默认已经在/opt/cni/bin中,所以无需单独安装,但是如果是其他的网络项目,则需要把其对应的CNI插件可执行文件放在/opt/cni/bi目录下。
网络方案本身的安装
当在宿主机上安装网络方案本身比如flanneld时,flanneld启动时会在每台宿主机上生成它对应的CNI配置文件(ConfigMap),从而告诉Kubernetes这个集群使用Flannel作为容器网络解决方案,CNI配置文件内容如下:
dockershim是什么?
在Kubernetes处理容器网络的逻辑不在kubelet主干代码里执行,会在具体的CRI实现里完成,对于docker来说它的CRI是dockershim。
dockershim会加载CNI配置文件,上面的配置文件中Flannel项目指定了flannel和portmap两个插件,dockershim会对CNI配置文件加载,将列表中的第一个插件也就是flannel设置默认插件。
Kubelet组件在创建Pod时,dockershim会调用Docker API创建并启动Infra容器,紧接着执行一个SetUpPod方法,该方法会为CNI插件准备参数,然后调用CNI插件为Infra容器配置网络。
CNI插件的工作原理
CNI插件在被调用时会接收由dockershim设置的一组CNI环境变量,其中有一个环境变量参数叫做CNI_COMMAND,取值有ADD和DEL两种。
ADD和DEL就是CNI插件唯一要实现的两个方法,ADD的含义是把容器添加到CNI网络中,DEL则是把容器从CNI网络中移除。
CNI ADD操作的参数主要包括:容器里网卡的名字eth0(CNIIFNAME),Pod的Network NameSpace文件路径(CNINETNS),容器的ID(CNICONTAINERID), CNI_AGRS(CRI可以key-value的形式传递自定义信息给网络插件)。
CNI插件在被调用时还需要接收dockershim从CNI配置文件里加载到的默认插件的配置信息。
ADD操作过程
知道了做什么操作,且有了配置信息以后,就可以设置一些网络信息了,其中配置文件中的delegate文件表示Flannel插件不会自己工作,而是会调用delegate指定的CNI内置插件完成,对于Flannel则是指CNI bridge插件。
dockershimp对于Flannel CNI插件的调用只是个过场,但是Flannel CNI插件会对其收到的配置信息进行补充,比如为delegate设置type为bridge, ipam字段设置host-local等,如下:
ipam字段的信息来Flannel在宿主机上生成的配置文件(/run/flannel/subnet.env)
下面Flannel CNI插件会调用CNI bridge插件,在调用插件的时候会传递两个参数,第一个参数还是ADD,第二个参数则是delegate字段的内容,同时Flannel CNI插件会把delegate字段保存在/var/lib/cni/flannel目录下,方便后续DEL进行使用。
CNI Bridge插件对的ADD操作
1. CNI Bridge首先会在宿主机上检查CNI网桥是否存在,如果没有则进行创建:
# 在宿主机上
$ ip link add cni0 type bridge
$ ip link set cni0 up
2. 通过Infra容器的Network Namespace文件进入到Network Namespace中创建一对Veth Pair设备:
#在容器里
# 创建一对Veth Pair设备。其中一个叫作eth0,另一个叫作vethb4963f3
$ ip link add eth0 type veth peer name vethb4963f3
# 启动eth0设备
$ ip link set eth0 up
# 将Veth Pair设备的另一端(也就是vethb4963f3设备)放到宿主机(也就是Host Namespace)里
ip link set vethb4963f3 netns HOST_NS
# 通过Host Namespace,启动宿主机上的vethb4963f3设备
ip netns exec HOST_NS ip link set vethb4963f3 up
3. 把宿主机上的vethb4963f3设备插到CNI 网桥上:
# 在宿主机上
$ ip link set vethb4963f3 master cni0
4. 为vethb4963f3设置HairpinMode模式,默认情况下网桥不允许一个数据包从一个端口进来以后再从相同的端口出去,设置HairpinMode则可以取消这个限制,这样才可以保证集群中的Pod能够通过自己的Service访问自己
5. CNI bridge插件会调用CNI ipam插件,从ipam.subnet规定的网段中为容器分配一个可用的ip地址看,同时为容器设置默认路由,如下:
# 在容器里
$ ip addr add 10.244.0.2/24 dev eth0
$ ip route add default via 10.244.0.1 dev eth0
6. CNI bridge为CNI网桥添加IP地址,如下:
# 在宿主机上
$ ip addr add 10.244.0.1/24 dev cni0
7. CNI 插件会把容器的IP地址返回给dockershim,然后kubelet会将其添加到Pod的status字段。