k8s flannel 插件 基本通信原理

    技术2022-07-13  61

    flannel

    是一种 CNI 解决方案,也可以为 Dokcer 提供服务,对 k8s 而言,是一个网络插件。

    实现了 CNI 的网络控制平面软件属于 coreOS 的子项目通过配置主机路由或者 overlay,避免对物理路由器进行配置 VxLANUDPHost-GW

    和 k8s 集成时,运行在 work node 上面,监听 k8s master 的状态,共用 k8s 的控制节点的 etcd 作为自己的数据库。

    安装

    实验节点分布

    master node

    # 初始化 master 节点 sudo kubeadm reset sudo kubeadm init --config kubadm.yaml # 下载 flannel 配置文件 wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml # 修改配置文件,net-json 改为 k8s 安装的 podSubnet,type 默认为 vxlan net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan" } } # 部署 kubectl apply -f kube-flannel.yml # 查看 kubectl get pods --all-namespaces --- NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-66bff467f8-m7ghl 0/1 ContainerCreating 0 11m kube-system coredns-66bff467f8-mgnj7 0/1 ContainerCreating 0 11m kube-system etcd-x-vm 1/1 Running 0 11m kube-system kube-apiserver-x-vm 1/1 Running 1 11m kube-system kube-controller-manager-x-vm 1/1 Running 0 11m kube-system kube-flannel-ds-amd64-g7hl9 1/1 Running 0 35s kube-system kube-proxy-5x7l5 1/1 Running 0 11m kube-system kube-scheduler-x-vm 1/1 Running 0 11m # 多次查看,可以看到 coredns Pending -> ContainerCreating -> Running,因为 flannel 初始化初始化完成之后,k8s 认为当前节点可用,就创建了 coredns

    worker node

    安装 docker、kubeadm,关闭 swap,加入到集群中,hostname 不能重复

    # 在 master node 上初始化完成之后,会输出如下 token kubeadm join 192.168.121.137:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:719b052641c7681b770f9609e82d6a8001ef9aa1125db6cea7b1a452d555c34a # 加入完成后,在 master node 上查看 kubectl get nodes NAME STATUS ROLES AGE VERSION worker Ready <none> 52s v1.18.4 x-vm Ready master 23m v1.18.4

    在 worker node 上查看容器,确认 flannel、kube-proxy 已经运行

    调整 coredns,使其分布到 worker node 上

    # -n 指定 namespace,先删除 kubectl scale -n kube-system deployment.v1.apps/coredns --replicas=0 # 再将数量调整为 2,期望结果是两个 codedns 的 pod 分布到两个节点上 kubectl scale -n kube-system deployment.v1.apps/coredns --replicas=2

    查看 pod 分布情况 kubectl get pods --all-namespaces -o wide,已经分布到两个节点上 查看 worker node 网卡信息,可以看到 doredns 容器对应的 veth 已经存在,而且也有了 cni 网卡

    host-gw 实现

    基本原理

    在 kube-net 网络实现中:

    同 node 中的 pod 互相通信是通过 cbr0 网桥二层互通跨 node 通信是通过主机的默认路由,路由到物理网络中进行数据转发,此时需要在物理路由器上进行路由相关的配置

    部署 flannel 时,将配置文件中 net-json 字段的 Type 修改为 host-gw 。 Flannel host-gw 实现方案中,由于 linux 具有路由转发功能,所以可以将物理路由器相关的配置下沉到 work node (主机)上,由主机进行路由,类似于DVR,也避免了单点故障。 Flannel 连接 k8s 的数据库,每个 node 上的 flanned 进程知道所有 podSubnet 对应的 node,进而在主机的网络空间中配置 podSubnet 的路由指向对应的 node。 Flannel 环境中连接 pod 的 Linuxbridge 为 cni0(kube-net 是 cbr0),所有的 work node 必须在同一个二层网络中(添加路由时必须是二层可达才会生效)

    实操

    Flannel 安装完成后,查看路由信息:

    # 在 master node 上创建一个临时的 pod,使用 nodeSelector 指定运行的 node kubectl run -it --rm --restart=Never test1 --image busybox --overrides='{"apiVersion": "v1","spec": {"nodeSelector": {"kubernetes.io/hostname": "x-vm"}}}' sh # 新开一个窗口,在 work node 上创建一个临时的 pod kubectl run -it --rm --restart=Never test2 --image busybox --overrides='{"apiVersion": "v1","spec": {"nodeSelector": {"kubernetes.io/hostname": "worker"}}}' sh

    新开一个窗口,检查一个 pod 运行情况,可以看到分布在两个 node 上,IP 分别是 11.0.0.5 和 11.0.1.3 用 pod test1 去 ping test2,在 work node 上抓包 可以从物理网卡抓到两个 pod 之间的通信流量 在 pod test1 上 traceroute test2,可以看到路径经过了 work node 到达 test2

    VxLAN 实现

    基本原理

    Ethernet Frame 封装到 UDP 中不考虑物理网络冲突问题封装需要额外的 50 字节(网卡默认 MTU 为 1450)允许 woker node 分布到三层网络中

    VxLAN 数据包封装:

    Flannel VxLAN 基于 Linux 原生的方式实现 VxLAN

    # Linux 通过 VxLAN 字节口实现 VxLAN 的封装解封装 ip link add vxlan0 type vxlan id 1 \ remote 192.168.121.137 \ local 192.168.121.138 \ dstport 8472 \ dev eth0 # 通过监听端口来拦截数据进行封装解封装

    Flannel 的 VxLAN 实现

    Flannel 会在 node 上额外创建 flannel.<vni> 设备,挂载的 ip 为当前 node podSubnet 的第 0 个地址作为 VTEP 地址。

    当有多个 node 时,如果按照 Linux 原生方式实现 VxLAN 时,每个 node 都要和其他 node 建立 VxLAN 隧道,也就是每个 node 上都要创建多个类型为 vxlan 的 netdev 设备,这样子接口的数量就是 n^2。

    为了避免这种情况,flannel 添加 flannel.vni 子接口的时候,并没有指定 remote ip,而是添加了对端 flannel.vni 的静态 arp 表项,并添加二层转规则(bridge fdb 查看),如果是发往对端的 flannel.vni 的 MAC 地址的话,从本端子接口发出,且指定了远端的 VTEP 地址。

    实操

    清理 host-gw 环境,修改配置文件,重新部署 flannel

    # 删除 flannel kubectl delete -f kube-flannel.yml # 删除 coredns kubectl scale -n kube-system deployment.v1.apps/coredns --replicas=0 # 修改 kube-flannel.yml 中 net-json type 为 vxlan # 重新部署 kubectl apply -f kube-flannel.yml # 添加 coredns kubectl scale -n kube-system deployment.v1.apps/coredns --replicas=2

    查看网卡信息

    ip addr,可以看到 vni 接口 查看 arp 表 查看转发数据库,bridge fdb 在 work node 上查看接口,MAC 地址和 master node 上的转发表一致

    创建 pod ,抓包验证

    # 在 master node 上创建一个临时的 pod,使用 nodeSelector 指定运行的 node kubectl run -it --rm --restart=Never test1 --image busybox --overrides='{"apiVersion": "v1","spec": {"nodeSelector": {"kubernetes.io/hostname": "x-vm"}}}' sh # 新开一个窗口,在 work node 上创建一个临时的 pod kubectl run -it --rm --restart=Never test2 --image busybox --overrides='{"apiVersion": "v1","spec": {"nodeSelector": {"kubernetes.io/hostname": "worker"}}}' sh

    查看 pods 分布情况:

    用 test1 ping test2 ,在 worker 物理接口上抓包,可以看到封装数据包的内容

    UDP 实现

    非 VxLAN 的 UDP 数据封装,不推荐使用 数据经过用户态转发,性能低

    Processed: 0.014, SQL: 10