Docker容器網(wǎng)絡(luò)簡(jiǎn)析

Docker容器網(wǎng)絡(luò)簡(jiǎn)析

參考:極客時(shí)間深入解析Kubernetes

Linux容器能看見(jiàn)的“網(wǎng)絡(luò)?!?,實(shí)際上是被隔離在它的Network Namespace當(dāng)中的?!熬W(wǎng)絡(luò)棧”包括了網(wǎng)卡、回環(huán)設(shè)備,路由表和iptables規(guī)則。作為一個(gè)容器,也可以聲明直接使用宿主機(jī)的網(wǎng)絡(luò)棧(-net=host),即不開(kāi)啟Network Namespace。如果直接使用宿主機(jī)網(wǎng)絡(luò)棧的化,雖然可以為容器提供良好的網(wǎng)絡(luò)性能,但是會(huì)不可避免地引入共享網(wǎng)絡(luò)資源的問(wèn)題,比如端口沖突。大多數(shù)情況下,我們都希望容器進(jìn)程能使用自己的Network Namespace,即擁有屬于自己IP地址和端口。

單機(jī)模式下容器間的通信

可以把每一個(gè)容器都看作一臺(tái)主機(jī),它們有一套獨(dú)立的“網(wǎng)絡(luò)?!?。如果要實(shí)現(xiàn)多臺(tái)主機(jī)之間的通信,就需用用網(wǎng)線把它們連接到一臺(tái)交換機(jī)上。

Linux中,能夠起到虛擬交換機(jī)作用的網(wǎng)絡(luò)設(shè)備,就是網(wǎng)橋。它是一個(gè)工作在數(shù)據(jù)鏈路層的設(shè)備,主要功能是根據(jù)MAC地址學(xué)習(xí)來(lái)將數(shù)據(jù)包轉(zhuǎn)發(fā)到網(wǎng)橋的不同端口。

Docker項(xiàng)目會(huì)默認(rèn)在宿主機(jī)上創(chuàng)建一個(gè)名叫docker0的網(wǎng)橋,凡是連接在docker0網(wǎng)橋上的容器,都可以通過(guò)它進(jìn)行通信。

docker容器通過(guò)一種名叫Veth Pair的虛擬設(shè)備“連接”到docker0網(wǎng)橋上。Veth Pair設(shè)備的特點(diǎn):總是以兩張?zhí)摂M網(wǎng)卡的形式成對(duì)出現(xiàn)。并且,從其中一個(gè)網(wǎng)卡發(fā)出的數(shù)據(jù)包,可以直接出現(xiàn)在與它對(duì)應(yīng)的另一張網(wǎng)卡上,哪怕這兩張網(wǎng)卡不在一個(gè)Network Namespace里。這使得Veth Pair常常被用作不同Network Namespace里的網(wǎng)線。

比如,現(xiàn)在我們啟動(dòng)一個(gè)叫做nginx-1的容器

[root@host1 ~]# docker run -d --name nginx-1 nginx

然后,我們進(jìn)入這個(gè)容器,查看他的網(wǎng)絡(luò)設(shè)備

# 在宿主機(jī)上
$ docker exec -it nginx-1 /bin/bash
# 在容器里
root@2b3c181aecf1:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
       inet 172.17.0.2 netmask 255.255.0.0 broadcast 0.0.0.0
       inet6 fe80::42:acff:fe11:2 prefixlen 64 scopeid 0x20<link>
       ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
       RX packets 364 bytes 8137175 (7.7 MiB)
       RX errors 0 dropped 0 overruns 0 frame 0
       TX packets 281 bytes 21161 (20.6 KiB)
       TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
       inet 127.0.0.1 netmask 255.0.0.0
       inet6 ::1 prefixlen 128 scopeid 0x10<host>
       loop txqueuelen 1000 (Local Loopback)
       RX packets 0 bytes 0 (0.0 B)
       RX errors 0 dropped 0 overruns 0 frame 0
       TX packets 0 bytes 0 (0.0 B)
       TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ route
Kernel IP routing table
Destination Gateway     Genmask     Flags Metric Ref Use Iface
default     172.17.0.1  0.0.0.0     UG    0      0   0   eth0
172.17.0.0  0.0.0.0    255.255.0.0  U     0      0   0   eth0

