1. CNI 简介
不管是 docker 还是 kubernetes,不管是在现在还是在未来,在网络方面都不会一个完美的、终极的、普适性的解决方案,不同的用户和企业因为各种原因会使用不同的网络方案。目前存在网络方案 flannel、calico、openvswitch、weave、ipvlan 还有我司的kube-ovn等,而且以后一定会有其他的网络方案,这些方案接口和使用方法都不相同,而不同的容器平台都需要网络功能,它们之间的适配如果没有统一的标准,会有很大的工作量和重复劳动。
我们不可能为了一个新的网络方案,重新修改kubelet的代码,所以CNI 就是这样一个标准,它旨在为容器平台提供网络的标准化。我们会从docker 出发,利用已经开源的cni 插件,体验cni 如何管理docker 的网络配置。
2. 先创建一个普通的netns,给它做些网络配置(一会儿用来测试下网络的联通与否)
-
先下载下cni 的插件二进制文件,这里使用0.4.0版本。
[root@VM-0-15-centos cni]#curl -OJL https://github.com/containernetworking/cni/releases/download/v0.4.0/cni-amd64-v0.4.0.tgz [root@VM-0-15-centos cni]#tar -xzvf cni-amd64-v0.4.0.tgz [root@VM-0-15-centos cni]# ll 总用量 70756 -rwxr-xr-x 1 root root 5924584 1月 14 2017 bridge -rw-r--r-- 1 root root 16066400 3月 2 21:59 cni-amd64-v0.4.0.tgz -rwxr-xr-x 1 root root 3614840 1月 14 2017 cnitool -rwxr-xr-x 1 root root 10354296 1月 14 2017 dhcp -rwxr-xr-x 1 root root 3684624 1月 14 2017 flannel -rwxr-xr-x 1 root root 4008016 1月 14 2017 host-local -rwxr-xr-x 1 root root 5308904 1月 14 2017 ipvlan -rwxr-xr-x 1 root root 5033704 1月 14 2017 loopback -rwxr-xr-x 1 root root 5334832 1月 14 2017 macvlan -rwxr-xr-x 1 root root 3400872 1月 14 2017 noop -rwxr-xr-x 1 root root 5910424 1月 14 2017 ptp -rwxr-xr-x 1 root root 3791288 1月 14 2017 tuning
-
创建一个bridge 设备
ip l a br0 type bridge ip l s br0 up
-
创建一个普通的netns
[root@VM-0-15-centos lmxia]# ip netns list lmxia
-
新增CNI的配置文件:
cat > mybridge.conf <<"EOF" { "cniVersion": "0.2.0", "name": "mybridge", "type": "bridge", "bridge": "cni_bridge0", "isGateway": true, "ipMasq": true, "hairpinMode":true, "ipam": { "type": "host-local", "subnet": "10.15.20.0/24", "routes": [ { "dst": "0.0.0.0/0" }, { "dst": "1.1.1.1/32", "gw":"10.15.20.1"} ] } } EOF
-
把它放在cni 的默认目录下
[root@VM-0-15-centos lmxia]# mkdir -p /etc/cni/net.d [root@VM-0-15-centos lmxia]# mv mybridge.conf /etc/cni/net.d
-
利用bridge 插件给这个netns 设置虚拟设备队和它的网络参数,可以看到bridge 插件返回了我们给这个lmxia 这个netns 下的eth12 这个网卡设置的网络参数。
[root@VM-0-15-centos netns]# cd ~/lmxia [root@VM-0-15-centos lmxia]# ls bridge cni cni-amd64-v0.4.0.tgz cnitool dhcp flannel go1.15.2.linux-amd64.tar.gz host-local ipvlan loopback macvlan noop ptp tuning [root@VM-0-15-centos lmxia]# CNI_COMMAND=ADD CNI_CONTAINERID=1234567890 CNI_NETNS=/var/run/netns/lmxia CNI_IFNAME=eth12 CNI_PATH=`pwd` ./bridge < /etc/cni/net.d/mybridge.conf 2020/09/28 10:44:33 Error retriving last reserved ip: Failed to retrieve last reserved ip: open /var/lib/cni/networks/mybridge/last_reserved_ip: no such file or directory { "ip4": { "ip": "10.15.20.2/24", "gateway": "10.15.20.1", "routes": [ { "dst": "0.0.0.0/0" }, { "dst": "1.1.1.1/32", "gw": "10.15.20.1" } ] }, "dns": {}
- 接着看下是不是在主机的network namespace 下是不是有了veth pair 的另一端?我们发现,br0 这个网桥的确已经master了这个pair 的另一端veth82e59ec2。且网卡配置,和路由设置都很OK。
[root@VM-0-15-centos ~]# brctl show br0 bridge name bridge id STP enabled interfaces br0 8000.0a580a0f1401 no veth82e59ec2 [root@VM-0-15-centos ~]# ip netns exec lmxia ip a 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 3: eth12@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 0a:58:0a:0f:14:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.15.20.2/24 scope global eth12 valid_lft forever preferred_lft forever inet6 fe80::241f:52ff:fe0d:f5b8/64 scope link valid_lft forever preferred_lft forever [root@VM-0-15-centos ~]# ip netns exec lmxia route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.15.20.1 0.0.0.0 UG 0 0 0 eth12 1.1.1.1 10.15.20.1 255.255.255.255 UGH 0 0 0 eth12 10.15.20.0 0.0.0.0 255.255.255.0 U 0 0 0 eth12
3. 创建一个pause 容器,其网络为none。
在上一步,我们看到bridge 这个插件,需要传入容器的id 和 netns。
- 我先创建一个pause 容器,给它设置下network namespace。
[root@VM-0-15-centos ~]# contid=$(docker run -d --net=none busybox:latest /bin/sleep 10000000) [root@VM-0-15-centos ~]# echo $contid f21731d52dbe402a39c6b20d7a84fa4490327f51efc3d70e0b088b928a7aff95 [root@VM-0-15-centos ~]# pid=$(docker inspect -f '{{ .State.Pid }}' $contid) [root@VM-0-15-centos ~]# echo $pid 19394 [root@VM-0-15-centos ~]# netnspath=/proc/$pid/ns/net
- 一个小插曲,这时虽然我们已经创建出了新的network namespace,却不能通过ip netns list 命令看到。这是因为默认docker把创建的网络命名空间链接文件隐藏起来了,导致ip netns命令无法读取,给分析网络原理和排查问题带来了麻烦。我们做一个简单的link 就可以。
[root@VM-0-15-centos lmxia]# ln -s /proc/$pid/ns/net /var/run/netns/pause1 [root@VM-0-15-centos lmxia]# ip netns list pause1 (id: 3) lmxia (id: 2) ns2 (id: 1) ns1 (id: 0) [root@VM-0-15-centos lmxia]# ip netns exec pause1 ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 3: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 0a:58:0a:0f:14:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.15.20.3/24 scope global eth0 valid_lft forever preferred_lft forever
4. 创建一个容器,加入到pause 容器的网络命名空间中
- 这一步就是临门一脚了,我们可以看到,由于当前容器和pause 容器同属一个network namespace 因此,这个容器的网络配置和上边的pause容器完全相同。
[root@VM-0-15-centos lmxia]# docker run --net=container:$contid --rm busybox:latest ifconfig eth0 Link encap:Ethernet HWaddr 0A:58:0A:0F:14:03 inet addr:10.15.20.3 Bcast:0.0.0.0 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:656 (656.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
- ping 下看看,我们在当前这个pause 容器里ping 下 第一步配置的地址,我们可以看到,是没问题的。如果你ping 不通,可以上下是不是iptables 的forward 链给drop掉了。
[root@VM-0-15-centos lmxia]# docker exec -it f21731d52dbe ping 10.15.20.2 PING 10.15.20.2 (10.15.20.2): 56 data bytes 64 bytes from 10.15.20.2: seq=0 ttl=64 time=0.133 ms ^C --- 10.15.20.2 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 0.133/0.133/0.133 ms
5. 总结
我们用docker 的方式,结合CNI插件,演示了kubernetes 如何利用CNI插件去管理容器网络的过程。那么在容器的创建过程中,是什么承担了,我们敲命令的过程呢?是CRI。
kubernetes 使用了 CNI 网络插件之后,工作过程是这样的:
- kubelet 的CRI 先创建 pause 容器生成对应的 network namespace
调用网络 driver(因为配置的是 CNI,所以会调用 CNI 相关代码) - CNI driver 根据配置调用具体的 cni 插件
- cni 插件给 pause 容器配置正确的网络
- pod 中其他的容器都是用 pause 的网络
和我们手工的操作过程如出一辙。