k8s-flannel
简介
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。
在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相互ping通。
Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
Flannel实质上是一种“覆盖网络(overlaynetwork)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等数据转发方式,默认的节点间数据通信方式是UDP转发。
简单总结Flannel的特点
使集群中的不同Node主机创建的Docker容器都具有全集群唯一的虚拟IP地址。
建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖网络是建立在另一个网络之上并由其基础设施支持的虚拟网络。覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础设施分离。在将封装的数据包转发到端点后,将其解封装。
创建一个新的虚拟网卡flannel0接收docker网桥的数据,通过维护路由表,对接收到的数据进行封包和转发(vxlan)。
etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化。
Flannel对网络提出的解决方法
互不冲突的IP
flannel会给集群内每个pod分配一个全局唯一的IP地址;
flannel会给集群内node节点分配不同的小子网,pod都运行在这个网段,并且作为pod内容器的网关
当前node主机IP地址范围
Mast节点 ]# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.244.0.0/16 //flannel网段 FLANNEL_SUBNET=10.244.0.1/24 //当前node节点的子网,每个node节点的子网都不一样,node的pod都运行在这个网段 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true Node1节点 ]# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.244.0.0/16 FLANNEL_SUBNET=10.244.1.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true Node2节点 ]# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.244.0.0/16 FLANNEL_SUBNET=10.244.2.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true
node节点的子网网段为pod的网关
]# ifconfig //pod被调度到node1节点 cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.1.1 netmask 255.255.255.0 broadcast 10.244.1.255 inet6 fe80::fc02:90ff:fed0:5fa4 prefixlen 64 scopeid 0x20<link> ether fe:02:90:d0:5f:a4 txqueuelen 1000 (Ethernet) RX packets 573 bytes 50081 (48.9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 643 bytes 163362 (159.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 查看pod内容器网关 ]# kubectl exec -it busybox -n linux40 sh / # route -n //可以看到网关地址为cni0的地址 Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.244.1.1 0.0.0.0 UG 0 0 0 eth0 10.244.0.0 10.244.1.1 255.255.0.0 UG 0 0 0 eth0 10.244.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 查看网桥设备 ]#yum -y install bridge-utils.x86_64 ]# brctl show //查看网桥设备,接收pod报文信息并发送给flannel.1,flannel.1封装发送给eth0网卡,eth0再次封装转发 bridge name bridge id STP enabled interfaces cni0 8000.eae8be1e6915 no veth1d9832ad //容器与网桥设备绑定的接口 vethb5f67d2f //可以在宿主机使用ifconfig查看容器虚拟网卡 docker0 8000.0242acd62831 no
pod之间互相访问
1. flanneld将本主机获取的subnet以及用于主机间通信的Public IP通过etcd存储起来,需要时发送给相应模块。
2. flannel利用各种backend mechanism,例如udp,vxlan等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。
flannel实现pod通信
- pod内部通信 pod内多个容器通信
- pod跨主机通信 pod与pod通信 同一个node节点 不同node节点
- pod到公网通信 访问宿主机的同子网的其他服务(mysql、redis等) 访问宿主机外网
- 外网用户访问集群pod
flannel网络模型,容器如何实现跨主机通信
从上面通信过程中可以看到,pod实现跨主机通信,数据必须经过两次封装与解封装,性能与效率会比较差。
Flannel目前有三种方式实现 UDP/VXLAN/host-gw:
UDP:早期版本的Flannel使用UDP封装完成报文的跨越主机转发,其安全性及性能略有不足。
VXLAN:Linux 内核在在2012年底的v3.7.0之后加入了VXLAN协议支持,因此新版本的Flannel也有UDP转换为
VXLAN,VXLAN本质上是一种tunnel(隧道)协议,用来基于3层网络实现虚拟的2层网络,目前flannel 的网络模型已经是基于VXLAN的叠加(覆盖)网络。
Host-gw:也就是Host GateWay,通过在node节点上创建到达各目标容器地址的路由表而完成报文的转发,因此这种方式要求各node节点本身必须处于同一个局域网(二层网络)中,因此不适用于网络变动频繁或比较大型的网络环境,但是其性能较好。
各个组件的解释:
Cni0:网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡)。Pod中从网卡eth0发出的流量都会发送到Cni0网桥设备的端口(网卡)上。
Cni0 设备获得的ip地址是该节点分配到的网段的第一个地址。
]# ifconfig cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.0.1 netmask 255.255.255.0 broadcast 10.244.0.255 inet6 fe80::e8e8:beff:fe1e:6915 prefixlen 64 scopeid 0x20<link> ether ea:e8:be:1e:69:15 txqueuelen 1000 (Ethernet) RX packets 17058 bytes 1160744 (1.1 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 17428 bytes 5210849 (4.9 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Flannel.1: overlay网络的设备,用来进行 vxlan 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.0.0 netmask 255.255.255.255 broadcast 10.244.0.0 inet6 fe80::d475:83ff:fe2c:ff80 prefixlen 64 scopeid 0x20<link> ether d6:75:83:2c:ff:80 txqueuelen 0 (Ethernet) RX packets 4467 bytes 1251194 (1.1 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 4567 bytes 407173 (397.6 KiB) TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0
Flanneld:flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库etcd,为flannel.1设备提供封装数据时必要的mac,ip等网络数据信息。
不同node上的pod的通信流程:
1. pod中产生数据,根据pod的路由信息,将数据发送到Cni0
2. Cni0 根据节点的路由表,将数据发送到隧道设备flannel.1
3. Flannel.1查看数据包的目的ip,从flanneld获得对端隧道设备的必要信息,封装数据包。
4. Flannel.1将数据包发送到对端设备。对端节点的网卡接收到数据包,发现数据包为overlay数据包,解开外层封装,并发送内层封装到flannel.1设备
5. Flannel.1设备查看数据包,根据路由表匹配,将数据发送给Cni0设备。
6. Cni0匹配路由表,发送数据给网桥上对应的端口。
vxlan模式
]# kubectl exec -it -n linux40 busybox sh / # route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.244.1.1 0.0.0.0 UG 0 0 0 eth0 10.244.0.0 10.244.1.1 255.255.0.0 UG 0 0 0 eth0 10.244.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 # 可以看到路由为pod—cni0—对端Flannel—pod / # traceroute 10.244.2.37 traceroute to 10.244.2.37 (10.244.2.37), 30 hops max, 46 byte packets 1 10.244.1.1 (10.244.1.1) 0.006 ms 0.004 ms 0.002 ms 2 10.244.2.0 (10.244.2.0) 0.278 ms 0.847 ms 0.530 ms 3 10-244-2-37.ng-deploy-80.linux40.svc.cluster.local (10.244.2.37) 0.780 ms 0.649 ms 0.743 ms
开启VxLAN Directrouting模式
需要让配置文件在node节点重新生效
root@k8s-master1:/etc/ansible# vim roles/flannel/templates/kube-flannel.yaml.j2 net-conf.json: | { "Network": "{{ CLUSTER_CIDR }}", "Backend": { "Type": "{{ FLANNEL_BACKEND }}", "Directrouting": true //开启直连路由模式 }
改之前的路由表
root@k8s-node2:~# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.7.254 0.0.0.0 UG 0 0 0 eth0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.31.0.0 172.31.0.0 255.255.255.0 UG 0 0 0 flannel.1 172.31.1.0 172.31.1.0 255.255.255.0 UG 0 0 0 flannel.1 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 172.31.3.0 172.31.3.0 255.255.255.0 UG 0 0 0 flannel.1 192.168.0.0 0.0.0.0 255.255.248.0 U 0 0 0 eth0
改之后的路由表
root@k8s-master1:/etc/ansible# ansible-playbook 06.network.yml root@k8s-node1:~# reboot root@k8s-node2:~# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.7.254 0.0.0.0 UG 0 0 0 eth0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.31.0.0 192.168.7.102 255.255.255.0 UG 0 0 0 eth0 172.31.1.0 192.168.7.101 255.255.255.0 UG 0 0 0 eth0 172.31.2.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 172.31.3.0 192.168.7.110 255.255.255.0 UG 0 0 0 eth0 192.168.0.0 0.0.0.0 255.255.248.0 U 0 0 0 eth0
验证修改后的路由效果
改之前的路由效果
ifconfig eth0 //容器网卡 eth0 Link encap:Ethernet HWaddr BE:EE:B9:F7:62:47 inet addr:172.31.3.4 Bcast:0.0.0.0 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1 RX packets:48 errors:0 dropped:0 overruns:0 frame:0 TX packets:35 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:3700 (3.6 KiB) TX bytes:2286 (2.2 KiB) traceroute 172.31.2.4 traceroute to 172.31.2.4 (172.31.2.4), 30 hops max, 46 byte packets 1 172.31.3.1 (172.31.3.1) 0.049 ms 0.012 ms 0.007 ms //发给cni0 2 172.31.2.0 (172.31.2.0) 0.520 ms 0.639 ms 4.061 ms //发给对端flannel 3 172.31.2.4 (172.31.2.4) 3.544 ms 0.587 ms 0.464 ms //flannel发给pod
改之后的路由效果
ifconfig eth0 //容器网卡 eth0 Link encap:Ethernet HWaddr 5E:83:2C:DD:66:D3 inet addr:172.31.3.2 Bcast:0.0.0.0 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1 RX packets:37 errors:0 dropped:0 overruns:0 frame:0 TX packets:18 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2824 (2.7 KiB) TX bytes:1170 (1.1 KiB) traceroute 172.31.2.2 traceroute to 172.31.2.2 (172.31.2.2), 30 hops max, 46 byte packets 1 172.31.3.1 (172.31.3.1) 0.007 ms 0.018 ms 0.010 ms //本机cni0网卡 2 192.168.7.111 (192.168.7.111) 1.174 ms 0.244 ms 2.292 ms //对端宿主机IP地址 3 172.31.2.2 (172.31.2.2) 2.527 ms 0.549 ms 0.456 ms //宿主机发给pod
hostgw
hostgw是最简单的backend,它的原理非常简单,直接添加路由,将目的主机当做网关,直接路由原始封包。
例如,我们从etcd中监听到一个EventAdded事件subnet为10.1.15.0/24被分配给主机Public IP 192.168.0.100,hostgw要做的工作就是在本主机上添加一条目的地址为10.1.15.0/24,网关地址为192.168.0.100,输出设备为上文中选择的集群间交互的网卡即可。
优点:简单,直接,效率高
缺点:要求所有的pod都在一个子网中,如果跨网段就无法通信。