這個(gè)容器中有一張etho的網(wǎng)卡,它就是Veth Pair設(shè)備在容器的這一端。

通過(guò)查看路由表,可以看出所有對(duì)172.17.0.0/16網(wǎng)段的請(qǐng)求,也會(huì)交給eth0處理。

Veth Pair設(shè)備的另一端,在宿主機(jī)上可以通過(guò)宿主機(jī)的網(wǎng)絡(luò)設(shè)備看到它,如下所示:

# 在宿主機(jī)上
$ ifconfig
...
docker0 Link encap:Ethernet HWaddr 02:42:d8:e4:df:c1
        inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
        inet6 addr: fe80::42:d8ff:fee4:dfc1/64 Scope:Link
        UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
        RX packets:309 errors:0 dropped:0 overruns:0 frame:0
        TX packets:372 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
           RX bytes:18944 (18.9 KB) TX bytes:8137789 (8.1 MB)
vethb4963f3 Link encap:Ethernet HWaddr 52:81:0b:24:3d:da
           inet6 addr: fe80::5081:bff:fe24:3dda/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
           RX packets:288 errors:0 dropped:0 overruns:0 frame:0
           TX packets:371 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
           RX bytes:21608 (21.6 KB) TX bytes:8137719 (8.1 MB)
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242d8e4dfc1 no veth9c02e56

這時(shí)候,我們?cè)僭谶@臺(tái)宿主機(jī)上啟動(dòng)一個(gè)Docker容器nginx-2:

$ docker run –d --name nginx-2 nginx
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242d8e4dfc1 no veth9c02e56
vethb4963f3

這時(shí)候,在nginx-1容器中訪問(wèn)nginx-2容器的IP地址(比如ping172.17.0.3)的時(shí)候,這個(gè)目的IP地址就會(huì)匹配到nginx-1中的第二條路由規(guī)則。這條規(guī)則的路由網(wǎng)關(guān)是0.0.0.0,這就意味著這是一條直連規(guī)則,即:凡是匹配到這條規(guī)則的IP包,應(yīng)該經(jīng)過(guò)本機(jī)的eth0網(wǎng)卡,通過(guò)二層網(wǎng)絡(luò)直接發(fā)往目的主機(jī)。

要通過(guò)二層網(wǎng)絡(luò)到達(dá)nginx-2容器,就需要有172.17.0.3這個(gè)IP地址的MAC地址。所以nginx-1容器的網(wǎng)絡(luò)協(xié)議棧,就需要通過(guò)eth0網(wǎng)卡發(fā)送一個(gè)ARP廣播,來(lái)通過(guò)IP地址查找對(duì)應(yīng)二點(diǎn)MAC地址。(ARP是通過(guò)三層的IP地址找到對(duì)應(yīng)二層MAC地址的協(xié)議)

eth0網(wǎng)卡是一個(gè)Veth Pair,它的一端在這個(gè)nginx-1容器的NetworkNamespace里,另一端位于宿主機(jī)上,并且被插在了docker0網(wǎng)橋上。一旦一張?zhí)摂M網(wǎng)卡被插在網(wǎng)橋上,它就會(huì)變?yōu)榫W(wǎng)橋的一個(gè)端口。這個(gè)端口的唯一作用就是接收流入的數(shù)據(jù)包,然后把這些數(shù)據(jù)包的處理操作全部交給對(duì)應(yīng)的網(wǎng)橋。

所以在收到這些ARP請(qǐng)求后,docker0網(wǎng)橋就會(huì)扮演二層交換機(jī)的角色,把ARP廣播轉(zhuǎn)發(fā)到其他插在了docker0的虛擬網(wǎng)卡上。這樣連接在docker0上的nginx-2容器的網(wǎng)絡(luò)協(xié)議棧就會(huì)收到這個(gè)ARP請(qǐng)求,從而將172.17.0.3所對(duì)應(yīng)MAC地址回復(fù)給nginx-1容器。

有了這個(gè)MAC地址,nginx-1容器的eth0網(wǎng)卡就可以將數(shù)據(jù)包發(fā)出去。

