這次分享介紹Flannel中的vxlan backend,包含兩方面內(nèi)容:
?????? 深入理解內(nèi)核中的VXLAN原理:使用iproute2和bridge等原生工具來搭建一個(gè)基于VXLAN的Overlay網(wǎng)絡(luò)。
?????? 理解Flannel使用vxlan backend時(shí)的工作原理: 有了前面對內(nèi)核VXLAN原理的理解,通過分析Flannel部分源碼來從根本上掌握其vxlan backend的原理。
一、VXLAN的原理
? ? ? ? Virtual eXtensible Local Area Network(VXLAN)是一個(gè)在已有的3層物理網(wǎng)絡(luò)上構(gòu)建2層邏輯網(wǎng)絡(luò)的協(xié)議。
?????? 在2012年底的v3.7.0之后,Linux Kernel加入了VXLAN協(xié)議支持,作者:Stephen Hemminger,所以如果要使用Linux Kernel中的VXLAN支持,最低內(nèi)核版本3.7+(推薦3.9+)。
?????? Stephen Hemminger同時(shí)也實(shí)現(xiàn)了iproute2、bridge等工具,用以管理Linux中復(fù)雜的網(wǎng)絡(luò)配置,目前在絕大多數(shù)Linux發(fā)行版中都是默認(rèn)支持的。
?????? VXLAN本質(zhì)上是一種tunnel(隧道)協(xié)議,用來基于3層網(wǎng)絡(luò)實(shí)現(xiàn)虛擬的2層網(wǎng)絡(luò)。泛泛地說,tunnel協(xié)議有點(diǎn)像今天電話會議,通過可視電話連接不同的會議室讓每個(gè)人能夠直接交談,就好像坐在一個(gè)會議室里一樣。很多tunnel協(xié)議,如GRE也有類似VXLAN中VNI的用法。
????? tunnel協(xié)議的另外一個(gè)重要的特性就是軟件擴(kuò)展性,是軟件定義網(wǎng)絡(luò)(Software-defined Network,SDN)的基石之一。
????? Flannel中有兩個(gè)基于tunnel協(xié)議的backend:UDP(默認(rèn)實(shí)現(xiàn))和VXLAN,本質(zhì)上都是tunnel協(xié)議,區(qū)別僅僅在于協(xié)議本身和實(shí)現(xiàn)方式。
?????? 這里順便提一句:tunnel協(xié)議在比較老的內(nèi)核中已經(jīng)有支持,我印象中v2.2+就可以使用tunnel來創(chuàng)建虛擬網(wǎng)絡(luò)了,因此UDP backend適合在沒有vxlan支持的linux版本中使用,但性能會相比vxlan backend差一些。
?以上是一些背景介紹,下面開始介紹VXLAN的內(nèi)核支持
圖1. VXLAN可以在分布多個(gè)網(wǎng)段的主機(jī)間構(gòu)建2層虛擬網(wǎng)絡(luò)

圖2. VXLAN基本原理:套路還是tunnel那一套,區(qū)別僅僅在于tunnel協(xié)議本身的實(shí)現(xiàn)

?????? 為了說明圖1和圖2中談到的VXLAN原理,這里在兩臺不同網(wǎng)段的VPS上手動(dòng)搭建一個(gè)Overlay Network,并在兩個(gè)節(jié)點(diǎn)上分別運(yùn)行了Docker Container,當(dāng)我們看到容器之間使用虛擬網(wǎng)絡(luò)的IP完成直接通信時(shí),實(shí)驗(yàn)就成功了。
圖3 手動(dòng)搭建vxlan虛擬網(wǎng)絡(luò)的網(wǎng)絡(luò)拓?fù)?/p>

