Flannel中vxlan backend的原理和實現

這次分享介紹Flannel中的vxlan backend,包含兩方面內容:

?????? 深入理解內核中的VXLAN原理:使用iproute2和bridge等原生工具來搭建一個基于VXLAN的Overlay網絡。

?????? 理解Flannel使用vxlan backend時的工作原理: 有了前面對內核VXLAN原理的理解,通過分析Flannel部分源碼來從根本上掌握其vxlan backend的原理。

一、VXLAN的原理

? ? ? ? Virtual eXtensible Local Area Network(VXLAN)是一個在已有的3層物理網絡上構建2層邏輯網絡的協議。

?????? 在2012年底的v3.7.0之后,Linux Kernel加入了VXLAN協議支持,作者:Stephen Hemminger,所以如果要使用Linux Kernel中的VXLAN支持,最低內核版本3.7+(推薦3.9+)。

?????? Stephen Hemminger同時也實現了iproute2、bridge等工具,用以管理Linux中復雜的網絡配置,目前在絕大多數Linux發行版中都是默認支持的。

?????? VXLAN本質上是一種tunnel(隧道)協議,用來基于3層網絡實現虛擬的2層網絡。泛泛地說,tunnel協議有點像今天電話會議,通過可視電話連接不同的會議室讓每個人能夠直接交談,就好像坐在一個會議室里一樣。很多tunnel協議,如GRE也有類似VXLAN中VNI的用法。

????? tunnel協議的另外一個重要的特性就是軟件擴展性,是軟件定義網絡(Software-defined Network,SDN)的基石之一。

????? Flannel中有兩個基于tunnel協議的backend:UDP(默認實現)和VXLAN,本質上都是tunnel協議,區別僅僅在于協議本身和實現方式。

?????? 這里順便提一句:tunnel協議在比較老的內核中已經有支持,我印象中v2.2+就可以使用tunnel來創建虛擬網絡了,因此UDP backend適合在沒有vxlan支持的linux版本中使用,但性能會相比vxlan backend差一些。

?以上是一些背景介紹,下面開始介紹VXLAN的內核支持

圖1. VXLAN可以在分布多個網段的主機間構建2層虛擬網絡

圖2. VXLAN基本原理:套路還是tunnel那一套,區別僅僅在于tunnel協議本身的實現

?????? 為了說明圖1和圖2中談到的VXLAN原理,這里在兩臺不同網段的VPS上手動搭建一個Overlay Network,并在兩個節點上分別運行了Docker Container,當我們看到容器之間使用虛擬網絡的IP完成直接通信時,實驗就成功了。

圖3 手動搭建vxlan虛擬網絡的網絡拓撲

圖3中提到一個VTEP的概念,全稱VXLAN Tunnel Endpoint,本質上就是前面提到的tunnel中的endpoint

現在正式開始手動搭建圖 3中的虛擬網絡

第一步、創建docker bridge

?????? 默認的docker bridge地址范圍是172.17.0.1/24(比較老的版本是172.17.42.1/24), 而本實驗中兩個節點node1和node2的子網要求分別為: 192.1.78.1/24,192.1.87.1/24 。

????? 修改docker daemon啟動參數,增加以下參數后重啟docker daemon:

node1: --bip=192.1.78.1/24

node2: --bip=192.1.87.1/24

?????? 這時node1和node2的容器之間還不能直接通信, node1也不能跨主機和node2上的容器直接通信,反之node2也無法直接和node1上的容器通信.。

第二步、創建VTEPs

在node1上執行以下命令:

PREFIX=vxlan

IP=$external-ip-of-node-1

DESTIP=$external-ip-of-node-2

PORT=8579

VNI=1

SUBNETID=78

SUBNET=192.$VNI.0.0/16

VXSUBNET=192.$VNI.$SUBNETID.0/32

DEVNAME=$PREFIX.$VNI

ip link delete $DEVNAME

ip link add $DEVNAME type vxlan id $VNI dev eth0 local $IP dstport $PORT nolearning

echo '3' > /proc/sys/net/ipv4/neigh/$DEVNAME/app_solicit

ip address add $VXSUBNET dev $DEVNAME

ip link set $DEVNAME up

ip route delete $SUBNET dev $DEVNAME scope global

ip route add $SUBNET dev $DEVNAME scope global

node2上執行以下命令:

PREFIX=vxlan

IP=$external-ip-of-node-2

DESTIP=$external-ip-of-node-1

VNI=1

SUBNETID=87

PORT=8579

SUBNET=192.$VNI.0.0/16

VXSUBNET=192.$VNI.$SUBNETID.0/32

DEVNAME=$PREFIX.$VNI

ip link delete $DEVNAME

ip link add $DEVNAME type vxlan id $VNI dev eth0 local $IP dstport $PORT nolearning

echo '3' > /proc/sys/net/ipv4/neigh/$DEVNAME/app_solicit

ip -d link show

ip addr add $VXSUBNET dev $DEVNAME

ip link set $DEVNAME up

ip route delete $SUBNET dev $DEVNAME scope global

ip route add $SUBNET dev $DEVNAME scope global

第三步、為VTEP配置forward table

# node1

$ bridge fdb add $mac-of-vtep-on-node-2 dev $DEVNAME dst $DESTIP