而根據(jù)Veth Pair設(shè)備的原理,這個(gè)數(shù)據(jù)包會(huì)立即出現(xiàn)在宿主機(jī)上的veth9c02e56。對(duì)于宿主機(jī)來(lái)說(shuō),docker0就是一個(gè)普通的網(wǎng)卡,所以對(duì)于docker0上流入的數(shù)據(jù)包,就會(huì)通過(guò)宿主機(jī)的網(wǎng)絡(luò)協(xié)議棧進(jìn)行處理。在宿主機(jī)上,docker0會(huì)為你設(shè)置如下所示的路由規(guī)則:

[root@host1 ~]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
...
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
...

這次流入的數(shù)據(jù)包的IP地址是172.17.0.3,所以它出現(xiàn)在宿主機(jī)之后,就會(huì)按照上述這條路由規(guī)則,再次交給docker0網(wǎng)橋轉(zhuǎn)發(fā)出去。docker0繼續(xù)扮演二層交換機(jī)的角色,此時(shí)docker0根據(jù)數(shù)據(jù)包的目的MAC地址,在它的CAM表(交換機(jī)通過(guò)MAC地址學(xué)習(xí)維護(hù)的端口和MAC地址的對(duì)應(yīng)表)里查到對(duì)應(yīng)的端口為vethb4963f3,然后把數(shù)據(jù)包發(fā)往這個(gè)端口。

這個(gè)端口正是nginx-2容器在docker0網(wǎng)橋插的一塊虛擬網(wǎng)卡,也是一個(gè)Veth Pair設(shè)備。這樣,數(shù)據(jù)包就進(jìn)入到了nginx-2容器的network namespace里。

nginx-2發(fā)現(xiàn)自己的eth0網(wǎng)卡流入了數(shù)據(jù)包,這樣,nginx-2的網(wǎng)絡(luò)協(xié)議棧就會(huì)對(duì)請(qǐng)求進(jìn)行處理,最后將響應(yīng)返回到nginx-1.

整個(gè)流程如下圖所示:

image.png

容器跨主通信

Flannel項(xiàng)目

Flannel項(xiàng)目是CoreOS主推的容器網(wǎng)絡(luò)方案。Flannel支持三種后端實(shí)現(xiàn),分別是:

  1. VXLAN;
  2. host-gw
  3. UDP

UDP模式(已棄用)

UDP模式,是Fannel最早支持的一種方式,也是性能最差的一種,目前已經(jīng)被棄用。這種模式是最直接,也是最容易理解的容器跨主網(wǎng)絡(luò)實(shí)現(xiàn)。
本例中,有兩臺(tái)宿主機(jī)

  • 宿主機(jī)Node1上有一個(gè)容器container-1,它的IP地址是100.96.1.2,對(duì)應(yīng)的docker0網(wǎng)橋的地址是:100.96.1.1/24
  • 宿主機(jī)Node2上有一個(gè)容器container-2,它的IP地址是100.96.2.3,對(duì)應(yīng)的docker0網(wǎng)橋地址是:100.96.2.1/24
    現(xiàn)在的任務(wù)就是讓container-1訪問(wèn)container-2
    container-1容器中的進(jìn)程發(fā)起的IP包,其源地址就是100.96.1.2,目的地址就是100.96.2.3。由于目的地址100.96.2.3并不在Node1的docker0網(wǎng)橋?qū)?yīng)的網(wǎng)段,所以這個(gè)IP包會(huì)交給默認(rèn)路由規(guī)則,通過(guò)容器的網(wǎng)關(guān)進(jìn)入docker0網(wǎng)橋,這時(shí)候,這個(gè)IP包的下一個(gè)目的地址,就取決于宿主機(jī)的路由規(guī)則。此時(shí),F(xiàn)lannel已經(jīng)在宿主機(jī)上創(chuàng)建了一系列的路由規(guī)則,以Node1為例,如下:
# 在 Node 1 上
$ ip route
default via 10.168.0.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0
100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1
10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.2

由于我們Ip包的目的地址是100.96.2.3,所以只能匹配到第二條路由規(guī)則,進(jìn)入一個(gè)叫做flannel0的設(shè)備。

flannel0設(shè)備

