Kubernetes 网络数据包抓包分析

环境信息

  • Centos7
  • Kubernetes 1.21
  • flannel 0.20

为观察 Kubernetes 集群中跨主机 POD 之间的网络通信数据流,本文通过 tcpdump 抓包,对数据流向进行记录观察。

本示例中的 Kubernetes 集群由以下节点组成:

节点主机名 节点角色 节点 IP 节点上测试 POD IP
master master eth0: 10.150.0.21 10.244.0.2
worker1 worker eth0: 10.150.0.19 10.244.1.38
worker2 worker eth0: 10.150.0.20 10.244.2.27

Flannel 简介

Kubernetes 系统上 POD 网络的实现依赖于第三方插件,Kubernetes 只负责提供了 CNI(容器网络接口),只要符合 CNI 规则的第三方插件都可以用于为 POD 提供网络实现。而 Flannel 是由 CoreOS 主推的目前比较主流的容器网络解决方案。

Flannel 支持三种不同后端实现,分别是:

  • UDP
  • VXLAN
  • host-gw

UDP 是 Flannel 项目最早支持的一种方式,是性能最差的方式,目前已被废弃。

用的最多的是 VXLAN 和 host-gw 模式的部署。

VXLAN

Flannel 默认使用 VXLAN 模式。flannel 运行后,在各个宿主机配置了一个网络接口 flannel.1

$ ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1410
inet 10.244.0.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::7c63:25ff:fea9:a780 prefixlen 64 scopeid 0x20<link>
ether 7e:63:25:a9:a7:80 txqueuelen 0 (Ethernet)
RX packets 2857781 bytes 1505969406 (1.4 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1946111 bytes 1603736503 (1.4 GiB)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0

默认分配给集群 POD 的网段为 10.244.0.0/16

flannel 给 master 节点配置的 Pod 网络为 10.244.0.0/24 段,给 worker1 节点配置的 Pod 网络为 10.244.1.0/24 段,如果有更多的节点,以此类推。

当有容器运行之后,flannel 会在节点上配置虚拟接口 cni0,比如在节点 worker1,IP 地址默认为 10.244.1.1,在 master 节点上 IP 为 10.244.0.1

cni0 接口是一个网桥设备,会作为此节点上的 POD 的默认网关

在 POD 启动时,flannel 会创建一对 veth 虚拟设备,一端连接到容器,一端连接到 cni0

$ ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1410
inet 10.244.0.1 netmask 255.255.255.0 broadcast 10.244.0.255
inet6 fe80::f054:6bff:fe0e:f9ef prefixlen 64 scopeid 0x20<link>
ether f2:54:6b:0e:f9:ef txqueuelen 1000 (Ethernet)
RX packets 3795780 bytes 469016599 (447.2 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3832558 bytes 408850763 (389.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

一旦节点启动并加入集群后,其他节点上的 flanneld 会添加路由规则,将到其他节点上的 POD 网段的流量路由到本节点的 flannel.1 接口,即进入二层隧道,然后封装 VXLAN 包,到达目标节点后(pod 网段和节点 ip 的映射关系,flannel 保存在 etcd 或者 k8s 中),由目标节点上的 flannel.1 解封装,这就是 flannel 默认的 VXLAN 模式

$ ip route show
default via 10.150.0.1 dev eth0
10.150.0.1 dev eth0 scope link
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
169.254.0.0/16 dev eth0 scope link metric 1002

由于 VXLAN 额外的封包解包,导致其性能较差,所以 Flannel 就有了 host-gw 模式,即把宿主机当作网关,除了本地路由之外没有额外开销,性能和 calico 差不多,由于没有叠加来实现报文转发,这样会导致路由表庞大。因为一个节点对应一个网络,也就对应一条路由条目。

host-gw 虽然比 VXLAN 网络性能要强很多,但是种方式有个缺陷:要求各物理节点必须在同一个二层网络中。物理节点必须在同一网段中。这样会使得一个网段中的主机量会非常多,万一发一个广播报文就会产生干扰。在私有云场景下,宿主机不在同一网段是很常见的状态,所以就不能使用 host-gw 了。

VXLAN 还有另外一种功能,VXLAN 也支持类似 host-gw 的方案,如果两个节点在同一网段时使用 host-gw 通信,如果不在同一网段中,即当前 pod 所在节点与目标 pod 所在节点中间有路由器,就使用 VXLAN 这种方式,使用叠加网络。

结合了 Host-gw 和 VXLAN,这就是 VXLAN 的 Directrouting 模式

因此 Flannel 的 VXLAN 模式有两种:

  • VXLAN: 原生的 VXLAN,即扩展的虚拟 LAN
  • Directrouting:直接路由型

配置 Directrouting 模式

修改 kube-flannel.yml,修改以下内容:

kube-flannel.yml
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true
}
}

抓包示例

在 Flannel 的 VXLAN 模式下,overlay 流量需要通过节点的 8472/udp 端口(隧道)进行跨节点通信,如果跨节点 POD 网络通信存在异常,可以在两个节点上对 8472/udp 抓包观察流量

tcpdump -i eth0 -nnn 'udp port 8472 and host 10.150.0.19'

参考链接

详解Kubernetes网络原理