前面,我们在[搭建 Kubernetes 集群](https://xie.infoq.cn/article/4a4bf681ef319ca80674e9c6e)时,用了比较常见的 Flannel 做为集群的 CNI,它会负责集群内跨 Node 的通信。
该 CNI 的 yaml 文件定义在这里:https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
其中,跟本文相关的重点内容是:
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
复制代码
在 net-conf.json 部分,可以看到,集群内 Pod 的网段是:10.244.0.0/16;默认启用的 backend 类型是 VXLAN。
本文将分析两种不同情况下的 Pod 之间的网络通信方式:
相同 Node,不同 Pod;
不同 Node,不同 Pod。
实验的集群,带了两个 Node,其中的 Master Node,已经被去除了污点,允许被调度。
为了实验相同 Node、不同 Node 的两种情况,计划建立 4 个 Replicas 的 Busybox。这样可以保证:每个 Node 上都有两个 Pod,用来做相同 Node 通信的情况;每个 Node 上都会有 Pod,用来实验不同 Node 通信的情况。
OK, let's hit the road.
1. 创建 YAML 文件
首先创建 Deployment 的 YAML 文件:
$ vi busybox_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
spec:
selector:
matchLabels:
app: busybox
replicas: 4
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
复制代码
按:wq 保存退出。
2. 创建 Deployment
用上一步的 YAML 文件,创建 K8S 的 Deployment:
$ kubectl apply -f busybox_deployment.yaml
deployment.apps/busybox-deployment configured
复制代码
可以看到,K8S 已经按照预期,在两个 Node 上,分别创建了两个 Pod:
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-deployment-6fc48fb64-4c5l2 1/1 Running 0 12s 10.244.0.19 ycwang-ubuntu <none> <none>
busybox-deployment-6fc48fb64-4nccz 1/1 Running 0 12s 10.244.1.14 ycwang-ubuntu-worker <none> <none>
busybox-deployment-6fc48fb64-jbhxl 1/1 Running 0 12s 10.244.1.13 ycwang-ubuntu-worker <none> <none>
busybox-deployment-6fc48fb64-vd89t 1/1 Running 0 12s 10.244.0.18 ycwang-ubuntu <none> <none>
复制代码
其中,10.244.0.18、10.244.0.19 的两个 Pod,运行在 Master Node;10.244.1.13、10.244.1.14 的两个 Pod,运行在 Worker Node。
3. 节点上的网卡
先看一下此时在 Master Node 上的网卡情况:
$ ip addr
......
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:49:39:91 brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet 192.168.111.128/24 brd 192.168.111.255 scope global dynamic noprefixroute ens33
valid_lft 1628sec preferred_lft 1628sec
inet6 fe80::72bf:3960:42cd:13cb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
......
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether ce:5f:f6:73:cf:b9 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::cc5f:f6ff:fe73:cfb9/64 scope link
valid_lft forever preferred_lft forever
5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether e6:62:4d:8e:51:21 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.1/24 brd 10.244.0.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::e462:4dff:fe8e:5121/64 scope link
valid_lft forever preferred_lft forever
.......
复制代码
我们只要观察上面的三个网卡即可。
ens33:是 Node 的物理网卡,所有跨 Node 的通信,不管用了什么技术,最终都是要通过它进出的。
flannel.1:是 Flannel 安装的网卡/VTEP,用来实现 VXLAN 的支持。跨 Node 的 Pod 之间通信,都需要经过它来进行 Overlay 网络的封装和解封装。
可以看一下它的具体信息:
$ ip -details link show flannel.1
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 36:a8:31:9e:3e:5d brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
vxlan id 1 local 192.168.111.128 dev ens33 srcport 0 0 dstport 8472 nolearning ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
复制代码
可以看到,这个 VXLAN 设备,VNI 是 1、本地 IP 是 192.168.111.128、目标端口是 8472(不是标准 VXLAN 的 4789)。
cni0:这个跟 Docker 里面带的 Docker0 感觉没什么区别,就是一个 Linux Bridge。每个 Pod 会产生一个 veth pair,一端接在 Pod 里面;另一端接在 cni0 里面。Pod 之间的通信,都需要经过这个网桥。
4. 网络拓扑
这张图,是 Pod 在相同 Node 通信、不同 Node 通信的总结。后面的实验,都是为了验证这张图。
相同 Node、不同 Pod 之间的通信,走的是黑色 + 绿色的线。Pod A 和 Pod B 都通过 veth 接在同一个网桥 cni0 上。Pod A 将数据通过 veth 发到 cni0 之后,cni0 将数据转发到 Pod B 的 veth。
不同 Node、不同 Pod 之间的通信,走的是黑色 + 蓝色的线。Node 1 上的 Pod A,给 Node 2 上的 Pod C 发数据时,途径 cni0,然后经过 flannel.1 进行 VXLAN 封装,最后依靠 Node 1 的物理网卡将数据发送给 Node 2 的物理网卡。Node 2 的物理网卡再将数据依次发送给 Node 2 上的 flannel.1、cni0,最后到达 Pod C。
5. 相同 Node 通信实验
登录运行在 Master Node 上的一个 Pod:
$ kubectl exec -it busybox-deployment-6fc48fb64-4c5l2 -- sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether a6:72:78:4a:0b:72 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.19/24 brd 10.244.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::a472:78ff:fe4a:b72/64 scope link
valid_lft forever preferred_lft forever
复制代码
该 Pod 的 IP 是:10.244.0.19。用它去 ping 相同 Node 上的另一个 Pod:10.244.0.18:
/ # ping 10.244.0.18
PING 10.244.0.18 (10.244.0.18): 56 data bytes
64 bytes from 10.244.0.18: seq=0 ttl=64 time=0.373 ms
64 bytes from 10.244.0.18: seq=1 ttl=64 time=0.065 ms
......
复制代码
然后在 Node 上的 cni0 抓包:
$ sudo tcpdump -i cni0 -s 0 -X -nnn -vvv
17:06:16.588123 IP (tos 0x0, ttl 64, id 58509, offset 0, flags [DF], proto ICMP (1), length 84)
10.244.0.19 > 10.244.0.18: ICMP echo request, id 15, seq 17, length 64
0x0000: 4500 0054 e48d 4000 4001 400f 0af4 0013 E..T..@.@.@.....
0x0010: 0af4 0012 0800 f64d 000f 0011 e9d3 17be .......M........
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000 ....
17:06:16.588135 IP (tos 0x0, ttl 64, id 12216, offset 0, flags [none], proto ICMP (1), length 84)
10.244.0.18 > 10.244.0.19: ICMP echo reply, id 15, seq 17, length 64
0x0000: 4500 0054 2fb8 0000 4001 34e5 0af4 0012 E..T/...@.4.....
0x0010: 0af4 0013 0000 fe4d 000f 0011 e9d3 17be .......M........
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000
复制代码
可以看到,ICMP 的往返报文在 cni0 上传递。
如果此时在 Node 上的 flannel.1 抓包,可以发现什么报文都没有:
$ sudo tcpdump -i flannel.1 -s 0 -X -nnn -vvv
复制代码
所以,相同 Node、不同 Pod 之间的通信,依靠的是 veth + cni0 网桥。
6. 不同 Node 通信实验
继续用 Master Node 的 Pod 10.244.0.19,用它去 ping 在另一个 Node 上的 Pod 10.244.1.13:
/ # ping 10.244.1.13
PING 10.244.1.13 (10.244.1.13): 56 data bytes
64 bytes from 10.244.1.13: seq=0 ttl=62 time=1.099 ms
64 bytes from 10.244.1.13: seq=1 ttl=62 time=0.544 ms
......
复制代码
此时,在 Master Node 上的 cni0 抓包:
$ sudo tcpdump -i cni0 -s 0 -X -nnn -vvv
tcpdump: listening on cni0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:10:01.770478 IP (tos 0x0, ttl 64, id 48266, offset 0, flags [DF], proto ICMP (1), length 84)
10.244.0.19 > 10.244.1.13: ICMP echo request, id 19, seq 4, length 64
0x0000: 4500 0054 bc8a 4000 4001 6717 0af4 0013 E..T..@.@.g.....
0x0010: 0af4 010d 0800 0647 0013 0004 6dd6 83cb .......G....m...
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000 ....
17:10:01.771463 IP (tos 0x0, ttl 62, id 15347, offset 0, flags [none], proto ICMP (1), length 84)
10.244.1.13 > 10.244.0.19: ICMP echo reply, id 19, seq 4, length 64
0x0000: 4500 0054 3bf3 0000 3e01 29af 0af4 010d E..T;...>.).....
0x0010: 0af4 0013 0000 0e47 0013 0004 6dd6 83cb .......G....m...
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000
复制代码
可以看到,ICMP 的往返报文也是通过 cni0 进行传递的。
继续在 Master Node 上的 flannel.1 抓包:
$ sudo tcpdump -i flannel.1 -s 0 -X -nnn -vvv
tcpdump: listening on flannel.1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:12:02.155017 IP (tos 0x0, ttl 63, id 10040, offset 0, flags [DF], proto ICMP (1), length 84)
10.244.0.19 > 10.244.1.13: ICMP echo request, id 20, seq 3, length 64
0x0000: 4500 0054 2738 4000 3f01 fd69 0af4 0013 E..T'8@.?..i....
0x0010: 0af4 010d 0800 cc53 0014 0003 7ac2 b0d2 .......S....z...
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000 ....
17:12:02.156094 IP (tos 0x0, ttl 63, id 36934, offset 0, flags [none], proto ICMP (1), length 84)
10.244.1.13 > 10.244.0.19: ICMP echo reply, id 20, seq 3, length 64
0x0000: 4500 0054 9046 0000 3f01 d45b 0af4 010d E..T.F..?..[....
0x0010: 0af4 0013 0000 d453 0014 0003 7ac2 b0d2 .......S....z...
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000
复制代码
这种情况下,报文经 cni0,传送到了 flannel.1。Flannel.1 进行 VXLAN 封装后,会继续传送到 Master Node 的物理网卡。
继续在 Master Node 上的 ens33 抓包:
$ sudo tcpdump -i ens33 udp -s 0 -X -nnn -vvv
tcpdump: listening on ens33, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:12:46.181905 IP (tos 0x0, ttl 64, id 21130, offset 0, flags [none], proto UDP (17), length 134)
192.168.111.128.60568 > 192.168.111.129.8472: [bad udp cksum 0x60d6 -> 0x76a1!] OTV, flags [I] (0x08), overlay 0, instance 1
IP (tos 0x0, ttl 63, id 15296, offset 0, flags [DF], proto ICMP (1), length 84)
10.244.0.19 > 10.244.1.13: ICMP echo request, id 20, seq 47, length 64
0x0000: 4500 0086 528a 0000 4011 c78a c0a8 6f80 E...R...@.....o.
0x0010: c0a8 6f81 ec98 2118 0072 60d6 0800 0000 ..o...!..r`.....
0x0020: 0000 0100 3a87 cf81 6ace ce5f f673 cfb9 ....:...j.._.s..
0x0030: 0800 4500 0054 3bc0 4000 3f01 e8e1 0af4 ..E..T;.@.?.....
0x0040: 0013 0af4 010d 0800 2e59 0014 002f 788e .........Y.../x.
0x0050: 50d5 0000 0000 0000 0000 0000 0000 0000 P...............
0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0080: 0000 0000 0000 ......
17:12:46.182333 IP (tos 0x0, ttl 64, id 8026, offset 0, flags [none], proto UDP (17), length 134)
192.168.111.129.55939 > 192.168.111.128.8472: [udp sum ok] OTV, flags [I] (0x08), overlay 0, instance 1
IP (tos 0x0, ttl 63, id 42036, offset 0, flags [none], proto ICMP (1), length 84)
10.244.1.13 > 10.244.0.19: ICMP echo reply, id 20, seq 47, length 64
0x0000: 4500 0086 1f5a 0000 4011 faba c0a8 6f81 E....Z..@.....o.
0x0010: c0a8 6f80 da83 2118 0072 88b6 0800 0000 ..o...!..r......
0x0020: 0000 0100 ce5f f673 cfb9 3a87 cf81 6ace ....._.s..:...j.
0x0030: 0800 4500 0054 a434 0000 3f01 c06d 0af4 ..E..T.4..?..m..
0x0040: 010d 0af4 0013 0000 3659 0014 002f 788e ........6Y.../x.
0x0050: 50d5 0000 0000 0000 0000 0000 0000 0000 P...............
0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0080: 0000 0000 0000
复制代码
可以看到,这是 Overlay 的报文。报文内层是 10.244.0.19 > 10.244.1.13 的 ICMP 请求;报文外层是 192.168.111.128 > 192.168.111.129 的 UDP 请求。只不过由于它的目标端口是自定义的 8472,而不是 RFC 定义的 4789,tcpdump 没把它解释成 VXLAN 报文而已。
所以,不同 Node、不同 Pod 之间的通信,依靠的是 veth + cni0 + flannel.1(VXLAN)。
评论