lannel0設(shè)備是一個(gè)TUN設(shè)備(Tunnel設(shè)備)。
TUN設(shè)備是一種工作在三層(Network Layer)的虛擬網(wǎng)絡(luò)設(shè)備。TUN設(shè)備的功能很簡(jiǎn)單,即:在操作系統(tǒng)內(nèi)核和用戶應(yīng)用程序之間傳遞IP包。
以flannel0設(shè)備為例:當(dāng)操作系統(tǒng)將一個(gè)IP包發(fā)送給flannel0設(shè)備之后,flannel0就會(huì)把這個(gè)IP包,交給創(chuàng)建這個(gè)設(shè)備的應(yīng)用程序,也就是Flannel進(jìn)程。這是一個(gè)從內(nèi)核態(tài)(Linux操作系統(tǒng))到用戶態(tài)(Flannel進(jìn)程)的流動(dòng)方向。
反之,如果Flannel進(jìn)程向flannel0設(shè)備發(fā)送了一個(gè)IP包,那么這個(gè)IP包就會(huì)出現(xiàn)在宿主機(jī)的網(wǎng)絡(luò)棧中,這是一個(gè)用戶態(tài)向內(nèi)核態(tài)的流動(dòng)方向。

所以,當(dāng)IP包從容器經(jīng)過(guò)docker0出現(xiàn)在宿主機(jī),然后又根據(jù)路由表進(jìn)入flannel0設(shè)備后,宿主機(jī)上的flanneld進(jìn)程(Flannel項(xiàng)目在每個(gè)宿主機(jī)上的主進(jìn)程),就會(huì)收到這個(gè)IP包。然后flanneld進(jìn)程看到了這個(gè)IP包的目的地址,是100.96.2.3,就把它發(fā)送給了Node2宿主機(jī)。

子網(wǎng)

flanneld是通過(guò)子網(wǎng),知道這個(gè)IP地址對(duì)應(yīng)的容器,是運(yùn)行在Node2上的。
由Flannel管理的容器網(wǎng)絡(luò)里,一臺(tái)宿主機(jī)上的所有容器,都屬于該宿主機(jī)被分配的一個(gè)“子網(wǎng)”。在我們的例子中,Node1的子網(wǎng)是100.96.1.0/24,container-1的IP地址是100.96.1.2。Node2的子網(wǎng)是100.96.2.0/24,container-2的IP地址是100.96.2.3。
這些子網(wǎng)與宿主機(jī)的對(duì)應(yīng)關(guān)系,保存在etcd中。如下所示:

$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
/coreos.com/network/subnets/100.96.3.0-24

所以,flanneld進(jìn)程在處理由flannel0傳入的IP包時(shí),就可以根據(jù)IP的地址,匹配到對(duì)應(yīng)的子網(wǎng),從Etcd中找到這個(gè)子網(wǎng)對(duì)應(yīng)的宿主機(jī)的IP地址是10.168.0.3,如下所示:

$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24
{"PublicIP":"10.168.0.3"}

flanneld在收到container-1發(fā)給container-2的IP包之后,就會(huì)把這個(gè)IP包封裝在一個(gè)UDP包中,然后發(fā)送給Node2。這個(gè)UDP包的源地址,就是flanneld所在的Node1的地址,目的地址,就是Container-2所在的宿主機(jī)Node2的地址。
每臺(tái)宿主機(jī)上的flanneld,都監(jiān)聽(tīng)著一個(gè)8252端口,所以flanneld只要把這個(gè)UDP包發(fā)往Node2的8252端口即可。
這樣,一個(gè)UDP包就從Node1到達(dá)了Node2。Node2上負(fù)責(zé)監(jiān)聽(tīng)8252端口的也是flanneld,這時(shí)候Node2上的flanneld就可以從這個(gè)UDP包里解析出封裝在里面的、container-1發(fā)來(lái)的原IP包。然后flanneld會(huì)直接把這個(gè)IP包發(fā)送給它所管理的TUN設(shè)備,即flannel0設(shè)備。
這是一個(gè)用戶態(tài)向內(nèi)核態(tài)的流動(dòng)方向,所以Linux內(nèi)核網(wǎng)絡(luò)棧就會(huì)負(fù)責(zé)處理這個(gè)IP包,具體的處理方法,就是通過(guò)本機(jī)的路由表來(lái)尋找這個(gè)IP包的下一步流向。
Node2上的路由表,如下所示:

# 在 Node 2 上
$ ip route
default via 10.168.0.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.2.0
100.96.2.0/24 dev docker0 proto kernel scope link src 100.96.2.1
10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.3

