kubernetes CNI
目录:
CNI
Pod在启动的时候,会先创建Infra(Pause)容器,以此来hold住network namespace,之后我们需要的container就可以加入到对应的network namespace了
实现了
- 可以通过localhost通信
- 网络设备在容器内都一致
- IP地址都是networknamespace对应地址
- pod的生命周期与Infra容器一致
所以CNI的重点就是如何配置Pod的network namespace,而不是每个container如何配置网络
network namespace对应的就是一个网络栈,包括
- 网卡
- 回环设备
- 路由表
- iptables规则
cni插件的可执行文件在/opt/cni/bin
目录,通过kubernetes-cni安装的
cni插件分为三类
- main插件,用来创建具体的网络设备的二进制文件,例如briage,ipvlan,loopback、macvlan、ptp(veth pair)和vlan
- IPAM插件,用来分配IP地址的二进制文件,例如dhcp、host-local
- CNI社区维护插件,例如flannel、tuning(用来调整sysctl网络配置)、portmap(iptable端口映射)等
容器的网络实现,例如flannel主要有两部分
- 创建和配置网络设备、配置宿主机路由、配置ARP和FBP表信息
- 配置Infra容器内的网络栈
flannel是如果是通过daemonset,在启动的时候会将相关文件cp到cni插件目录
容器网络处理的部分在CRI中完成,docker的CRI为dockershim
在创建和启动Infra容器之后,会执行一个SetUpPod方法,会执行两个步骤
- 为CNI准备参数
- 加载CNI的配置文件,目前只加载
/etc/cni/net.d
目录的第一个配置,配置内plugins字段可以定义多个插件协作,示例插件配置
准备参数就是为dockershim设置环境变量,包括
- CNI_COMMAND:对容器添加还是删除网络
- CNI_IFNAME:容器内网卡名称
- CNI_NETNS:network namespace的文件路径
- CNI_CONTAINERID:容器ID
- CNI_ARGS:网卡插件的自定义数据
$ cat /etc/cni/net.d/10-flannel.conflist
{
"name": "cbr0",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
完整定义参考CNI Network Configuration
依次完成配置容器和配置端口映射
delegate的意思是CNI不会自己完成,而是调用指定的CNI插件完成,对于flannel就是bridge。flannel插件的主要作用是补全Network Configuration,例如将delegate的Type设置为bridge,IPAM设置为host-local,补全之后delegate就是
{
"hairpinMode":true,
"ipMasq":false,
"ipam":{
"routes":[
{
"dst":"10.244.0.0/16"
}
],
"subnet":"10.244.1.0/24",
"type":"host-local"
},
"isDefaultGateway":true,
"isGateway":true,
"mtu":1410,
"name":"cbr0",
"type":"bridge"
}
ipam的数据来自flannel的配置文件/run/flannel/subnet.env
然后由flannel把补全的delegate以stdin的方式交由briage来完成,并且字段也会保存到/var/lib/cni/flannel便于删除使用
bridge会检查CNI网桥是否存在,没有则创建
$ ip link add cni0 type bridge
$ ip link set cni0 up
然后bridge插件会通过Infra容器进入对应的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
再bridge将vethb4963f3连接到CNI网桥,并且配置为HairpinMode(发夹模式针对有nat端口映射的时候,访问nat地址)
# 在宿主机上
$ ip link set vethb4963f3 master cni0
再调用ipam插件分配地址,并设置到网卡上
# 在容器里
$ ip addr add 10.244.0.2/24 dev eth0
$ ip route add default via 10.244.0.1 dev eth0
最后bridge会为CNI网桥添加路由
# 在宿主机上
$ ip addr add 10.244.0.1/24 dev cni0
然后这些ip等信息会返回给dockershim,然后返回给kubelet添加到pod的status字段
最后Kubernetes的CNI网络实现的网络模型
- 所有容器都可以直接使用IP地址与其他容器通信,而无需使用NAT
- 所有宿主机都可以直接使用IP地址与所有容器通信,而无需使用NAT,反之亦然
- 容器自己看到的自己的IP地址,和别人(宿主机或者容器)看到的地址是完全一样的