Docker的Linux網(wǎng)絡(luò)基礎(chǔ)
Docker 技術(shù)依賴(lài)于近年來(lái) Linux 內(nèi)核虛擬化技術(shù)的發(fā)展,所以 Docker 對(duì) Linux 內(nèi)核有很強(qiáng)的依賴(lài)。本文將 Docker 使用到的與 Linux 網(wǎng)絡(luò)有關(guān)的主要技術(shù)進(jìn)行簡(jiǎn)單介紹。
一、網(wǎng)絡(luò)命名空間
為了支持網(wǎng)絡(luò)協(xié)議棧的多個(gè)實(shí)例, Linux 在網(wǎng)絡(luò)棧中引入了網(wǎng)絡(luò)命名空間,這些獨(dú)立的協(xié)議棧被隔離到不同的命名空間中。處于不同命名空間中的網(wǎng)絡(luò)棧是完全隔離的,彼此之間無(wú)法通信。通過(guò)對(duì)網(wǎng)絡(luò)資源的隔離,就能在一個(gè)宿主機(jī)上虛擬多個(gè)不同的網(wǎng)絡(luò)環(huán)境。Docker 正是利用了網(wǎng)絡(luò)的命名空間特性,實(shí)現(xiàn)了不同容器之間的網(wǎng)絡(luò)隔離。
在 Linux 的網(wǎng)絡(luò)命名空間中可以有自己獨(dú)立的路由表及獨(dú)立的 iptables 設(shè)置來(lái)提供包轉(zhuǎn)發(fā)、 NAT 及 IP 包過(guò)濾等功能。
1. 網(wǎng)絡(luò)命名空間的實(shí)現(xiàn)
為了隔離出獨(dú)立的協(xié)議棧,需要納入命名空間的元素有進(jìn)程、套接字、網(wǎng)絡(luò)設(shè)備等。
Linux 的網(wǎng)絡(luò)協(xié)議棧是十分復(fù)雜的,為了支持獨(dú)立的協(xié)議棧,相關(guān)的這些全局變量都必須被修改為協(xié)議棧私有。最好的辦法就是讓這些全局變量成為 Net Namespace 變量的成員,然后為協(xié)議棧的函數(shù)調(diào)用加入一個(gè) Namespace 參數(shù)。這就是 Linux 實(shí)現(xiàn)網(wǎng)絡(luò)命名空間的核心。
命名空間的內(nèi)核數(shù)據(jù)結(jié)構(gòu):
在新生成的私有命名空間中只有回環(huán)設(shè)備(名為 lo 且是停止?fàn)顟B(tài)),其他設(shè)備默認(rèn)都不存在,如果我們需要,則要一一手工建立。Docker 容器中的各類(lèi)網(wǎng)絡(luò)棧設(shè)備都是 Docker Daemon 在啟動(dòng)時(shí)自動(dòng)創(chuàng)建和配置的。
所有網(wǎng)絡(luò)設(shè)備(物理的或虛擬接口、橋等在內(nèi)核里都叫作 Net Device)都只能屬于一個(gè)命名空間。當(dāng)然,物理設(shè)備(連接實(shí)際硬件的設(shè)備)通常只能關(guān)聯(lián)到 root 這個(gè)命名空間中。虛擬網(wǎng)絡(luò)設(shè)備(虛擬以太網(wǎng)接口或者虛擬網(wǎng)口對(duì))則可以被創(chuàng)建并關(guān)聯(lián)到一個(gè)給定的命名空間中,而且可以在這些命名空間之間移動(dòng)。
不同網(wǎng)絡(luò)命名空間是相互隔離,無(wú)法通信的,而 Veth 設(shè)備對(duì)的一個(gè)重要作用就是打通了相互看不到的協(xié)議棧之間的壁壘,它就像一條管子,一端連著這個(gè)網(wǎng)絡(luò)命名空間的協(xié)議棧,一端連著另一個(gè)網(wǎng)絡(luò)命名空間的協(xié)議棧。所以如果想在兩個(gè)命名空間之間通信 ,就必須有一個(gè) Veth 設(shè)備對(duì)。
2. 對(duì)網(wǎng)絡(luò)命名空間的操作
(1)對(duì)網(wǎng)絡(luò)命名空間的基本操作
對(duì)網(wǎng)絡(luò)命名空間的基本操作主要有:創(chuàng)建命名空間、查看命名空間列表、在指定命名空間執(zhí)行命令、進(jìn)入指定命名空間執(zhí)行命令。示例如下:
<1>創(chuàng)建命名空間 & 查看命名空間列表
<2>在指定命名空間執(zhí)行命令
<3>進(jìn)入指定命名空間執(zhí)行命令
(2)查看設(shè)備是否可以在命名空間之間轉(zhuǎn)移
我們可以在不同的網(wǎng)絡(luò)命名空間之間轉(zhuǎn)移設(shè)備。因?yàn)橐粋€(gè)設(shè)備只能屬于一個(gè)命名空間,所以轉(zhuǎn)移后在這個(gè)命名空間中就看不到這個(gè)設(shè)備了。
具體哪些設(shè)備能被轉(zhuǎn)移到不同的命名空間中呢?在設(shè)備里面有一個(gè)重要的屬性:NETIF_F_ ETNS_LOCAL,如果這個(gè)屬性為 on,就不能被轉(zhuǎn)移到其他命名空間中了。Veth 設(shè)備屬于可以轉(zhuǎn)移的設(shè)備,而很多其他設(shè)備如 lo 設(shè)備、 vxlan 設(shè)備、 ppp 設(shè)備、 bridge 設(shè)備等都是不可以轉(zhuǎn)移的。
二、Veth 設(shè)備對(duì)
引入 Veth 設(shè)備對(duì)是為了在不同的網(wǎng)絡(luò)命名空間之間通信,利用它可以直接將兩個(gè)網(wǎng)絡(luò)命名空間連接起來(lái)。由 Veth 設(shè)備的一端發(fā)送數(shù)據(jù)時(shí),它會(huì)將數(shù)據(jù)直接發(fā)送到另一端,并觸發(fā)另一端的接收操作。
Veth 設(shè)備對(duì)示意圖:
1. 對(duì) Veth 設(shè)備對(duì)的基本操作實(shí)踐
(1)創(chuàng)建 Veth 設(shè)備對(duì)
(2)將其中一個(gè) Veth 設(shè)備移動(dòng)到另一個(gè)網(wǎng)絡(luò)命名空間
(3)給 Veth 設(shè)備對(duì)分配 IP 地址,啟動(dòng),通信

