诸如 containerd 这样的容器运行时在 Linux 上通过网络命名空间 (Network Namespace) 实现容器之间的网络隔离.
为了让容器和容器之间, 容器和宿主机之间可以互相通信, 我们需要借助 veth 和 network brdige. veth 的全称是 Virtual Ethernet Device, 即虚拟网卡, 用于在容器网络和宿主机网络之间架起桥梁. bridge 则是链接着所有容器的交换机, 简化路由逻辑.
当 containerd 创建新的容器时, 其新建一个 veth, 连接容器和宿主机的网络空间.
类似命令 ip link add <p1-name> netns <p1-ns> type veth peer <p2-name> netns <p2-ns>
.
我们在一个测试集群中寻找一个容器, 并确定其 veth 对应的 if.
~ k get -n dev -l app=signin pods
NAME READY STATUS RESTARTS AGE
signin-test-a-8bcd5db69-9z2c5 2/2 Running 0 6d3h
~ k exec -n dev -ti signin-test-a-8bcd5db69-9z2c5 -- 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@if220: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether c2:ce:72:1b:97:6e brd ff:ff:ff:ff:ff:ff
~ k exec -n dev -ti signin-test-a-8bcd5db69-9z2c5 -- cat /sys/class/net/eth0/iflink
220
随后, 我们可以在宿主机的网络空间中找到对应的设备.
~ grep -l 220 /sys/class/net/veth*/ifindex
/sys/class/net/veth12acb4c1/ifindex
~ ip link show dev veth12acb4c1
220: veth12acb4c1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cni0 state UP mode DEFAULT group default
link/ether be:78:61:7f:a4:25 brd ff:ff:ff:ff:ff:ff link-netnsid 50
我们可以发现该设备链接到了名为 cni0 的 bridge 上.
~ ls -alht /sys/class/net/veth12acb4c1/ | grep master
lrwxrwxrwx. 1 root root 0 12 月 5 21:49 master -> ../cni0
或者更准确的说, 这台宿主机上和容器绑定的设备都链接到了 cni0.
~ ls -alht /sys/class/net/cni0/ | grep veth | head
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth0c9a5076 -> ../veth0c9a5076
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth75d5e70a -> ../veth75d5e70a
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth12acb4c1 -> ../veth12acb4c1
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth25c523a3 -> ../veth25c523a3
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth54236aab -> ../veth54236aab
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth69d77b1d -> ../veth69d77b1d
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_vethd81e7b63 -> ../vethd81e7b63
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth2ea2cc31 -> ../veth2ea2cc31
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_veth534bb0ad -> ../veth534bb0ad
lrwxrwxrwx. 1 root root 0 12 月 5 23:44 lower_vethabcf68fe -> ../vethabcf68fe
此时容器内看路由表, 可以发现:
# k exec -n dev -ti signin-test-a-8bcd5db69-9z2c5 -- ip route
default via 172.22.0.1 dev eth0
172.22.0.0/24 dev eth0 scope link src 172.22.0.253
172.22.0.0/16 via 172.22.0.1 dev eth0
# k get -n dev pods signin-test-a-8bcd5db69-9z2c5 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
signin-test-a-8bcd5db69-9z2c5 2/2 Running 0 6d4h 172.22.0.253 10.30.180.56 <none> <none>
上文中 172.22.0.0./24 是当前宿主机的 podCIDR, 172.22.0.0/16 是整个集群的 CIDR, 172.22.0.1 是宿主机上 cni0 对应的地址.
# ip addr show dev cni0
7: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether aa:8d:2f:44:14:b0 brd ff:ff:ff:ff:ff:ff
inet 172.22.0.1/24 brd 172.22.0.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::a88d:2fff:fe44:14b0/64 scope link
valid_lft forever preferred_lft forever
在宿主机上看路由表, 可以发现:
ip route | grep -v -E "172.1(7|8)"
default via 10.30.180.254 dev eth0 proto static metric 100
10.30.180.0/24 dev eth0 proto kernel scope link src 10.30.180.56 metric 100
172.22.0.0/24 dev cni0 proto kernel scope link src 172.22.0.1
172.22.1.0/24 via 10.30.180.55 dev eth0