一、Flannel简介
1.1、Flannel原理架构
github地址:https://github.com/flannel-io/flannel
flannel最早由CoreOS开发,它是容器编排系统中最成熟的网络插件示例之一。随着CNI概念的兴起,flannel也是最早实现CNI标准的网络插件(CNI标准也是由CoreOS提出的)。flannel的功能非常简单明确,解决容器跨节点访问的问题。flannel的设计目的是为集群中的所有节点重新规划IP地址的使用规则,从而使得集群中的不同节点主机创建的容器都具有全集群“唯一”且“可路由的IP地址”,并让属于不同节点上的容器能够直接通过内网IP通信。那么节点是如何知道哪些IP可用,哪些不可用,即其他节点已经使用了哪些网段,flannel就用到了etcd的分布式协同功能。
flannel网络中pod跨主机转发流程
flannel在架构上分为管理面和数据面,管理面主要包含一个etcd,用于协调各个节点上容器分配的网段,数据面即在每个节点上运行一个flanneld进程。与其他网络方案不同的是,flannel采用的是no server架构,即不存在所谓的控制节点,简化了flannel的部署与运维。集群内所有flannel节点共享一个大的容器地址段(在我们的例子中就是10.0.0.0/16),flanneld一启动便会观察etcd,从etcd得知其他节点上的容器已占用网段信息,然后像etcd申请该节点可用的IP地址段(在大网段中划分一个,例如10.0.2.0/24),并把该网段和主机IP地址等信息都记录在etcd中。flannel通过etcd分配了每个节点可用的IP地址段后,修改了Docker的启动参数,例如--bip=172.17.18.1/24限制了所在节点容器获得的IP范围,以确保每个节点上的Docker会使用不同的IP地址段,需要注意的是,这个IP范围是有flannel自动分配的,由flannel通过保存在etcd服务中的记录确保它们不会重复,无需用户手动干预。flannel的底层实现实质上是一种overlay网络(除了Host-Gateway模式),即把某一协议的数据包封装在另一种网络协议中进行路由转发。flannel在封包的时候会观察etcd的数据,在其他节点向etcd更新网段和主机IP信息时,etcd感知到,在向其他主机上的容器转发网络包,用对方的容器所在主机的IP进行封包,然后将数据发往对应主机上的flanneld,再交由其转发给目的容器。
flannel架构图
flannel网络模式
Flannel 网络模型 (后端模型),Flannel目前有三种方式实现 UDP/VXLAN/host-gw
- UDP:早期版本的Flannel使用UDP封装完成报文的跨越主机转发,其安全性及性能略有不足。
- VXLAN:Linux 内核在在2012年底的v3.7.0之后加入了VXLAN协议支持, 因此新版本的Flannel也有UDP转换为VXLAN,VXLAN本质上是一种tunnel(隧道) 协议,用来基于3层网络实现虚拟的2层网络,目前flannel 的网络模型已经是基于VXLAN的叠加(覆盖)网络,目前推荐使用vxlan作为其网络模型。
- Host-gw:也就是Host GateWay,通过在node节点上创建到达各目标容器地址的路由表而完成报文的转发,因此这种方式要求各node节点本身必须处于同一个局域网(二层网络)中,因此不适用于网络变动频繁或比较大型的网络环境,但是其性能较好。
Flannel 组件:
- Cni0:网桥设备,每创建一个pod都会创建一对veth pair,其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡),Pod中从网卡eth0发出的流量都会发送到Cni0网桥设备的端口(网卡)上,Cni0设备获得的ip地址是该节点分配到的网段的第一个地址。
- Flannel.1: overlay网络的设备,用来进行vxlan报文的处理(封包和解包),不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。
1.2、flannel UDP模式跨主机通信
容器跨节点通信实现流程:假设在节点A上有容器A(10.244.1.96),在节点B上有容器B(10.244.2.194)。此时容器A向容器发送一个ICMP请求报文(ping)。
(1)容器A发出ICMP请求报文,通过IP封装后的形式为10.244.1.96(源)--->10.244.2.194(目的),通过容器A内的路由表匹配到应该将IP包发送到网关10.244.1.1(cni0网桥)。
此时的ICMP报文以太网帧格式
(2)到达cni0的IP包目的地IP 10.244.2.194,匹配到节点A上第一条路由规则(10.244.0.0),内核通过查本机路由表知道应该将RAW IP包发送给flannel0接口。
(3)flannel0 为tun设备,发送给flannel0接口的RAW IP包(无MAC信息)将被flanneld进程接收,flanneld进程接收RAW IP包后在原有的基础上进行UDP封包,UDP封包的形式为172.16.130.140:{系统管理的随机端口}--->172.16.130.164;8285。
注:172.16.130.164是10.244.2.194这个目的容器所在宿主机的IP地址,flanneld通过查询etcd很容易得到,8285是flanneld监听端口。
(4)flanneld将封装好的UDP报文经eth0发出,从这里可以看出网络包在通过eth0发出前先是加上UDP头(8个字节),再加上IP头(20个字节)进行封装,这也是flannel0的MTU要被eth0的MTU小28个字节的原因,即防止封包的以太网帧超过eth0的MTU,而在经过eth0时被丢弃。
此时,完整的封包后的ICMP以太网帧格式
(5)网络包经过主机网络从节点A到达节点B。
(6)主机B收到UDP报文后,Linux内核通过UDP端口号8285将包交给正在监听的flanneld。
(7)运行在hostB中的flanneld将UDP包解封包后得到RAW IP包:10.244.1.96--->10.244.2.194。
(8)解封包后的RAW IP包匹配到主机B上的路由规则(10.244.2.0),内核通过查本机路由表知道应该将RAW IP包发送到cni0网桥。
此时,完整的解封包后的以太网帧格式
(9)cni0网桥将IP包转发给连接在该网桥上的容器B,由docker0转发到目标容器,至此整个流程结束。回程报文将按上面的数据流原路返回。
flannel UDP 模式数据流简图
1.3、flannel VXLAN模式数据通信
(1)同UDP Backend模式,容器A中的IP包通过容器A内的路由表被发送到cni0。
(2)到达cni0中的IP包通过匹配hostA中的路由表发现通过10.244.2.194的IP包应该交给flannel1.1接口。
(3)flannel1.1作为一个VTEP设备,收到报文后将按照VTEP的配置进行封包。首先,通过etcd得知10.244.2.194属于节点B,并得到节点B的IP。然后,通过节点A中的转发表得到节点B对应的VTEP的MAC,根据flannel1.1设备创建时的设置参数(VNI、localIP、Port)进行VXLAN封包。
(4)通过host A 跟host B 之间的网络连接,VXLAN包到达hostB的eth1接口。
(5)通过端口8472,VXLAN包被转发给VTEP设备flannel1.1进行解包。
(6)解封装后的IP包匹配hostB中的路由表(10.244.2.0),内核将IP包转发给cni0。
(7)cni0将IP包转发给连接在cni0上的容器B。
flannel VXLAN模式工作原理
1.4、flannel Host-gw模式数据通信
(1)同UDP、VXLAN模式一致,通过容器A的路由表IP包到达cni0。
(2)到达cni0的IP包匹配到hostA中的路由规则(10.244.2.0),并且网关为172.16.130.164,即hostB,所以内核将IP包发送给hostB(172.16.130.164)。
(3)IP包通过物理网络到达hostB的eth1。
(4)到达hostB eth1的IP包匹配到hostB中的路由表(10.244.2.0),IP包被转发给cni0。
(5)cni0 将IP包转发给连接在cni0上的容器B。
二、flannel环境部署
#环境准备 192.168.247.81 k8s-flannel-master-01 kubeapi.flannel.com 2vcpu 4G 50G ubuntu20.04 192.168.247.84 k8s-flannel-node-01 2vcpu 4G 50G ubuntu20.04 192.168.247.85 k8s-flannel-node-02 2vcpu 4G 50G ubuntu20.04 #关闭swap swapoff -a sed -i 's@/swap.img@#/swap.img@g' /etc/fstab #关闭防火墙 ufw disable && ufw status #配置时间同步 apt install chrony -y cat > /etc/chrony/chrony.conf <<EOF server ntp.aliyun.com iburst stratumweight 0 driftfile /var/lib/chrony/drift rtcsync makestep 10 3 bindcmdaddress 127.0.0.1 bindcmdaddress ::1 keyfile /etc/chrony.keys commandkey 1 generatecommandkey logchange 0.5 logdir /var/log/chrony EOF systemctl restart chrony #安装并启用docker apt -y install apt-transport-https ca-certificates curl software-properties-common curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add - add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" apt update apt install docker-ce -y #配置docker加速 cat > /etc/docker/daemon.json <<EOF { "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com", "https://reg-mirror.qiniu.com", "https://registry.docker-cn.com", "https://a7h8080e.mirror.aliyuncs.com" ], "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "200m" }, "storage-driver": "overlay2" } EOF systemctl restart docker docker info #查看docker版本及加速是否生效 #安装cri-dockerd curl -LO https://github.com/Mirantis/cri-dockerd/releases/download/v0.2.6/cri-dockerd_0.2.6.3-0.ubuntu-focal_amd64.deb apt install ./cri-dockerd_0.2.6.3-0.ubuntu-focal_amd64.deb #安装kubelet、kubeadm、kubectl apt update && apt install -y apt-transport-https curl curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main EOF apt-get update apt install -y kubelet kubeadm kubectl systemctl enable kubelet #整合kubelet和cri-dockerd # vim /usr/lib/systemd/system/cri-docker.service # cat /usr/lib/systemd/system/cri-docker.service [Unit] Description=CRI Interface for Docker Application Container Engine Documentation=https://docs.mirantis.com After=network-online.target firewalld.service docker.service Wants=network-online.target Requires=cri-docker.socket [Service] Type=notify #ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --cni-bin-dir=/opt/cni/bin --cni-cache-dir=/var/lib/cni/cache --cni-conf-dir=/etc/cni/net.d ExecReload=/bin/kill -s HUP $MAINPID TimeoutSec=0 RestartSec=2 Restart=always StartLimitBurst=3 StartLimitInterval=60s LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TasksMax=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.target systemctl daemon-reload systemctl restart cri-docker #配置kubelet mkdir /etc/sysconfig/ vim /etc/sysconfig/kubelet #初始化master # kubeadm config images list #查看所需的容器镜像 registry.k8s.io/kube-apiserver:v1.25.3 registry.k8s.io/kube-controller-manager:v1.25.3 registry.k8s.io/kube-scheduler:v1.25.3 registry.k8s.io/kube-proxy:v1.25.3 registry.k8s.io/pause:3.8 registry.k8s.io/etcd:3.5.4-0 registry.k8s.io/coredns/coredns:v1.9.3
#拉取镜像 kubeadm config images pull --cri-socket unix:///run/cri-dockerd.sock #拉取镜像 #初始化集群 kubeadm init --control-plane-endpoint="kubeapi.flannel.com" \ --kubernetes-version=v1.25.3 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --token-ttl=0 \ --cri-socket unix:///run/cri-dockerd.sock \ --upload-certs
#master添加
kubeadm join kubeapi.flannel.com:6443 --token 9qajrn.br48bhnpqyhq593p \
--discovery-token-ca-cert-hash sha256:4b72b489daf2bb7052c67065339796f966a666de6068735babf510259c0717ca \
--control-plane --certificate-key 19db0bbf09af6973df6cd0ad93b974be028bbabcde41c714b5ecc3616487a776
#node添加 kubeadm join kubeapi.flannel.com:6443 --token 9qajrn.br48bhnpqyhq593p \ --discovery-token-ca-cert-hash sha256:4b72b489daf2bb7052c67065339796f966a666de6068735babf510259c0717ca \ --cri-socket unix:///run/cri-dockerd.sock [root@k8s-flannel-master-01 ~]# mkdir -p $HOME/.kube [root@k8s-flannel-master-01 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@k8s-flannel-master-01 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl get node -owide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-flannel-master-01 NotReady control-plane 13m v1.25.3 192.168.247.81 <none> Ubuntu 20.04.3 LTS 5.4.0-122-generic docker://20.10.21 k8s-flannel-node-01 NotReady <none> 83s v1.25.3 192.168.247.84 <none> Ubuntu 20.04.3 LTS 5.4.0-122-generic docker://20.10.21 k8s-flannel-node-02 NotReady <none> 79s v1.25.3 192.168.247.85 <none> Ubuntu 20.04.3 LTS 5.4.0-122-generic docker://20.10.21 [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-565d847f94-khn5v 0/1 Pending 0 128m kube-system coredns-565d847f94-xsvrp 0/1 Pending 0 128m kube-system etcd-k8s-flannel-master-01 1/1 Running 0 128m kube-system kube-apiserver-k8s-flannel-master-01 1/1 Running 0 128m kube-system kube-controller-manager-k8s-flannel-master-01 1/1 Running 0 128m kube-system kube-proxy-ccsp9 1/1 Running 0 115m kube-system kube-proxy-jlhdb 1/1 Running 0 128m kube-system kube-proxy-wdnjh 1/1 Running 0 115m kube-system kube-scheduler-k8s-flannel-master-01 1/1 Running 0 128m [root@k8s-flannel-master-01 ~]# #安装flannel mkdir /opt/bin/ curl -L https://github.com/flannel-io/flannel/releases/download/v0.20.1/flanneld-amd64 -o /opt/bin/flanneld chmod +x /opt/bin/flanneld #拷贝flannel安装文件 https://github.com/flannel-io/flannel/blob/master/Documentation/kube-flannel.yml [root@k8s-flannel-master-01 ~]# kubectl apply -f kube-flannel.yaml #安装flannel网络插件 namespace/kube-flannel created clusterrole.rbac.authorization.k8s.io/flannel created clusterrolebinding.rbac.authorization.k8s.io/flannel created serviceaccount/flannel created configmap/kube-flannel-cfg created daemonset.apps/kube-flannel-ds created [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl get node -owide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-flannel-master-01 Ready control-plane 158m v1.25.3 192.168.247.81 <none> Ubuntu 20.04.3 LTS 5.4.0-122-generic docker://20.10.21 k8s-flannel-node-01 Ready <none> 146m v1.25.3 192.168.247.84 <none> Ubuntu 20.04.3 LTS 5.4.0-122-generic docker://20.10.21 k8s-flannel-node-02 Ready <none> 146m v1.25.3 192.168.247.85 <none> Ubuntu 20.04.3 LTS 5.4.0-122-generic docker://20.10.21 [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-flannel kube-flannel-ds-5n4qv 1/1 Running 0 5m8s kube-flannel kube-flannel-ds-8b97k 1/1 Running 0 5m8s kube-flannel kube-flannel-ds-bbksr 1/1 Running 0 5m8s kube-system coredns-565d847f94-khn5v 1/1 Running 0 158m kube-system coredns-565d847f94-xsvrp 1/1 Running 0 158m kube-system etcd-k8s-flannel-master-01 1/1 Running 0 158m kube-system kube-apiserver-k8s-flannel-master-01 1/1 Running 0 158m kube-system kube-controller-manager-k8s-flannel-master-01 1/1 Running 0 158m kube-system kube-proxy-ccsp9 1/1 Running 0 146m kube-system kube-proxy-jlhdb 1/1 Running 0 158m kube-system kube-proxy-wdnjh 1/1 Running 0 146m kube-system kube-scheduler-k8s-flannel-master-01 1/1 Running 0 158m [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl create deployment nginx --image=nginx:1.23.2-alpine --replicas=3 deployment.apps/nginx created [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-5cd78c5d7-2nll4 1/1 Running 0 3m8s 10.244.1.2 k8s-flannel-node-01 <none> <none> nginx-5cd78c5d7-gb7f6 1/1 Running 0 3m8s 10.244.2.4 k8s-flannel-node-02 <none> <none> nginx-5cd78c5d7-hf6nr 1/1 Running 0 3m8s 10.244.1.3 k8s-flannel-node-01 <none> <none> [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# kubectl create service nodeport nginx --tcp=80:80 service/nginx created [root@k8s-flannel-master-01 ~]# kubectl get svc -l app=nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx NodePort 10.101.156.194 <none> 80:31818/TCP 13s [root@k8s-flannel-master-01 ~]# [root@k8s-flannel-master-01 ~]# curl 192.168.247.81:31818 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> [root@k8s-flannel-master-01 ~]#
三、Flannel Pod通信
3.1、Flannel Pod通信抓包
Flannel vxlan架构图
1、flannel通信流程
vxlan报文格式