CNI是什么
Container Nerwork Interface
,由一组用于配置 Linux 容器的网络接口的规范和库组成,同时还包含了一些插件。
CNI插件可以分为下面三类(可见官方库https://github.com/containernetworking/plugins):
Main插件:用来创建具体的网络设备的二进制文件,包括:
- bridge:在宿主机上创建网桥然后通过veth pair的方式连接到容器
- host-device:移动宿主上已经存在的设备到容器中
- ipvlan:和macvlan相似,也是通过一个主机接口虚拟出多个虚拟网络接口,不同的是ipvlan虚拟出来的是共享MAC地址,IP地址不同
- loopback:lo设备(将回环接口设置成up)
- macvlan:虚拟出多个macvtap,每个macvtap都有不同的mac地址
- ptp:Veth Pair设备
- vlan:分配vlan设备
- 等
IPAM插件:负责分配IP地址,包括:
- host-local:使用提前分配好的IP地址段来分配,这是当前最为常用的IPAM插件
- dhcp:宿主机上运行的守护进程,代表容器发出DHCP请求
- static:用于为容器分配静态的IP地址,主要是调试使用
Meta插件:由CNI社区维护的内部插件,包括:
- bandwidth:使用 Token Bucket Filter (TBF)来进行限流的二进制文件
- firewall:通过iptables或者firewalled添加规则控制容器的进出流量
- portmap:通过iptables配置端口映射的二进制文件,将端口从主机的地址空间映射到容器
- tuning:通过sysctl调整网络设备参数的二进制文件
- 等
CNI的思想就是在 kubelet 启动infra容器后,就可以直接调用CNI插件为这个infra容器的 Network Namespace 配置符合预期的网络栈。
Kubernetes中如何使用CNI
K8S通过CNI配置文件来决定使用何种CNI插件,基本使用方法:
- 读取节点CNI配置文件:/etc/cni/net.d/xxnet.conf
- 安装CNI配置文件中对应的二进制,目录:/opt/cni/bin
- 在这个节点上创建Pod后,根据CNI配置文件执行CNI二进制来配置容器网络
例如 KinD 中的CNI配置文件:
$ cat /etc/cni/net.d/10-kindnet.conflist
{
"cniVersion": "0.3.1",
"name": "kindnet",
"plugins": [
{
"type": "ptp",
"ipMasq": false,
"ipam": {
"type": "host-local",
"dataDir": "/run/cni-ipam-state",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"ranges": [
[ { "subnet": "10.244.3.0/24" } ]
]
},
"mtu": 1500
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
这个网络的名字叫 kindnet
,根据前文CNI插件分类可知 type: ptp
即表示是该CNI插件是创建 Veth Pair
类型的网络设备。ipam
下的 type: host-local
表示是从提前分配好给本地的IP地址段来分配,具体可分配的IP段在 ranges
中指定,也就是 subnet: 10.244.3.0/24
,IP分配数据保存在 dataDir
指定的 /run/cni-ipam-state
目录中。
$ tree /run/cni-ipam-state/
/run/cni-ipam-state/
└── kindnet
├── 10.244.3.2
├── 10.244.3.3
├── 10.244.3.4
├── last_reserved_ip.0
└── lock
如何选择CNI
通常来说,CNI插件可以分成3种:Overlay
、路由
和 Underlay
环境限制
- 虚拟机环境
- 物理机环境
- 公有云环境
功能需求
- 安全需求
- 是否需要集群外的资源与集群内的资源互联互通
- K8S的服务发现与负载均衡的能力
性能需求
- Pod的创建速度
- Pod的网络性能
如何开发CNI插件
CNI插件的实现通常包含两个部分:
- 一个二进制的CNI插件去配置Pod网卡和IP地址,相当于给Pod插上网线
- 一个Daemon进程去管理Pod之间的网络打通,相当于给Pod连上网络
给Pod插上网线
- 分配IP地址,即IPAM
- 创建节点网桥,若已存在则跳过
- 创建网卡,一般是veth-pair,一端放入Pod作为eth0,另一端连接到网桥
- 配置Pod的IP和路由
- 将分配的IP地址配置到网卡
- 在网卡上配置集群网段的路由
- 在宿主机上配置到Pod的IP段的路由
给Pod连上网络
一般是由CNI Daemon进程去做网络打通的事情,通常是这样的步骤:
- Daemon进程监听kube-apiserver,获取Pod的IP地址与Node信息
- 配置网络进程打通
- 创建到集群所有节点的通道,如:Overlay隧道、阿里云的VPC路由表、BGP路由等
- 将Pod的IP地址与通道进行关联,具体实现如:Linux路由、fdb转发表、ovs流表等