Kubernetes进阶 -- Flannel网络插件vxlan,host-gw,udp

  • flannelcoreoskubernets提供的网络解决方案,主要为打通跨节点的容器通信,其中vxlan模式为flannel实现的一种后端模式,其他模式还包括udp, host-gw等。

vxlan

简述

  • VxLAN(Virtual eXtensible Local Area Network,虚拟扩展局域网)技术很好地解决了现有VLAN技术无法满足大二层网络需求的问题。VxLAN技术是一种大二层的虚拟网络技术,主要原理是引入一个UDP格式的外层隧道作为数据链路层,而原有数据报文内容作为隧道净荷加以传输。由于外层采用了UDP作为传输手段,净荷数据可以轻松地在二三层网络中传送。VxLAN已成为业界主流的虚拟网络技术之一,IETF正在制定相关标准。VxLAN技术的特点在于如下几个方面。
  • VXLAN主要解决现阶段大规模云计算数据中心虚拟网络不足的问题。VMware ESXi、Open vSwitch、当前主流的网络芯片均已支持VXLAN:它备受业界关注,未来有可能成为网络虚拟化技术当中的主流技术之一,如此看来,它的发展和应用前景还是值得期待的。

原理

  • 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网关。

    • VTEP是直接与终端连接的设备,负责原始以太报文的VXLAN封装和解封装。
    • VXLAN网关除了具备VTEP的功能外,还负责VLAN报文与VXLAN报文之间的映射和转发。VXLAN的虚拟机与传统VLAN的虚拟机之间互访,通过VXLAN网关来完成。
    • VXLANIP网关具有VXLAN网关的所有功能,此外,还负责处理不同VXLAN之间的报文通信。不同VXLAN的虚拟机之间需要互访,必须经过VXLANIP网关完成。

Kubernetes进阶 -- Flannel网络插件vxlan,host-gw,udp_第1张图片
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 

flannel封装VNI

然后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封装

通过 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的时候有时会卡顿,就是因为访问时调度到的后端结点有时需要跨主机,所以才会卡顿。

host-gw

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 ~]# 
访问依然很顺畅。

你可能感兴趣的:(k8s)