上一篇说到在k8s里使用underlay网络有一个弊端,使用了underlay网络的pod无法访问serviceIP,这一点可能通过修改修改业务应用的chart来解决,主要解决方法是:
kube-dns
服务通过underlay网络直接可达,可以将coredns
的pod设置为使用hostNetwork,然后修改kubelet里配置的--cluster-dns
参数。上述两个方案虽然可行,但需要进行一系列调整,成本确实比较高,如果业务应用的chart比较复杂,改造起来就更费劲了。
在网上偶然看到一篇文章,在这篇文章里讲到可以向已经使用了macvlan等underlay网络的pod中再插入一个ptp的网络接口,设置必要的路由规则后,即可实现在这种pod中也可以正常访问serviceIP。当然依旧是使用cni插件的方式实现的。Bingo, 这正是我需要的。
于是我参考unnumbered-ptp.go, 我实现了一个veth-to-host的cni插件,该插件的原理如下:
然后原来的macvlan配合使用该cni插件,即可在使用了underlay网络的pod也可以正常访问serviceIP,cni的配置如下:
/etc/cni/net.d/10-cnichain.conflist
{
"name": "cni0",
"cniVersion": "0.3.1",
"plugins": [
{
"name": "mymacvlan",
"type": "macvlan",
"master": "enp5s0f0",
"ipam": {
"type": "host-local",
"subnet": "10.10.20.0/24",
"rangeStart": "10.10.20.50",
"rangeEnd": "10.10.20.60",
"gateway": "10.10.20.254",
"routes": [{
"dst": "0.0.0.0/0"
}]
}
},
{
"name": "myptp",
"type": "veth-to-host",
"hostInterface": "enp5s0f0",
"containerInterface": "veth0",
"ipMasq": true
}
]
}
这里有几点要注意一下:
CNI_BIN_PATH
目录中macvlan
等underlay网络的cni配置要放在前面应用了上述cni配置后,一般来说可以正常工作了。如果我们对实现原理比较好奇的话,可以简单创建一个pod,执行以下命令观察创建的网络接口和各种路由规则:
# 检查pod容器里的网络接口
[root@node-1 ~]# kubectl -n demo exec -ti redis-predixy-67d989bdd9-p7fbf sh
/ # ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
link/ether 2e:31:a0:bc:39:20 brd ff:ff:ff:ff:ff:ff
4: veth0@if139: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 46:4a:fd:d5:3a:bf brd ff:ff:ff:ff:ff:ff
# 检查pod容器里的路由规则
/ # ip route show
default via 10.10.20.152 dev veth0
10.10.20.0/24 dev eth0 scope link src 10.10.20.53
10.10.20.152 dev veth0 scope link
# 退出pod容器
/ # exit
# 检查宿主机里的网络接口
[root@node-1 ~]# ip link show
...
139: vetha0e043dd@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 12:78:cc:ea:9b:26 brd ff:ff:ff:ff:ff:ff link-netnsid 3
...
# 检查宿主机主表里的路由规则
[root@node-1 ~]# ip route show
...
10.10.20.53 dev vetha0e043dd scope link
...
# 检查宿主机的自定义路由表
[root@node-1 ~]# ip rule show
...
1024: from all iif vetha0e043dd lookup 596
...
# 检查宿主机上自定义路由表中的路由规则
[root@node-1 ~]# ip route show table 596
default via 10.10.20.53 dev vetha0e043dd
从上述输出可以看出确实与该CNI插件的原理是一致的。
上面实际的开发了一个崭新的cni插件,下面总结一下开发cni插件的过程:
cmdAdd
、cmdDel
等方法,这些方法的主要逻辑就是按设想创建网络接口、设置路由规则、设置NAT规则等。 # 提前将二进制文件放入CNI_PATH目录下,配置好/etc/cni/net.d目录下的cni配置文件
# 下面会创建一个网络命名空间,并调用cni插件尝试为该网络命名空间配置好网络
ip netns add test
CNI_PATH=/usr/local/bin NETCONFPATH=/etc/cni/net.d /usr/local/bin/cnitool add cni0 /var/run/netns/test
# 进入网络命名空间,检查网络接口、路由表信息
ip netns exec test sh
....
# 在宿主机检查网络接口、路由表信息
ip link show
ip route show
ip rule show
iptables -t nat -L -n
iptables -L -n
检查结果如果没有什么大的问题,跟设想一致的话,这个cni插件就差不多了。
总的来说,开发一个cni网络插件过程还是挺清晰的,也比较简单,本周也算是又掌握一门技能。