flannel
是一种 CNI 解决方案,也可以为 Dokcer 提供服务,对 k8s 而言,是一个网络插件。
- 实现了 CNI 的网络控制平面软件
- 属于 coreOS 的子项目
- 通过配置主机路由或者 overlay,避免对物理路由器进行配置
- VxLAN
- UDP
- Host-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 数据封装,不推荐使用
数据经过用户态转发,性能低