這個(gè)IP包的目的地址是100.96.2.3,它和第三條路由規(guī)則匹配,所以,Linux內(nèi)核會(huì)將這個(gè)IP包準(zhǔn)發(fā)給docker0網(wǎng)橋。
接下來(lái)的流程,就和單機(jī)模式一致了,docker0網(wǎng)橋扮演二層交換機(jī)的角色,將數(shù)據(jù)包發(fā)送給正確的端口,進(jìn)而通過(guò)Veth Pair設(shè)備進(jìn)入到container-2的Network Namespace中。
container-2返回給container-1的數(shù)據(jù)包,則會(huì)經(jīng)過(guò)與上述相反的路徑回到container-1中。
其流程如下圖所示:


image.png

Flannel UDP模式提供的是一個(gè)三層的Overlay網(wǎng)絡(luò),即:它首先對(duì)發(fā)出端的IP包進(jìn)行UDP封裝,然后在接收端進(jìn)行解封拿到原始的IP包,進(jìn)而把這個(gè)IP包發(fā)送給目標(biāo)容器。

UDP模式的性能問(wèn)題

相比于兩臺(tái)宿主機(jī)直接通信,F(xiàn)lannel UDP模式的容器通信多了一個(gè)額外的步驟,即flanneld的處理過(guò)程。這個(gè)過(guò)程,由于使用到了flannel0這個(gè)TUN設(shè)備,僅在發(fā)出IP包的過(guò)程,就需要三次用戶態(tài)與內(nèi)核態(tài)之間的數(shù)據(jù)拷貝,如下:


image.png

第一次:用戶態(tài)的容器進(jìn)程發(fā)出IP包經(jīng)過(guò)docker0網(wǎng)橋進(jìn)入內(nèi)核態(tài)。
第二次:IP包根據(jù)路由表進(jìn)入TUN設(shè)備,從而回到用戶態(tài)的flanneld進(jìn)程;
第三次:flanneld進(jìn)行UDP封包之后重新進(jìn)入內(nèi)核態(tài),將UDP包通過(guò)宿主機(jī)的eth0發(fā)出去。

VXLAN模式

VXLAN,即Virtural Extensible LAN(虛擬可擴(kuò)展局域網(wǎng)),是Linux內(nèi)核本身就支持的一種虛擬化技術(shù)。所以說(shuō),VXLAN可以完全在內(nèi)核態(tài)實(shí)現(xiàn)上述封裝和解封的工作,從而通過(guò)與前面相似的“隧道”機(jī)制,構(gòu)建出覆蓋網(wǎng)絡(luò)。
VXLAN的覆蓋網(wǎng)絡(luò)設(shè)計(jì)思想:在現(xiàn)有的三層網(wǎng)絡(luò)之上,“覆蓋”一層虛擬的、由內(nèi)核VXLAN模塊負(fù)責(zé)維護(hù)的二層網(wǎng)絡(luò),使得連接在VXLAN二層網(wǎng)絡(luò)上的主機(jī)(虛擬機(jī)或者容器)之間,可以像在同一個(gè)局域網(wǎng)里自由通信。
為了能夠在二層網(wǎng)絡(luò)上打通隧道,VXLAN會(huì)在宿主機(jī)上設(shè)置一個(gè)特殊的網(wǎng)絡(luò)設(shè)備作為“隧道”兩端,這個(gè)設(shè)備叫做VTEP,即VXLAN TUN End Point(虛擬隧道端點(diǎn))。
VTEP設(shè)備的作用和flanneld進(jìn)程非常相似,只不過(guò),它進(jìn)行封裝和解封裝的對(duì)象是二層數(shù)據(jù)幀;這個(gè)工作流程,全部是在內(nèi)核中完成的。(VXLAN本身就是Linux內(nèi)核中的一個(gè)模塊)。
流程圖如下:


image.png