#?node2

$ bridge fdb add $mac-of-vtep-on-node-1 dev $DEVNAME dst $DESTIP

第四步、配置Neighbors,IPv4中為ARP Table

# node1

$ ip neighbor add $ip-on-node-2 lladdr $mac-of-vtep-on-node-2 dev vxlan.1

# node2

$ ip neighbor add $ip-on-node-1 lladdr $mac-of-vtep-on-node-1 dev vxlan.1

????? 注意:ARP表一般不會手動更新,在VXLAN的實現中由對應的Network Agent監聽L3 MISS來 動態更新;這里手動添加ARP entry僅僅是為了測試;另外,如果跨主機訪問多個IP, 每個跨主機的IP就都需要配置對應的ARP entry。

????? 以上操作都需要root權限,完成后整個Overlay Network就搭建成功了,下面通過測試兩種連通性來總結本實驗:

node1容器與node2上的容器直接通信(容器與跨主機容器間直接通信)

node1與node2上容器直接通信;node2與node1上容器直接通信(主機和跨主機容器之間通信)

????? 先看容器與跨主機容器間直接通信的測試。

現在node1和node2上分別起一個busybox:

node1$ docker run -it --rm busybox sh

node1$ ip a

1: lo: mtu 65536 qdisc noqueue qlen 1

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

inet 127.0.0.1/8 scope host lo

? valid_lft forever preferred_lft forever

inet6 ::1/128 scope host

? valid_lft forever preferred_lft forever

6: eth0@if7: mtu 1500 qdisc noqueue

link/ether 02:42:c0:01:4e:02 brd ff:ff:ff:ff:ff:ff

inet 192.1.78.2/24 scope global eth0

? valid_lft forever preferred_lft forever

inet6 fe80::42:c0ff:fe01:4e02/64 scope link

? valid_lft forever preferred_lft forever

node2$ docker run -it --rm busybox sh

node2$ ip a

1: lo: mtu 65536 qdisc noqueue qlen 1

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

inet 127.0.0.1/8 scope host lo

? valid_lft forever preferred_lft forever

inet6 ::1/128 scope host

? valid_lft forever preferred_lft forever

10: eth0@if11: mtu 1500 qdisc noqueue

link/ether 02:42:c0:01:57:02 brd ff:ff:ff:ff:ff:ff

inet 192.1.87.2/24 scope global eth0

? valid_lft forever preferred_lft forever

inet6 fe80::42:c0ff:fe01:5702/64 scope link

? valid_lft forever preferred_lft forever

接下來讓我們來享受一下容器之間的連通性。

node1@busybox$ ping -c1 192.1.87.2

PING 192.1.87.2 (192.1.87.2): 56 data bytes

64 bytes from 192.1.87.2: seq=0 ttl=62 time=2.002 ms

node2@busybox$ ping -c1 192.1.78.2

PING 192.1.78.2 (192.1.78.2): 56 data bytes

64 bytes from 192.1.78.2: seq=0 ttl=62 time=1.360 ms

然后看主機和跨主機容器之間連通性的測試。

node1$ ping -c1 192.1.87.2

PING 192.1.87.2 (192.1.87.2) 56(84) bytes of data.

64 bytes from 192.1.87.2: icmp_seq=1 ttl=63 time=1.49 ms

node2$ ping -c1 192.1.78.2

PING 192.1.78.2 (192.1.78.2) 56(84) bytes of data.

64 bytes from 192.1.78.2: icmp_seq=1 ttl=63 time=1.34 ms

具體實驗的截屏可以訪問:https://asciinema.org/a/bavkebqxc4wjgb2zv0t97es9y 。

二、Flannel中vxlan backend實現

?弄清楚了kernel中vxlan的原理后,就不難理解Flannel的機制了。

注意:

使用vxlan backend時,數據是由Kernel轉發的,Flannel不轉發數據,僅僅動態設置ARP entry

而udp backend會承擔數據轉發工具(這里不展開介紹其實現),UDP backend自帶了一個C實現的proxy,連接不同節點上的tunnel endpoints 這里討論的源碼基于最新穩定版v0.7.0。

vxlan backend啟動時會動態啟動兩個并發任務:

監聽Kernel中L3 MISS并反序列化成Golang對象

根據L3 MISS和子網配置(etcd)來自動更新本地neighbor配置

關于源碼,請看:http://dwz.cn/5MXKuC

最后,Flannel的實現中有一個小細節,在0.7.0中剛剛加入,即VTEP的IP加上了/32位的掩碼避免了廣播,此前的版本都是/16掩碼,解決了VXLAN網絡中由于廣播導致的“網絡風暴”的問題。

三、總結一下

Flannel中有多種backend,其中vxlan backend通過內核轉發數據,而udp backend通過用戶態進程中的proxy轉發數據

Flannel在使用vxlan backend的時候,短暫啟停flanneld不會造成網絡中斷,而udp backend會

很多第三方的網絡測試表明,udp backend比vxlan backend網絡的性能差大概1個數量級,一般來說只要內核支持(v3.9+),建議選擇vxlan backend

Flannel中使用vxlan backend時,建議升級到0.7+,因為此前的版本都存在潛在的網絡風暴問題

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容