在 Docker 內(nèi)部,Veth 設(shè)備對(duì)是連通容器與宿主機(jī)的主要網(wǎng)絡(luò)設(shè)備,離開(kāi)它是不行的。
2. Veth 設(shè)備對(duì)如何查看對(duì)端
先在當(dāng)前網(wǎng)絡(luò)命名空間查看 Veth 設(shè)備對(duì)端接口在設(shè)備列表中的序列號(hào),再到目標(biāo)網(wǎng)絡(luò)命名空間中查看該序列號(hào)代表什么設(shè)備:
三、路由
路由是指設(shè)備從一個(gè)接口上收到數(shù)據(jù)包,根據(jù)數(shù)據(jù)包的目的地址進(jìn)行重定向并轉(zhuǎn)發(fā)到另一個(gè)接口的過(guò)程。
Linux 系統(tǒng)包含一個(gè)完整的路由功能。當(dāng) IP 層在處理數(shù)據(jù)發(fā)送或者轉(zhuǎn)發(fā)時(shí),會(huì)使用由 IP 層維護(hù)的路由表來(lái)決定發(fā)往哪里。當(dāng)從網(wǎng)絡(luò)側(cè)接收到數(shù)據(jù)報(bào)文時(shí),IP 層首先會(huì)檢查報(bào)文的 IP 地址是否與主機(jī)自身的地址相同。如果數(shù)據(jù)報(bào)文中的 IP 地址是主機(jī)自身的地址,那么報(bào)文將被發(fā)送到傳輸層相應(yīng)的協(xié)議中。如果報(bào)文中的 IP 地址不是主機(jī)自身的地址,并且主機(jī)配置了路由功能,那么報(bào)文將被轉(zhuǎn)發(fā),否則報(bào)文將被丟棄。
1. 路由策略
策略路由是指按照用戶(hù)指定的策略進(jìn)行路由選擇,是一種比基于目標(biāo)網(wǎng)絡(luò)進(jìn)行路由更加靈活的數(shù)據(jù)包路由轉(zhuǎn)發(fā)機(jī)制,它使網(wǎng)絡(luò)管理員不僅能夠根據(jù)目的地址而且能夠根據(jù)報(bào)文大小、應(yīng)用或 IP 源地址等屬性來(lái)選擇轉(zhuǎn)發(fā)路徑。
查看當(dāng)前主機(jī)路由策略:
可以看到當(dāng)前有三條路由策略,其中 0、32766、32767 表示策略?xún)?yōu)先級(jí),數(shù)字越小優(yōu)先級(jí)越高,而 local、main、default 是路由表的名稱(chēng),還可以通過(guò) from 關(guān)鍵字設(shè)置指定來(lái)源的數(shù)據(jù)包查詢(xún)指定路由表,這里 from all 表示所有,不對(duì)來(lái)源地址進(jìn)行過(guò)濾。用戶(hù)還可以通過(guò) ip rule add/del 等命令對(duì)路由策略進(jìn)行增刪改查操作,但 local 是一個(gè)特殊的路由表,包含對(duì)于本地和廣播地址的高優(yōu)先級(jí)控制路由,rule 0 不能被刪除或者覆蓋。
當(dāng)主機(jī)收到數(shù)據(jù)包時(shí),會(huì)按照路由策略中設(shè)置的優(yōu)先級(jí)依次遍歷查詢(xún)路由表,直到?jīng)Q策出下一跳。所以,不在路由策略中的路由表,是不會(huì)被用于路由的。
2. 路由表
可以通過(guò)文件 /etc/iproute2/rt_tables 查看當(dāng)前系統(tǒng)中的路由表:
可以看到,linux 系統(tǒng)維護(hù)了 4 個(gè)路由表,序號(hào)分別為 0、253、254、255,其作用分別如下:
(1)0 unspec:系統(tǒng)保留表
(2)253 default:沒(méi)特別指定的默認(rèn)路由都放在該表
(3)254 main:沒(méi)指明路由表的所有路由都放在該表
(4)255 local:保存本地接口地址、廣播地址、NAT 地址,由系統(tǒng)維護(hù),用戶(hù)不得更改(LOCAL 表用于供 Linux 協(xié)議棧識(shí)別本地地址,以及進(jìn)行本地各個(gè)不同網(wǎng)絡(luò)接口之間的數(shù)據(jù)轉(zhuǎn)發(fā))
除此之外,用戶(hù)可以自定義序號(hào)為 1- 252 的路由表,手動(dòng)編輯文件 /etc/iproute2/rt_tables 即可,保存即生效。
路由查看:
在顯示的信息中,如果標(biāo)志 (Flags)是 U(代表 Up) ,則說(shuō)明該路由是有效的;如果標(biāo)志是 G(代表 Gateway),則說(shuō)明這個(gè)網(wǎng)絡(luò)接口連接的是網(wǎng)關(guān);如果標(biāo)志是 H(代表 Host ),則說(shuō)明目的地是主機(jī)而非網(wǎng)絡(luò)域,等等。
路由解析:以第 5 條路由為例,表示發(fā)送到網(wǎng)段 30.102.72.0/24 的數(shù)據(jù)包由本地接口 eth0 發(fā)送到下一個(gè)路由器 30.102.73.254(在這里也是網(wǎng)關(guān))。
默認(rèn)查看的是路由表 main 的路由,還可以通過(guò)指定路由表名稱(chēng)、路由類(lèi)型進(jìn)行查看:
用戶(hù)可以通過(guò) ip route add/del 等命令對(duì)路由進(jìn)行增刪改查。
3. 靜態(tài)路由與動(dòng)態(tài)路由
(1)靜態(tài)路由
靜態(tài)路由是指用戶(hù)或網(wǎng)絡(luò)管理員手工配置的路由信息。當(dāng)網(wǎng)絡(luò)的拓?fù)浣Y(jié)構(gòu)或鏈路狀態(tài)發(fā)生變化時(shí),網(wǎng)絡(luò)管理員需要手工去修改路由表中相關(guān)的靜態(tài)路由信息。
靜態(tài)路由信息在缺省情況下是私有的,不會(huì)傳遞給其他的路由器。
靜態(tài)路由一般適用于比較簡(jiǎn)單的網(wǎng)絡(luò)環(huán)境。
(2)動(dòng)態(tài)路由
動(dòng)態(tài)路由是指路由器能夠根據(jù)路由器之間交換的路由信息自動(dòng)地建立自己的路由表,并且能夠根據(jù)實(shí)際情況的變化適時(shí)地進(jìn)行調(diào)整。
當(dāng)網(wǎng)絡(luò)中節(jié)點(diǎn)或節(jié)點(diǎn)間的鏈路發(fā)生故障,或存在其他可用路由時(shí),動(dòng)態(tài)路由可用自行選擇最佳的可用路由并繼續(xù)轉(zhuǎn)發(fā)報(bào)文。
常見(jiàn)的動(dòng)態(tài)路由協(xié)議:RIP、OSPF、BGP 等。動(dòng)態(tài)路由發(fā)現(xiàn)協(xié)議一般使用組播功能來(lái)通過(guò)發(fā)送路由發(fā)現(xiàn)數(shù)據(jù),動(dòng)態(tài)地交換和獲取網(wǎng)絡(luò)的路由信息,并更新到路由表中。
四、網(wǎng)橋
Linux 可以支持多個(gè)不同的網(wǎng)絡(luò),它們之間能夠相互通信,如何將這些網(wǎng)絡(luò)連接起來(lái)并實(shí)現(xiàn)各網(wǎng)絡(luò)中主機(jī)的相互通信呢?可以用網(wǎng)橋。網(wǎng)橋是一個(gè)二層的虛擬網(wǎng)絡(luò)設(shè)備,把若干個(gè)網(wǎng)絡(luò)接口“連接”起來(lái),以使得網(wǎng)絡(luò)接口之間的報(bào)文能夠相互轉(zhuǎn)發(fā)。網(wǎng)橋能夠解析收發(fā)的報(bào)文,讀取目標(biāo) MAC 地址的信息,將其與自己記錄的 MAC 表結(jié)合,來(lái)決策報(bào)文的轉(zhuǎn)發(fā)目標(biāo)網(wǎng)絡(luò)接口。為了實(shí)現(xiàn)這些功能,網(wǎng)橋會(huì)學(xué)習(xí)源 MAC 地址(二層網(wǎng)橋轉(zhuǎn)發(fā)的依據(jù)就是 MAC 地址 )。在轉(zhuǎn)發(fā)報(bào)文時(shí),網(wǎng)橋只需向特定的網(wǎng)口進(jìn)行轉(zhuǎn)發(fā),來(lái)避免不必要的網(wǎng)絡(luò)交互。如果它遇到一個(gè)自己從未學(xué)習(xí)到的地址,就無(wú)法知道這個(gè)報(bào)文應(yīng)該向哪個(gè)網(wǎng)絡(luò)接口轉(zhuǎn)發(fā),將報(bào)文廣播給所有的網(wǎng)絡(luò)接口(報(bào)文來(lái)源的網(wǎng)絡(luò)接口除外)。
在實(shí)際的網(wǎng)絡(luò)中,網(wǎng)絡(luò)拓?fù)洳豢赡苡谰貌蛔儭TO(shè)備如果被移動(dòng)到另一個(gè)端口上,卻沒(méi)有發(fā)送任何數(shù)據(jù),網(wǎng)橋設(shè)備就無(wú)法感知這個(gè)變化,網(wǎng)橋還是向原來(lái)的端口轉(zhuǎn)發(fā)數(shù)據(jù)包,在這種情況下數(shù)據(jù)會(huì)丟失。所以網(wǎng)橋還要對(duì)學(xué)習(xí)到的 MAC 地址表加上超時(shí)時(shí)間(默認(rèn)為5min) 。如果網(wǎng)橋收到了對(duì)應(yīng)端口 MAC 地址回發(fā)的包,則重置超時(shí)時(shí)間,否則過(guò)了超時(shí)時(shí)間,就認(rèn)為設(shè)備已經(jīng)不在那個(gè)端口上了,它會(huì)重新廣播發(fā)送。
在 Linux 的內(nèi)部網(wǎng)絡(luò)棧里實(shí)現(xiàn)的網(wǎng)橋設(shè)備,作用和上面的描述相同。 Linux 主機(jī)過(guò)去一般只有一個(gè)網(wǎng)卡,現(xiàn)在多網(wǎng)卡的機(jī)器越來(lái)越多,而且有很多虛擬設(shè)備存在,所以 Linux 網(wǎng)橋提供了在這些設(shè)備之間相互轉(zhuǎn)發(fā)數(shù)據(jù)的二層設(shè)備。
1. Linux 網(wǎng)橋的實(shí)現(xiàn)
Linux 內(nèi)核是通過(guò)一個(gè)虛擬網(wǎng)橋設(shè)備 (Net Device) 來(lái)實(shí)現(xiàn)橋接的。這個(gè)虛擬設(shè)備可以綁定若干個(gè)以太網(wǎng)接口設(shè)備,從而將它們橋接起來(lái)。這種 Net Device 網(wǎng)橋和普通的設(shè)備不同,最明顯的一個(gè)特性是它還可以有一個(gè) IP 地址。
網(wǎng)橋的位置:
如圖所示,網(wǎng)橋設(shè)備 br0 綁定了 eth0 和 eth1。對(duì)于網(wǎng)絡(luò)協(xié)議棧的上層來(lái)說(shuō),只看得到 br0 就行。因?yàn)闃蚪邮窃跀?shù)據(jù)鏈路層實(shí)現(xiàn)的,上層不需要關(guān)心橋接的細(xì)節(jié),所以協(xié)議棧上層需要發(fā)送的報(bào)文被送到 br0,網(wǎng)橋設(shè)備的處理代碼判斷報(bào)文應(yīng)該被轉(zhuǎn)發(fā)到 eth0 還是 eth1,或者兩者應(yīng)該皆轉(zhuǎn)發(fā);反過(guò)來(lái),從 eth0 或從 eth1 接收到的報(bào)文被提交給網(wǎng)橋的處理代碼,在這里會(huì)判斷報(bào)文應(yīng)該被轉(zhuǎn)發(fā)、丟棄還是被提交到協(xié)議棧上層。
而有時(shí) eth0、eth1 也可能會(huì)作為報(bào)文的源地址或目的地址,直接參與報(bào)文的發(fā)送與接收,從而繞過(guò)網(wǎng)橋。
Linux 網(wǎng)橋常被用于虛擬機(jī)與容器之間的網(wǎng)絡(luò)聯(lián)通,相當(dāng)于一個(gè)虛擬機(jī)交換機(jī)的功能。
2. Linux 網(wǎng)橋的基本操作
(1)創(chuàng)建網(wǎng)橋
(2)將設(shè)備連接到網(wǎng)橋上
這里將 Veth 設(shè)備 veth0 連接到網(wǎng)橋 wjt-bridge 上,此時(shí) Veth 設(shè)備 veth0 成為網(wǎng)橋 wjt-bridge 的一個(gè)網(wǎng)口,在鏈路層工作,不再需要 IP 地址了,所以 veth0 上面的 IP 地址會(huì)失效:
兩邊都無(wú)法 ping 通了。
(3)為網(wǎng)橋配置 IP 地址
這時(shí) Veth 設(shè)備對(duì)兩端的 network namespace 還是無(wú)法通信:
(4)路由改動(dòng)
查看兩個(gè)命名空間當(dāng)前路由:
為當(dāng)前命名空間 10.1.1.0/24 網(wǎng)段刪除到 10.1.1.1 的路由,只留下到網(wǎng)橋 10.1.1.3 的路由:
為命名空間 wjt-net 添加到網(wǎng)橋 10.1.1.3 的默認(rèn)路由:
修改后路由如下:
再次通信測(cè)試:
通信成功。
五、netfilter 與 iptables
詳見(jiàn)《netfilter與iptables的基本原理 》
參考:
《Kubernetes 權(quán)威指南第 5 版》

浙公網(wǎng)安備 33010602011771號(hào)