每臺(tái)宿主機(jī)上名叫flannel.1的設(shè)備,就是VXLAN所需的VTEP設(shè)備,它既有IP地址,也有MAC地址。
現(xiàn)在,container-1的IP地址是10.1.15.2,container-2的IP地址是10.1.16.3
與UDP模式類似,當(dāng)container-1發(fā)出請(qǐng)求之后,這個(gè)目的地址是10.1.16.3的IP包會(huì)首先出現(xiàn)在docker0網(wǎng)橋,然后被路由到本機(jī)flannel.1設(shè)備進(jìn)行處理。也就是到了“隧道”的入口。
為了能夠?qū)ⅰ霸糏P包”封裝并發(fā)送到正確的宿主機(jī),VXLAN就需要找到這條隧道的出口,即目的宿主機(jī)的VTEP設(shè)備。這個(gè)設(shè)備的信息,是由每臺(tái)宿主機(jī)上的flanneld進(jìn)程負(fù)責(zé)維護(hù)的。
比如,Node2啟動(dòng)并加入Flannel網(wǎng)絡(luò),在Node1以及其他節(jié)點(diǎn)上,flanneld就會(huì)添加一條如下所示的路由規(guī)則:

$ route -n
Kernel IP routing table
Destination     Gateway       Genmask           Flags  Metric  Ref  Use   Iface
...
10.1.16.0      10.1.16.0    255.255.255.0    UG     0      0     0   flannel.1

即:凡是發(fā)往10.1.16.0/24網(wǎng)段的IP包,都需要經(jīng)過(guò)flannel.1設(shè)備發(fā)出,并且,它最后發(fā)往的網(wǎng)關(guān)地址是:10.1.16.0
10.1.16.0對(duì)應(yīng)的就是Node2上的VTEP設(shè)備的IP地址。
源VTEP設(shè)備收到原始數(shù)據(jù)包之后,就要想辦法把原始數(shù)據(jù)包加上一個(gè)MAC地址,封裝成一個(gè)二層數(shù)據(jù)幀,然后發(fā)送給目的VTEP設(shè)備。我們已經(jīng)知道了目的VTEP設(shè)備的IP地址,要根據(jù)這個(gè)三層IP地址查詢對(duì)應(yīng)的二層MAC地址,這正是ARP表的功能。
有了目的VTEP設(shè)備的MAC地址,Linux內(nèi)核就可以開(kāi)始二層封包工作了。這個(gè)二層幀的格式,如下:


image.png

上述封包過(guò)程只是加一個(gè)二層頭,不會(huì)改變?cè)糏P包的內(nèi)容。
上面提到的這些VTEP設(shè)備的MAC地址,對(duì)于宿主機(jī)網(wǎng)絡(luò)來(lái)說(shuō)沒(méi)有什么實(shí)際意義。所以上面封裝出來(lái)的數(shù)據(jù)幀,并不能在我們的宿主機(jī)二層網(wǎng)絡(luò)里傳輸。我們把它稱為內(nèi)部數(shù)據(jù)幀。
接下來(lái),Linux內(nèi)核還需要把內(nèi)部數(shù)據(jù)幀進(jìn)一步封裝成為宿主機(jī)網(wǎng)絡(luò)里的一個(gè)普通數(shù)據(jù)幀,讓它“載著”內(nèi)部數(shù)據(jù)幀,通過(guò)宿主機(jī)的eth0網(wǎng)卡進(jìn)行傳輸。我們把這次封裝出來(lái)的數(shù)據(jù)幀稱為外部數(shù)據(jù)幀。
為了實(shí)現(xiàn)這個(gè)“搭便車”的機(jī)制,Linux內(nèi)核會(huì)在內(nèi)部數(shù)據(jù)幀前面,加上一個(gè)特殊的VXLAN頭,用來(lái)表示這個(gè)乘客實(shí)際上是一個(gè)VXLAN要使用的數(shù)據(jù)幀。
這個(gè)VXLAN頭里有一個(gè)重要的標(biāo)識(shí)叫做VNI,它是VTEP設(shè)備識(shí)別某個(gè)數(shù)據(jù)幀是不是應(yīng)該歸自己處理的重要標(biāo)識(shí)。在Flannel中,VNI的默認(rèn)值是1,這就是宿主機(jī)上的VTEP設(shè)備都叫做flannel.1的原因,這里的1就是VNI的值。
然后Linux內(nèi)核會(huì)把這個(gè)數(shù)據(jù)幀封裝成一個(gè)UDP包發(fā)出去。
一個(gè)flannel.1設(shè)備只知道另一端flannel.1設(shè)備的MAC地址,卻不知道對(duì)應(yīng)的宿主機(jī)地址。其實(shí),flannel.1設(shè)備扮演的就是一個(gè)網(wǎng)橋的角色,在二層網(wǎng)絡(luò)之間進(jìn)行UDP包的轉(zhuǎn)發(fā)。在Linux內(nèi)核里面,網(wǎng)橋設(shè)備轉(zhuǎn)發(fā)的依據(jù),來(lái)自一個(gè)叫做FDB(Forwarding Database)的轉(zhuǎn)發(fā)數(shù)據(jù)庫(kù)。這個(gè)flannel.1網(wǎng)橋?qū)?yīng)的FDB信息,也是flanneld進(jìn)程負(fù)責(zé)維護(hù)的。它的內(nèi)容可以通過(guò)bridge fdb命令查看到,如下所示:

