写点什么

Kubernetes CNI 之 Flannel 网络模型分析

作者:王玉川
  • 2023-05-13
    上海
  • 本文字数:6897 字

    阅读完需:约 23 分钟

Kubernetes CNI之Flannel网络模型分析

前面,我们在[搭建 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 之间的网络通信方式:


  1. 相同 Node,不同 Pod;

  2. 不同 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/v1kind: Deploymentmetadata: name: busybox-deploymentspec: 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.yamldeployment.apps/busybox-deployment configured
复制代码


可以看到,K8S 已经按照预期,在两个 Node 上,分别创建了两个 Pod:


$ kubectl get pods -o wideNAME                                 READY   STATUS    RESTARTS   AGE     IP            NODE                   NOMINATED NODE   READINESS GATESbusybox-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 forever5: 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.14: 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 addr1: 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 forever2: 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.18PING 10.244.0.18 (10.244.0.18): 56 data bytes64 bytes from 10.244.0.18: seq=0 ttl=64 time=0.373 ms64 bytes from 10.244.0.18: seq=1 ttl=64 time=0.065 ms......
复制代码


然后在 Node 上的 cni0 抓包:


$ sudo tcpdump -i cni0 -s 0 -X -nnn -vvv17: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.13PING 10.244.1.13 (10.244.1.13): 56 data bytes64 bytes from 10.244.1.13: seq=0 ttl=62 time=1.099 ms64 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 -vvvtcpdump: listening on cni0, link-type EN10MB (Ethernet), snapshot length 262144 bytes17: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 -vvvtcpdump: listening on flannel.1, link-type EN10MB (Ethernet), snapshot length 262144 bytes17: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 -vvvtcpdump: listening on ens33, link-type EN10MB (Ethernet), snapshot length 262144 bytes17: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 1IP (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 1IP (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)。

用户头像

王玉川

关注

https://yuchuanwang.github.io/ 2018-11-13 加入

https://www.linkedin.com/in/yuchuan-wang/

评论

发布
暂无评论
Kubernetes CNI之Flannel网络模型分析_Kubernetes_王玉川_InfoQ写作社区