flannel

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

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

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

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_封装

安装

实验节点分布

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_ide_02

  1. 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
  1. 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 已经运行

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_网络_03

  1. 调整 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,已经分布到两个节点上

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_封装_04


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

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_网络_05

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

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_linux_06

# 在 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

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_网络_07


用 pod test1 去 ping test2,在 work node 上抓包

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_ide_08


可以从物理网卡抓到两个 pod 之间的通信流量

在 pod test1 上 traceroute test2,可以看到路径经过了 work node 到达 test2

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_linux_09

VxLAN 实现

基本原理

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

VxLAN 数据包封装:

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_ide_10

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 地址。

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_网络_11

实操

  1. 清理 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
  1. 查看网卡信息
    ip addr,可以看到 vni 接口

    查看 arp 表

    查看转发数据库,bridge fdb

    在 work node 上查看接口,MAC 地址和 master node 上的转发表一致
  2. 创建 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 分布情况:

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_网络_12

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

K8S 网络插件 Underlay IPVLAN k8s网络插件原理_kubernetes_13

UDP 实现

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