# 在 Node 1 上,使用“目的 VTEP 設(shè)備”的 MAC 地址進(jìn)行查詢
$ bridge fdb show flannel.1 | grep 5e:f8:4f:00:e3:37
5e:f8:4f:00:e3:37 dev flannel.1 dst 10.168.0.3 self permanent

這條規(guī)則的含義是:發(fā)往目的VTEP設(shè)備(MAC地址是5e:f8:4f:00:e3:37)的二層數(shù)據(jù)幀,應(yīng)該是通過(guò)flannel.1設(shè)備,發(fā)往10.168.0.3的主機(jī),這個(gè)主機(jī)就是Node2,UDP包的目的地就找到了。
接下來(lái)的流程,就是一個(gè)正常的,宿主機(jī)網(wǎng)絡(luò)上的封包工作。
UDP包是一個(gè)四層數(shù)據(jù)包,Linux內(nèi)核會(huì)在它前面加一個(gè)IP頭,組成一個(gè)IP包。并且,在這個(gè)IP頭里,回填上通過(guò)FDB查詢出來(lái)的目的主機(jī)的IP地址。然后Linux內(nèi)核再在這個(gè)IP包前面加上二層數(shù)據(jù)幀頭,并把Node2的MAC地址填進(jìn)去。


image.png

接下來(lái),Node1的flannel.1設(shè)備就可以把這個(gè)數(shù)據(jù)幀從Node1的eth0網(wǎng)卡發(fā)出去,然后經(jīng)過(guò)宿主機(jī)網(wǎng)絡(luò)來(lái)到Node2的eth0網(wǎng)卡。
這時(shí)候,Node2的內(nèi)核網(wǎng)絡(luò)棧會(huì)發(fā)現(xiàn)這個(gè)數(shù)據(jù)幀里有VXLAN Header,并且VNI為1.所以linux內(nèi)核會(huì)對(duì)它進(jìn)行拆包,拿到里面的內(nèi)部數(shù)據(jù)幀,然后根據(jù)VNI的值,把它交給Node2上的flannel.1設(shè)備。
flannel.1會(huì)進(jìn)一步拆包,取出原始數(shù)據(jù)包。然后之后的流程就和單機(jī)模式類似了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 概述 自從docker容器出現(xiàn)以來(lái),容器的網(wǎng)絡(luò)通信就一直是大家關(guān)注的焦點(diǎn),也是生產(chǎn)環(huán)境的迫切需求。而容器的網(wǎng)絡(luò)通信...
    糙老爺們兒吃什么櫻桃閱讀 3,722評(píng)論 1 5
  • 很多人可能在項(xiàng)目中已經(jīng)使用docker很長(zhǎng)時(shí)間,但是卻很少有人知道docker的網(wǎng)絡(luò)是如何實(shí)現(xiàn)的。我應(yīng)該就算是很多...
    水雨田閱讀 1,241評(píng)論 0 3
  • docker 我們先來(lái)了解下docker的原理,如何才能制造出一個(gè)真正隔離的軟件運(yùn)行環(huán)境. namespace d...
    董鵬dp閱讀 680評(píng)論 0 0
  • 難得的良心劇,一直被朋友強(qiáng)烈推薦,第一集看了不下五六次都不能理解它好在哪。人物對(duì)不上號(hào),陌生的生活方式,迷迷糊糊看...
    Mango777閱讀 439評(píng)論 0 0
  • 明天晚上就要出發(fā)去北京了,不知道這一去自己所面臨是刀山還是火海,心里很是忐忑,可不管是那個(gè)危險(xiǎn)我都得坦然面對(duì),誰(shuí)讓...
    淫洋藿閱讀 220評(píng)論 0 0

友情鏈接更多精彩內(nèi)容