圖3中提到一個(gè)VTEP的概念,全稱VXLAN Tunnel Endpoint,本質(zhì)上就是前面提到的tunnel中的endpoint
現(xiàn)在正式開始手動(dòng)搭建圖 3中的虛擬網(wǎng)絡(luò)
第一步、創(chuàng)建docker bridge
?????? 默認(rèn)的docker bridge地址范圍是172.17.0.1/24(比較老的版本是172.17.42.1/24), 而本實(shí)驗(yàn)中兩個(gè)節(jié)點(diǎn)node1和node2的子網(wǎng)要求分別為: 192.1.78.1/24,192.1.87.1/24 。
????? 修改docker daemon啟動(dòng)參數(shù),增加以下參數(shù)后重啟docker daemon:
node1: --bip=192.1.78.1/24
node2: --bip=192.1.87.1/24
?????? 這時(shí)node1和node2的容器之間還不能直接通信, node1也不能跨主機(jī)和node2上的容器直接通信,反之node2也無法直接和node1上的容器通信.。
第二步、創(chuàng)建VTEPs
在node1上執(zhí)行以下命令:
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上執(zhí)行以下命令:
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表一般不會手動(dòng)更新,在VXLAN的實(shí)現(xiàn)中由對應(yīng)的Network Agent監(jiān)聽L3 MISS來 動(dòng)態(tài)更新;這里手動(dòng)添加ARP entry僅僅是為了測試;另外,如果跨主機(jī)訪問多個(gè)IP, 每個(gè)跨主機(jī)的IP就都需要配置對應(yīng)的ARP entry。
????? 以上操作都需要root權(quán)限,完成后整個(gè)Overlay Network就搭建成功了,下面通過測試兩種連通性來總結(jié)本實(shí)驗(yàn):
node1容器與node2上的容器直接通信(容器與跨主機(jī)容器間直接通信)
node1與node2上容器直接通信;node2與node1上容器直接通信(主機(jī)和跨主機(jī)容器之間通信)
????? 先看容器與跨主機(jī)容器間直接通信的測試。
現(xiàn)在node1和node2上分別起一個(gè)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
然后看主機(jī)和跨主機(jī)容器之間連通性的測試。
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
具體實(shí)驗(yàn)的截屏可以訪問:https://asciinema.org/a/bavkebqxc4wjgb2zv0t97es9y 。
二、Flannel中vxlan backend實(shí)現(xiàn)
?弄清楚了kernel中vxlan的原理后,就不難理解Flannel的機(jī)制了。
注意:
使用vxlan backend時(shí),數(shù)據(jù)是由Kernel轉(zhuǎn)發(fā)的,F(xiàn)lannel不轉(zhuǎn)發(fā)數(shù)據(jù),僅僅動(dòng)態(tài)設(shè)置ARP entry
而udp backend會承擔(dān)數(shù)據(jù)轉(zhuǎn)發(fā)工具(這里不展開介紹其實(shí)現(xiàn)),UDP backend自帶了一個(gè)C實(shí)現(xiàn)的proxy,連接不同節(jié)點(diǎn)上的tunnel endpoints 這里討論的源碼基于最新穩(wěn)定版v0.7.0。
vxlan backend啟動(dòng)時(shí)會動(dòng)態(tài)啟動(dòng)兩個(gè)并發(fā)任務(wù):
監(jiān)聽Kernel中L3 MISS并反序列化成Golang對象
根據(jù)L3 MISS和子網(wǎng)配置(etcd)來自動(dòng)更新本地neighbor配置
關(guān)于源碼,請看:http://dwz.cn/5MXKuC
最后,F(xiàn)lannel的實(shí)現(xiàn)中有一個(gè)小細(xì)節(jié),在0.7.0中剛剛加入,即VTEP的IP加上了/32位的掩碼避免了廣播,此前的版本都是/16掩碼,解決了VXLAN網(wǎng)絡(luò)中由于廣播導(dǎo)致的“網(wǎng)絡(luò)風(fēng)暴”的問題。
三、總結(jié)一下
Flannel中有多種backend,其中vxlan backend通過內(nèi)核轉(zhuǎn)發(fā)數(shù)據(jù),而udp backend通過用戶態(tài)進(jìn)程中的proxy轉(zhuǎn)發(fā)數(shù)據(jù)
Flannel在使用vxlan backend的時(shí)候,短暫啟停flanneld不會造成網(wǎng)絡(luò)中斷,而udp backend會
很多第三方的網(wǎng)絡(luò)測試表明,udp backend比vxlan backend網(wǎng)絡(luò)的性能差大概1個(gè)數(shù)量級,一般來說只要內(nèi)核支持(v3.9+),建議選擇vxlan backend
Flannel中使用vxlan backend時(shí),建議升級到0.7+,因?yàn)榇饲暗陌姹径即嬖跐撛诘木W(wǎng)絡(luò)風(fēng)暴問題