flannel
是coreos
爲kubernets
提供的網絡解決方案
,主要爲打通跨節點的容器通信
,其中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網關完成。
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: <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 //就是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 ~]#
訪問依然很順暢。