跨vpc的pod网络通信-flannel

部署前的准备

  • 需要保证每个节点都有自己独立的公网IP
  • kubeadm将apiServer服务暴露至公网
  • 由于Node节点加入集群的时候被登记的INTERNAL-IP是节点内网IP,这样Master节点在和Node节点通信的时候会直接访问Node的内网IP,所以需要做iptables的NAT转发。

flannel

Flannel是CoreOS维护的一个网络组件,Flannel为每个Pod提供全局唯一的IP,Flannel使用ETCD来存储Pod子网与Node IP之间的关系。flanneld守护进程在每台主机上运行,并负责维护ETCD信息和路由数据包。
其实k8s网络组件flannel和calico主要解决的问题是k8s节点之间容器网络的通信,flannel要保证每个pod的IP是唯一的,怎么保证是唯一的,大部分组件的做法是在每个Node上分配一个唯一的子网,node1是一个单独的子网,node2是一个单独的子网,可以理解是不同网段,不同vlan,所以每个节点都是一个子网,所以flannel会预先设置一个大的子网,然后在这个每个node上分配子网,这些信息都会由flannel存储到etcd中,并且每个子网绑定到node上都有关系记录的,然后方便下次进行二次的数据包传输,并且flannel在node上会启动一个守护进程并运行,守护进程主要维护的是本地的路由规则,和维护etcd中的信息。

flannel的部署

master通过kubectl logs和describe命令调用Node上的某一Pod的时候,往往会出现timeout的情况,这是由于Master和Node之间不能直接通信,而Master调用Node是根据Node的网卡地址的IP,这个IP是内网IP,因此需要做Iptables的NAT转发

1
iptables -t nat -I OUTPUT -d [node内网IP] -j DNAT --to [node公网IP]

修改k8s的public ip 以保证flannel可以在节点之间通信

1
kubectl annotate node vm-20-9-centos flannel.alpha.coreos.com/public-ip-overwrite=[public-ip]
1
2
3
4
5
安装flannel时需要将残留的calico文件删除
/etc/cni/net.d

wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubectl create -f kube-flannel.yml

flannel会以daemonset的形式在每个node上启动一个pod,来启动一个flannel的守护进程,主要负责本机路由表的设定和etcd中的数据,本地的子网上报到etcd中,所以守护进程是非常重要的 可以在flannel的配置文件去设置大子网和模式

1
2
3
4
5
6
7
8
net-conf.json: |
{
"Network": "10.244.0.0/16",
"EnableNFTables": false,
"Backend": {
"Type": "vxlan"
}
}

部署完成之后该配置会放在/etc/cni/net.d目录下,由于flannel是使用网桥的模式,实现的同节点数据包到达宿主机这个的通信,所以子网信息并没写到这个配置文件里,而是放到了这个 /var/run/flannel/subnet.env 下,通过ip a也能看到设备分配的ip,每个节点都会分配一个子网,网络接口设备为cni0,也就是一个node上可以分配255个小的子网

1
2
3
4
5
[root@iZ2zeiagx6ykthgr48snf2Z ~]# cat /var/run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.9.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

cni的二进制文件, /opt/cni/bin,kubelet调用这个二进制接口为创建的每个pod创建网络信息,并且是从我们的配置的子网中去拿IP

预先设定它的子网,以及工作模式,网络不能与k8s本身的内网冲突,否则导致网络不通的状况

Flannel工作模式及原理

Flannel支持多种数据转发方式:

UDP:最早支持的一种方式,由于性能最差,目前已经弃用。

VXLAN:Overlay Network方案,源数据包封装在另一种网络包里面进行路由转发和通信 这也是网络的虚拟化技术,也就是原来是有一个包数据包,有源IP和目的IP,但由于某些情况这个数据包到达不了目的地址上,这可能就会借助物理上的以太网网络进行封装一个数据包带上,然后通过这种物理网络传输到目的地址上,这是一种叠加式的网络,里面是有两种数据包,也叫做隧道方案

Host-GW:Flannel通过在各个节点上的Agent进程,将容器网络的路由信息刷到主机的路由表上,这样一来所有的主机都有整个容器网络的路由数据了,这样它就知道这个数据包到达这个节点转发到这个机器上,也就是路由表之间转发的,也叫路由方案

VXLAN

cat /etc/kubernetes/manifests/kube-controller-manager.yaml
......
--allocate-node-cidrs=true   #允许node自动分配cidr网络
--cluster-cidr=10.244.0.0/16  #指定pod网络的网段,网段要和flannel的网段对应上

这样就能以cni的标准来为k8s配置网络

1
2
3
4
5
6
7
8
net-conf.json: |
{
"Network": "10.244.0.0/16",
"EnableNFTables": false,
"Backend": {
"Type": "vxlan"
}
}

flannel保证每个node都是唯一的ip,它是在每个node上都分配一个子网
可以看到flannel是基于宿主机创建的,它会为每个node创建独立的子网,并为当前pod分配ip

1
2
3
4
5
[root@VM-20-9-centos ~]# kubectl get pod -n kube-flannel -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-flannel-ds-22hdg 1/1 Running 0 73m 82.156.79.118 vm-20-9-centos
kube-flannel-ds-v9wrz 1/1 Running 0 73m 82.156.200.82 vm-20-12-centos
kube-flannel-ds-xjr2c 1/1 Running 0 73m 60.205.255.226 iz2zeiagx6ykthgr48snf2z

Pod之间互相访问

  1. flanneld将本主机获取的subnet以及用于主机间通信的Public IP通过etcd存储起来,需要时发送给相应模块。
  2. flannel利用各种backend mechanism,例如udp,vxlan等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。

Cni0:网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡)。Pod中从网卡eth0发出的流量都会发送到Cni0网桥设备的端口(网卡)上。Cni0 设备获得的ip地址是该节点分配到的网段的第一个地址。

Flannel.1: overlay网络的设备,用来进行 vxlan 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。

Flanneld:flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac,ip等网络数据信息。

flannel查找到目标pod所在节点是通过查询etcd的node信息维护一张fdb表,可以通过下面命令查看

1
2
3
4
5
[root@VM-20-9-centos ~]# ip neigh show dev flannel.1
10.244.13.0 lladdr be:a3:4b:69:cc:f4 PERMANENT
10.244.4.0 lladdr 0a:07:f4:60:c4:49 PERMANENT
10.244.9.0 lladdr 4a:53:58:07:b1:6a PERMANENT
10.244.1.0 lladdr 62:b4:4f:01:a6:15 PERMANENT