flannel
是coreos
为kubernets
提供的网络解决方案
,主要为打通跨节点的容器通信
,其中vxlan
模式为flannel实现的一种后端模式,其他模式还包括udp, host-gw
等。VXLAN(Virtual eXtensible Local Area Network)是一种隧道技术
,能在三层网络的基础上建立二层以太网网络隧道
,从而实现跨地域的二层互连。
VXLAN采取了将原始以太网报文封装在UDP数据包
里的封装格式。将原来的二层数据帧加上VXLAN头部一起封装在一个UDP数据包里。
VXLAN头部包含有一个VXLAN标识(即VNI,VXLAN Network Identifier),只有在同一个VXLAN上的虚拟机之间才能相互通信。VNI在数据包之中占24比特,故可支持1600万
个VXLAN的同时存在,远多于VLAN的4094
个,因此可适应大规模租户的部署。
VXLAN一般通过安装在服务器上的软件实现报文的封装与解封装,网络只要IP路由可达即可。VXLAN实现了应用与物理网络的解耦,但网络与虚拟机还是相互独立的。业界一般通过网络控制器(如SDN,Software Defined Network)实现VXLAN网络与云业务的联动。当虚拟机发生迁移后,虚机/存储控制器会把虚拟机迁移信息通知给网络控制器,网络控制器根据虚拟机迁移的新位置,重新调整网络配置,从而实现网络与云业务的联动。也就是说,物理网络可以是传统的三层IP网络,路由可达即可。虚拟机可跨三层IP网络远距离迁移,不再受限于二层技术。物理网络也无需允许所有VLAN通过。接入交换机需要学习的MAC地址的数量也大大减少,削弱了网络设备MAC地址表项规格对虚拟机规模的约束。
VXLAN网络设备主要有三种角色,分别是VTEP(VXLAN Tunnel EndPoint)、VXLAN网关、VXLANIP网关。
node1的容器1通过cni0
的网关接口,通过flannel的vxlan
模式进行封装
,通过eth0网卡
出去,进行udp传输
,里面搭载
了vxlan的封装协议数据包
,传输给node2,到达node2后,内核
进行识别解封,然后node2的flannel
设备再次进行解封,在到达cni0
,这样就相当于已经到达了集群的内部了,然后把信息注入到node2的容器2。
结点在启动的时候,会启动flanneld服务,查询k8s的apiserver,从etcd获取分配子网的信息,并向etcd进行注册:
[root@server3 ~]# cd /run/flannel/
[root@server3 flannel]# cat subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
[root@server3 flannel]# ps ax |grep flannel
6852 ? Ssl 0:02 /opt/bin/flanneld --ip-masq --kube-subnet-mgr
17554 pts/0 S+ 0:00 grep --color=auto flannel
ip a
6: flannel.1: mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 26:c1:ab:a2:5d:20 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1 //就是flannel的ip地址
valid_lft forever preferred_lft forever
inet6 fe80::24c1:abff:fea2:5d20/64 scope link
[root@server2 manifest]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-example-846496db9d-6jvqv 1/1 Running 0 65m 10.244.2.69 server4 <none> <none>
deployment-example-846496db9d-xbqwz 1/1 Running 0 65m 10.244.1.93 server3 <none> <none>
当前我们server3和server4各有一个pod,他们的信息都保存在master主机的etcd
中。
他们是属于不同网段的.他们要通讯的话是跨主机的。
[root@server3 flannel]# ip route
default via 172.25.254.67 dev ens3
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 *
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink *
169.254.0.0/16 dev ens3 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.25.254.0/24 dev ens3 proto kernel scope link src 172.25.254.3
我们可以看出要访问10.244.1.0/24这个网段要走cni0的网卡,数据包先到达cni0网卡上,在从cni0网卡路由出去:
[root@server3 flannel]# brctl show
bridge name bridge id STP enabled interfaces
cni0 8000.ee2a6f7b5ebe no veth95f9a72c
docker0 8000.024255b8b349 no
可以看出一端在pod中,一端在cni0网卡上,他们通过虚拟网络进行通讯
当我们想访问10.244.2.0/24网段是需要走10.244.2.0这个网关,使用flannel.1这个设备。这就是为什么从cni0出来到达flannel设备。10.244.2.0正是server4上的flannel.1这个设备:
[root@server4 flannel]# ip a
6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether f6:5f:84:9e:d8:36 brd ff:ff:ff:ff:ff:ff
inet 10.244.2.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::f45f:84ff:fe9e:d836/64 scope link
然后server3的flannel开始封装VNI
,它有源地址和目的地地址组成。
[root@server3 flannel]# ip neigh
10.244.1.93 dev cni0 lladdr 12:d0:2d:d9:49:89 STALE
10.244.2.0 dev flannel.1 lladdr f6:5f:84:9e:d8:36 PERMANENT
源地址:10.244.1.93 mac:12:d0:2d:d9:49:89
目的地址:10.244.2.69 mac: f6:5f:84:9e:d8:36
由于目前server3上还不能知道 10.244.2.69 的mac地址,所以会把 10.244.2.0 网关的mac地址加上,这个对应的是server4上 flannel.1 的 mac 地址。
通过 eth0 走 udp 协议,我们才可以到达 node2 主机,所以我们还要获取 node2 的 eth0 的mac地址。这时我们flannel封装的信息会在eth0封装的信息的内部。
[root@server3 flannel]# bridge fdb show
。。。
f6:5f:84:9e:d8:36 dev flannel.1 dst 172.25.254.4 self permanent
。。。
告诉我们要访问 f6:5f:84:9e:d8:36 需要发送到 172.25.254.4 上,它的mac地址是:
[root@server3 flannel]# arp -an
? (10.244.1.91) at ca:66:fa:19:85:83 [ether] on cni0
? (172.25.254.1) at 52:54:00:65:28:86 [ether] on ens3
? (172.25.254.2) at 52:54:00:df:b5:2f [ether] on ens3
? (169.254.169.254) at <incomplete> on ens3
? (10.244.2.0) at f6:5f:84:9e:d8:36 [ether] PERM on flannel.1
? (172.25.254.4) at 52:54:00:c4:e4:e5 [ether] on ens3 //这里
src: 172.25.254.3 mac:52:54:00:fa:bc:3a
dst: 172.25.254.4 mac:52:54:00:c4:e4:e5
然后把flannel的数据包在封装进来,就是下面这样:
src: 172.25.254.3 mac:52:54:00:fa:bc:3a
dst: 172.25.254.4 mac:52:54:00:c4:e4:e5
源地址:10.244.1.93 mac:12:d0:2d:d9:49:89 目的地址:10.244.2.69 mac: f6:5f:84:9e:d8:36
然后server4上内核进行识别,flannel再进行解封,在通过cni0就可以访问到pod内部了。
这就是为什么在访问svc的时候有时会卡顿,就是因为访问时调度到的后端结点有时需要跨主机,所以才会卡顿。
flannel中包含了几种backend,vxlan只是其中的一种,其中还有udp的方式,以及另一种在二层的方式host-gw。
host-gw 不会封装数据包,而是在主机的路由表中创建到其他主机 subnet 的路由条目,从而实现容器跨主机通信。
[root@server2 manifest]# kubectl delete -f kube-flannel.yml
//删除之前的组件
[root@server2 manifest]# vim kube-flannel.yml
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw" //把类型改为host-gw
}
[root@server2 manifest]# kubectl apply -f kube-flannel.yml //应用
[root@server2 manifest]# kubectl get pod -n kube-system
kube-flannel-ds-amd64-7jrft 1/1 Running 0 2m44s
kube-flannel-ds-amd64-dq4j6 1/1 Running 0 2m44s
kube-flannel-ds-amd64-pkjnn 1/1 Running 0 2m44s
然后我们就可以发现flannel.1这个隧道网卡就不见了因为我们现在用不到这个设备了,直接走主机的网关。
要求在二层上必须是相通的。但在大型的私有云上不只有一个网络段,就不适用了。
[root@server1 harbor]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@server1 harbor]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@server1 harbor]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@server1 harbor]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
就一点也不卡顿了。
[root@server3 flannel]# ip route
default via 172.25.254.67 dev ens3
10.244.0.0/24 via 172.25.254.2 dev ens3
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
10.244.2.0/24 via 172.25.254.4 dev ens3 //直接转发到254.4上,没有像vxlan一样走flannel。
169.254.0.0/16 dev ens3 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.25.254.0/24 dev ens3 proto kernel scope link src 172.25.254.3
udp方式存在的弊端太多了,就不再介绍。
flannel主要是负责网络通信的,不具网络备策略功能,而k8s的另一种网络插件 calico 则在flannel的基础上可以定制网络策略,在每个结点上加一个路由器。功能更强。
对于二层网段相同的可以采用 直连路由的方式,相当于host-gw 的方式,对于不同的则采用 flannel 的方式。
[root@server2 manifest]# vim kube-flannel.yml
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true //直连路由
[root@server2 manifest]# kubectl apply -f kube-flannel.yml
[root@server3 flannel]# ip a
6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 26:c1:ab:a2:5d:20 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1 //隧道设备又出来了,不通网络段走这里
valid_lft forever preferred_lft forever
inet6 fe80::24c1:abff:fea2:5d20/64 scope link
[root@server3 flannel]# ip iroute
Object "iroute" is unknown, try "ip help".
[root@server3 flannel]# ip route
default via 172.25.254.67 dev ens3
10.244.0.0/24 via 172.25.254.2 dev ens3
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
10.244.2.0/24 via 172.25.254.4 dev ens3 //但是网关依然在这里,同段走这里
169.254.0.0/16 dev ens3 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.25.254.0/24 dev ens3 proto kernel scope link src 172.25.254.3
[root@rhel7host ~]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@rhel7host ~]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@rhel7host ~]# curl 172.25.254.100
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@rhel7host ~]#
访问依然很顺畅。