flannel 学习笔记

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

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

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

安装

节点分布
37ae6a952c139971c3cdbac8588173f6.png

  1. master node

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    # 初始化 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",
    "Directrouting": false
    }
    }
    # 部署
    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

  2. worker node

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

    1
    2
    3
    4
    5
    6
    7
    8
    # 在 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 已经运行
    4378c8c0098fd036be47a1d0177623c3.png

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

    1
    2
    3
    4
    # -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,已经分布到两个节点上
    216db286ae71b105eb3bd7b2a136de02.png

    查看 worker node 网卡信息,可以看到 doredns 容器对应的 veth 已经存在,而且也有了 cni 网卡
    f027622b6f15a60cb387bb86f214d02e.png

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 安装完成后,查看路由信息:
查看路由信息.png

1
2
3
4
# 在 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
7de41a1c0421d0999013099aa1f28edd.png
用 pod test1 去 ping test2,在 work node 上抓包
5ef90a49d64b4faa06f19d3273a2e4aa.png
可以从物理网卡抓到两个 pod 之间的通信流量
在 pod test1 上 traceroute test2,可以看到路径经过了 work node 到达 test2
460c4781a35299373800e1bd52c187f0.png

VxLAN 实现

基本原理

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

VxLAN 数据包封装:
24d7e3e98a34313a7f7b2a54047828aa.png

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

1
2
3
4
5
6
7
# 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 地址。

83b6071b31e417bfa2ed3d00c4fae740.png

Direct routing 实现

传统的 VXLAN 使用隧道的通信方式会导致报文开销及流量增大,对于节点之间二层网络的通信,可以直接使用路由的方式而不用隧道,仅在跨三层通信时使用隧道封装,这样可以大大节省开销,二层通信性能也接近物理网络。

仅使用 VXLAN,未启用 direct routing 时,路由表中同一二层网络的节点,路由表显示下一跳为 flannel.vni;启用 direct routing 后,路由表中下一跳变为物理网卡。

启用方式,修改配置文件中 Directrouting 字段为 true

实操

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 删除 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
  2. 查看网卡信息

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

  3. 创建 pod ,抓包验证

    1
    2
    3
    4
    5
    # 在 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 分布情况:
    b4b779f57a9c73b69244c34a23134b32.png

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

UDP 实现

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