记一次flannel跨节点不通的故障

发布时间 2023-04-14 17:39:38作者: shininglight

先贴一张flannel通信原理图镇楼。

最近遇到了一个问题,在一个k8s集群中有一个Fedora节点的flannel.1和别的节点flannel.1不通。确认了一下内容:

  1. flannel configmap中的vxlan network配置是否和集群配置文件cat /etc/kubernetes/manifests/kube-controller-manager.yaml |grep cidr一致
  2. 该节点的/run/flannel/subnet.env配置和该节点的cni0属性已经flannel.1的属性(ifconfig查看地址,ip -d link show flannel.1查看flannel.1绑定的网卡以上图为例绑定eth0网卡)
  3. 本地路由
  4. 查看各节点上针对flannel网段下一跳网卡即各个flannel.1的MAC表项这一项往往容易被忽略,但很重要
    以上第四项内容10.244.9.0对应的MAC不正确,不知道是谁回了这个ARP. MAC 表项如下:
[root@cluster-worker ~]# arp -a
? (192.168.8.2) at 36:9c:23:fb:7a:b9 [ether] on eth0
? (192.168.8.7) at 00:2b:69:a2:02:27 [ether] on eth0
? (192.168.8.204) at 54:54:00:87:66:cb [ether] on eth0
? (10.244.9.0) at 42:0b:23:0b:75:b3 [ether] PERM on flannel.1
? (10.244.10.0) at e6:69:ba:6c:4f:a2 [ether] PERM on flannel.1
? (192.168.8.210) at d4:5d:62:bb:a4:19 [ether] on eth0

仔细看这个表项是PERM,说明是静态MAC项,因此应该是flannel配置的. 查阅相关资料果然,flannel容器启动的时候会给自己所在的node注入一些信息,即添加一下annotation, 如下所示:

root@cluster-master:~# kubectl describe node cluster-worker  |grep -i flannel
Annotations:        flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"42:0b:23:0b:75:b3"}
                    flannel.alpha.coreos.com/backend-type: vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager: true
                    flannel.alpha.coreos.com/public-ip:192.168.8.123

然后flannel会根据这些节点信息配置静态MAC表,用命令kubectl edit node 来修改这些Annotations, 然后可以通过ip neigh show dev flannel.1; bridge fdb show flannel.1查看MAC表项。
如果不修改这些而尝试以下操作也是不行的:

1. /bin/rm -fr /var/lib/cni/flannel
2. ip link set cni0 down && ip link set flannel.1 down 
   ip link delete cni0 && ip link delete flannel.1
   systemctl restart containerd && systemctl restart kubelet

如果是该节点没有cni0网桥,还相对好解决一些,可以用一下命令创建:

ip link add cni0 type bridge
ip addr add dev cni0 10.244.3.1/24 #该节点的/run/flannel/subnet.env中FLANNEL_SUBNET值
ip link set cni0 up #或者 ifconfig cni0 up
查看是否有cni0的路由,该link up后内核会自动添加该项路由。最后需要重建该节点上的flannel管理的容器,使得容器在主机端的Veth-pair添加到cni0上。

另外,想看看Flannel代码。
flannel代码有好几个项目:

  1. https://github.com/flannel-io/flannel #flanneld,flannel守护进程
  2. https://github.com/flannel-io/plugins/ #二进制bridge ipvlan loopback macvlan ptp vlan host-device dhcp staic等等
  3. https://github.com/flannel-io/cni-plugin #二进制flannel, 和flanneld合作配置容器网络
  4. https://github.com/vishvananda/netlink #flannel 有大量操作 Linux 网络设备的代码,如创建 VxLAN 设备、操作路由、操作 fdb 等,使用了该库的调用

以上项目编译出来的二进制文件放在 /opt/cni/bin 目录下,由 kubelet 在创建单个 pod 的时候调用. 将读取 flanneld 产生的 /run/flannel/subnet.env,生成一系列配置文件,写入 /var/lib/cni/flannel/[pod id],然后将生成的配置作为参数调用另一个 CNI plugin 二进制 bridge.其中,当 kube-controller-manager 设置了 allocate-node-cidrs 和 cluster-cidr 参数时,kube-controller-manager 会为每个 node 确定 pod ip 范围。flanneld 刚启动时,在 RegisterNetwork (调用 kubeSubnetManager.AcquireLease)中获取当前 node 的 Spec.PodCIDR,并把需要的一些信息写入到 node 的 annotation。子网信息再写入到 /run/flannel/subnet.env (main.WriteSubnetFile),由 flannel CNI 读取,用于分配 pod ip。
bridge 二进制第一次运行的时候会在 node 上生成一个 Linux bridge 设备,默认名字是 cni0。这个 bridge 就是一个虚拟交换机,新生成的 pod 网卡会通过 veth 设备连接到这个 bridge 上面。
bridge 每次被调用的时候,会给 pod 创建 veth,将 veth 连接到 cni0,并且调用 host-local 从本机 subnet 中分配 ip。

几个跟 flannel CNI 有关的文件或目录:

  1. /etc/cni/net.d/10-flannel.conf flannel CNI 的配置文件。
  2. /var/lib/cni/flannel 这个目录下放的是 flannel 每次调用 bridge 用到的配置,文件名是 io.kubernetes.sandbox.id (通过docker inspect [container id] 可以看到)。
  3. /var/lib/cni/networks/cbr0 这个目录下放的是 host-local CNI 分配的 IP,文件名为分配的容器 IP,文件内容为io.kubernetes.sandbox.id。