日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Docker容器网络

發(fā)布時(shí)間:2025/3/8 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Docker容器网络 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

Docker的技術(shù)依賴于Linux內(nèi)核的虛擬化技術(shù)的發(fā)展,Docker使用到的網(wǎng)絡(luò)技術(shù)有Network Namespace、Veth設(shè)備對(duì)、Iptables/Netfilter、網(wǎng)橋、路由等。接下來(lái),我將以Docker容器網(wǎng)絡(luò)實(shí)現(xiàn)的基礎(chǔ)技術(shù)來(lái)分別闡述,在到真正的容器篇章節(jié)之前,能形成一個(gè)穩(wěn)固的基礎(chǔ)知識(shí)網(wǎng)。

Network Namespace

為了支持網(wǎng)絡(luò)協(xié)議棧的多個(gè)實(shí)例,Linux在網(wǎng)絡(luò)棧引入了Network Namespace,這些獨(dú)立的協(xié)議棧被隔離到不同的Namespace中,處于不同Namespace中的網(wǎng)絡(luò)棧是完全隔離的,彼此無(wú)法通信。具體有關(guān)Linux Namespace的介紹,可以另行瀏覽之前寫(xiě)的《Linux Namespace》。

Linux的網(wǎng)絡(luò)協(xié)議棧十分復(fù)雜,為了支持獨(dú)立的協(xié)議棧,相關(guān)的全局變量都必須修改為協(xié)議棧私有。Linux實(shí)現(xiàn)Network Namespace的核心就是讓這些全局變量稱(chēng)為Network Namespace變量的成員,然后為協(xié)議棧的函數(shù)調(diào)用加入一個(gè)Namespace參數(shù)。與此同時(shí),為了保證已開(kāi)發(fā)程序及內(nèi)核代碼的兼容性,內(nèi)核代碼隱式地使用了Namespace空間內(nèi)的變量。應(yīng)用程序如果沒(méi)有對(duì)Namespace有特殊需求,那么不需要額外的代碼,Network Namespace對(duì)應(yīng)用程序而言是透明的。

在建立了新的Network Namespace,并將某個(gè)進(jìn)程關(guān)聯(lián)到這個(gè)網(wǎng)絡(luò)命名空間后,就出現(xiàn)了如下的命名空間下的內(nèi)核數(shù)據(jù)結(jié)構(gòu),所有網(wǎng)絡(luò)棧變量都放入了Network Namespace的數(shù)據(jù)結(jié)構(gòu)中,這個(gè)Network Namespace是屬于它進(jìn)程組私有的,與其他進(jìn)程組不沖突。

Docker正是利用了Network Namespace特性,實(shí)現(xiàn)了不同容器之間的網(wǎng)絡(luò)隔離。如果一個(gè)容器聲明使用宿主機(jī)的網(wǎng)絡(luò)棧(-net = host),即不開(kāi)啟Network Namespace,例如:

docker run –d –net=host --name c_name i_name

這種情況下,這個(gè)容器啟動(dòng)之后監(jiān)聽(tīng)的是宿主機(jī)的80端口。像這樣直接使用宿主機(jī)網(wǎng)絡(luò)棧的方式,雖然可以為容器提供良好的網(wǎng)絡(luò)性能,但也不可避免的造成端口沖突等網(wǎng)絡(luò)資源沖突的問(wèn)題。

所以在一般情況下,我們都希望程序引入Network Namespace里的網(wǎng)絡(luò)棧,即這個(gè)容器擁有自己的IP和端口。但是,這個(gè)時(shí)候也會(huì)帶來(lái)一個(gè)新的問(wèn)題,被隔離的容器進(jìn)程,是怎么與其它被隔離的進(jìn)程進(jìn)行網(wǎng)絡(luò)通信的?

Net Bridge

上文說(shuō)到,Linux 可以支持不同的網(wǎng)絡(luò),他們之間是怎么支持夠互相通信的呢?如果是兩臺(tái)主機(jī),那需要的可能只是一根網(wǎng)線,把它們連接在一臺(tái)交換機(jī)上。而在Linux當(dāng)中,網(wǎng)橋(Bridge)就起到相應(yīng)的作用。本質(zhì)上來(lái)說(shuō),這是一個(gè)數(shù)據(jù)鏈路層(data link)的設(shè)備,根據(jù)Mac地址的信息轉(zhuǎn)發(fā)到網(wǎng)橋的不同端口上。而Docker就是在宿主機(jī)上默認(rèn)創(chuàng)建一個(gè)docker0的網(wǎng)橋,凡是連接docker0的網(wǎng)橋,都可以用它來(lái)通信。

細(xì)述Bridge

網(wǎng)橋是一個(gè)二層的虛擬網(wǎng)絡(luò)設(shè)備,把若干個(gè)網(wǎng)絡(luò)接口“連接”起來(lái),使得網(wǎng)口之間的報(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)口。為了實(shí)現(xiàn)這些功能,網(wǎng)橋會(huì)學(xué)習(xí)源Mac地址。在轉(zhuǎn)發(fā)報(bào)文時(shí),網(wǎng)橋只需要向特定的端口轉(zhuǎn)發(fā),從而避免不必要的網(wǎng)絡(luò)交互。如果它遇到了一個(gè)自己從未學(xué)過(guò)的地址,就無(wú)法知道這個(gè)報(bào)文應(yīng)該向哪個(gè)網(wǎng)口轉(zhuǎn)發(fā),就將報(bào)文廣播給除了報(bào)文來(lái)源之外的所有網(wǎng)口。

在實(shí)際網(wǎng)絡(luò)中,網(wǎng)絡(luò)拓?fù)洳豢赡苡谰貌蛔儭H绻O(shè)備移動(dòng)到另一個(gè)端口上,而它沒(méi)有發(fā)送任何數(shù)據(jù),那么網(wǎng)橋設(shè)備就無(wú)法感知到這個(gè)變化,結(jié)果網(wǎng)橋還是向原來(lái)的端口發(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)為哪個(gè)設(shè)備不在那個(gè)端口上了,他就會(huì)廣播重發(fā)。

Linux為了支持越來(lái)越多的網(wǎng)卡以及虛擬設(shè)備,所以使用網(wǎng)橋去提供這些設(shè)備之間轉(zhuǎn)發(fā)數(shù)據(jù)的二層設(shè)備。Linux內(nèi)核支持網(wǎng)口的橋接(以太網(wǎng)接口),這與單純的交換機(jī)還是不太一樣,交換機(jī)僅僅是一個(gè)二層設(shè)備,對(duì)于接受到的報(bào)文,要么轉(zhuǎn)發(fā),要么丟棄。運(yùn)行著Linux內(nèi)核的機(jī)器本身就是一臺(tái)主機(jī),有可能是網(wǎng)絡(luò)報(bào)文的目的地,其收到的報(bào)文要么轉(zhuǎn)發(fā),要么丟棄,還可能被送到網(wǎng)絡(luò)協(xié)議的網(wǎng)絡(luò)層,從而被自己主機(jī)本身的協(xié)議棧消化,所以我們可以把網(wǎng)橋看作一個(gè)二層設(shè)備,也可以看做是一個(gè)三層設(shè)備。

Linux中Bridge實(shí)現(xiàn)

Linux內(nèi)核是通過(guò)一個(gè)虛擬的網(wǎng)橋設(shè)備(Net Device)來(lái)實(shí)現(xiàn)橋接的。這個(gè)虛擬設(shè)備可以綁定若干個(gè)以太網(wǎng)接口,從而將它們連接起來(lái)。Net Device網(wǎng)橋和普通的設(shè)備不同,最明顯的是它還可以有一個(gè)ip地址。

如上圖所示,網(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)文被轉(zhuǎn)發(fā)到eth0還是eth1,或者兩者皆轉(zhuǎn)發(fā)。反之,從eth0或者從eth1接收到的報(bào)文被提交給網(wǎng)橋的處理代碼,在這里判斷報(bào)文應(yīng)該被轉(zhuǎn)發(fā)、丟棄或者提交到協(xié)議棧上層。

而有時(shí)eth0、eth1也可能會(huì)作為報(bào)文的源地址或目的地址,直接參與報(bào)文的發(fā)送和接收,從而繞過(guò)網(wǎng)橋。

Bridge常用操作

Docker自動(dòng)完成了對(duì)網(wǎng)橋的創(chuàng)建和維護(hù)。如果想要進(jìn)一步理解網(wǎng)橋,可以看下如下舉的一些常用操作命令。

新增一個(gè)網(wǎng)橋:

brctl addbr xxxxx

在新增網(wǎng)橋的基礎(chǔ)上增加網(wǎng)口,在linux中,一個(gè)網(wǎng)口其實(shí)就是一個(gè)物理網(wǎng)卡。將物理網(wǎng)卡和網(wǎng)橋連接起來(lái):

brctl addif xxxx ethx

網(wǎng)橋的物理網(wǎng)卡作為一個(gè)網(wǎng)口,由于在鏈路層工作,就不再需要IP地址了,這樣上面的IP地址自然失效:

ipconfig ethx 0.0.0.0

給網(wǎng)橋配置一個(gè)IP地址:

ipconfig brxxx xxx.xxx.xxx.xxx

這樣網(wǎng)橋就是一個(gè)有了IP地址,而連接在這之上的網(wǎng)卡就是一個(gè)純鏈路層設(shè)備了。

Veth Pair

上文說(shuō)到,docker在宿主機(jī)上創(chuàng)建docker0網(wǎng)橋后,凡是連接到docker0上的網(wǎng)橋,就可以用它來(lái)通信。那么這里又有個(gè)問(wèn)題,就是這些容器是如何連接到docker0網(wǎng)橋上的?所以這就是Veth Pair虛擬設(shè)備的作用了,Veth Pair就是為了在不同的Network Namespace之間進(jìn)行通信,利用它,可以將兩個(gè)Network Namespace連接起來(lái)。

Veth Pair設(shè)備的特點(diǎn)是:它被創(chuàng)建出來(lái)后,總是以兩張?zhí)摂M網(wǎng)卡(Veth Peer)的形式出現(xiàn)。并且,其中一個(gè)網(wǎng)卡發(fā)出的數(shù)據(jù)包,可以直接出現(xiàn)在另一張“網(wǎng)卡”上,哪怕這兩張網(wǎng)卡在不同的Network Namespace中。

正是因?yàn)檫@樣的特點(diǎn),Veth Pair成對(duì)出現(xiàn),很像是一對(duì)以太網(wǎng)卡,常常被看做是不同Network Namespace直連的“網(wǎng)線”。在Veth一端發(fā)送數(shù)據(jù)時(shí),他會(huì)將數(shù)據(jù)發(fā)送到另一端并觸發(fā)另一端的接收操作。我們可以把Veth Pair其中一端看做另一端的一個(gè)Peer。

Veth Pair操作命令

創(chuàng)建Veth Pair:

ip link add veth0 type veth peer name veth1

創(chuàng)建后查看Veth Pair的信息:

ip link show

將其中一個(gè)Veth Peer設(shè)置到另一個(gè)Namespace:

ip link set veth1 netns netns1

在netns1中查看veth1設(shè)備:

ip netns exec netns1 ip link show

當(dāng)然,在docker里面,除了將Veth放入容器,還改名為eth0。想要通信必須先分配IP地址:

ip netns exec netns1 ip addr add 10.1.1.1/24 dev veth1ip addr add 10.1.1.2/24 dev veth0

啟動(dòng)它們:

ip netns exec netns1 ip link set dev veth1 upip link set dev veth0 up

測(cè)試通信:

ip netns exec netns1 ping 10.1.1.2

Veth Pair查看端對(duì)端

在實(shí)際操作Veth Pair時(shí),可以使用ethtool便于操作。

在一個(gè)Namespace中查看Veth Pair接口在設(shè)備列表中的序列號(hào):

ip netns exec netns1 ethtool -S veth1

如果得知另一端的接口設(shè)備序列號(hào),假如序列號(hào)為6,則可以繼續(xù)查看6代表了什么設(shè)備:

ip netns exec netns2 ip link | grep 6

Iptables/Netfilter

Linux協(xié)議棧非常高效且復(fù)雜。如果我們想要在數(shù)據(jù)處理過(guò)程中對(duì)關(guān)心的數(shù)據(jù)進(jìn)行一些操作,則需要Linux提供一套相應(yīng)的機(jī)制幫助用戶實(shí)現(xiàn)自定義的數(shù)據(jù)包處理過(guò)程。

在Linux網(wǎng)絡(luò)協(xié)議棧有一組網(wǎng)絡(luò)回調(diào)函數(shù)掛接點(diǎn),通過(guò)這些掛接點(diǎn)函數(shù)掛接的鉤子函數(shù)可以在Linux網(wǎng)絡(luò)棧處理數(shù)據(jù)包的過(guò)程中對(duì)數(shù)據(jù)包一些操作,例如過(guò)濾、修改、丟棄等。整個(gè)掛接點(diǎn)技術(shù)叫做Iptables和Netfilter。

Netfilter負(fù)責(zé)在內(nèi)核中執(zhí)行各種各樣的掛接規(guī)則,運(yùn)行在內(nèi)核模式中。而Iptables是在用戶模式下運(yùn)行的進(jìn)程,負(fù)責(zé)協(xié)助維護(hù)內(nèi)核中Netfilter的各種規(guī)則表。通過(guò)二者的配合來(lái)實(shí)現(xiàn)整個(gè)Linux網(wǎng)絡(luò)協(xié)議棧中靈活的數(shù)據(jù)包處理機(jī)制。

規(guī)則表Table

這些掛載點(diǎn)能掛接的規(guī)則也分不同的類(lèi)型,目前主要支持的Table類(lèi)型如下:

?RAW?MANGLE?NAT?FILTER

上述4個(gè)規(guī)則鏈的優(yōu)先級(jí)是RAW最高,FILTER最低。

在實(shí)際應(yīng)用中,不同掛接點(diǎn)需要的規(guī)則類(lèi)型通常不同。例如,在Input的掛接點(diǎn)上明顯不需要FILTER的過(guò)濾規(guī)則,因?yàn)楦鶕?jù)目標(biāo)地址,已經(jīng)在本機(jī)的上層協(xié)議棧了,所以無(wú)需再掛載FILTER過(guò)濾規(guī)則。

Route

Linux系統(tǒng)包含了一個(gè)完整的路由功能。當(dāng)IP層在處理數(shù)據(jù)發(fā)送或者轉(zhuǎn)發(fā)時(shí),會(huì)使用路由表來(lái)決定發(fā)往哪里。通常情況下,如果主機(jī)與目的主機(jī)直接相連,那么主機(jī)可以直接發(fā)送IP報(bào)文到目的主機(jī)。

路由功能是由IP層維護(hù)的一張路由表來(lái)實(shí)現(xiàn)。當(dāng)主機(jī)收到數(shù)據(jù)報(bào)文時(shí),它用此表來(lái)決策接下來(lái)應(yīng)該做什么操作。當(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ī)自身的地址,并且配置了路由功能,那么報(bào)文將被轉(zhuǎn)發(fā),否則報(bào)文將被丟棄。

路由表的數(shù)據(jù)一般以條目形式存在,一個(gè)典型的路由表?xiàng)l目通常包含以下主要的條目項(xiàng):

?目的IP地址?下一個(gè)路由器的IP地址?標(biāo)志?網(wǎng)絡(luò)接口規(guī)范

通過(guò)路由表轉(zhuǎn)發(fā)時(shí),如果任何條目的第一個(gè)字段完全匹配目標(biāo)條目的IP地址(主機(jī))或部分匹配(網(wǎng)絡(luò)),那么它將指示下一個(gè)路由器的IP地址。這些信息將告訴主機(jī)數(shù)據(jù)包該轉(zhuǎn)發(fā)到哪一個(gè)“下一個(gè)路由器”。而條目中所有其它字段將提供更多的輔助信息來(lái)為路由轉(zhuǎn)發(fā)做決定。

如果沒(méi)有一個(gè)完全匹配的IP,則繼續(xù)搜索網(wǎng)絡(luò)ID。找到則轉(zhuǎn)發(fā)數(shù)據(jù)到指定路由器上。由此可知,網(wǎng)絡(luò)上所有主機(jī)都是通過(guò)這個(gè)路由表中的單個(gè)條目進(jìn)行管理。

如果上述兩個(gè)條件都不匹配,則將數(shù)據(jù)報(bào)文轉(zhuǎn)發(fā)到一個(gè)默認(rèn)路由器上。

如果上述步驟失敗,默認(rèn)路由器也不存在,那么這個(gè)數(shù)據(jù)報(bào)文無(wú)法轉(zhuǎn)發(fā)。任何無(wú)法投遞的數(shù)據(jù)都會(huì)產(chǎn)生一個(gè)ICMP主機(jī)不可達(dá)或者ICMP網(wǎng)絡(luò)不可達(dá)的錯(cuò)誤,并將該錯(cuò)誤返回給生成此數(shù)據(jù)的應(yīng)用程序。

Route Table

Linux路由表至少2個(gè),一個(gè)是LOCAL,一個(gè)是MAIN。

Local表用于供Linux協(xié)議棧識(shí)別本地地址,以及進(jìn)行本地各個(gè)不同網(wǎng)絡(luò)之間的數(shù)據(jù)轉(zhuǎn)發(fā)。MAIN表用于各類(lèi)網(wǎng)絡(luò)IP的轉(zhuǎn)發(fā)。它的建立既可以使用靜態(tài)配置生成,也可以使用動(dòng)態(tài)路由發(fā)現(xiàn)協(xié)議生成。動(dòng)態(tài)路由發(fā)現(xiàn)協(xié)議一般使用組播功能來(lái)通過(guò)發(fā)送路由發(fā)現(xiàn)數(shù)據(jù),動(dòng)態(tài)獲取和交換網(wǎng)絡(luò)的路由信息,并更新到路由表中。

通過(guò)下列命令查看LOCAL表的內(nèi)容:

ip route show table local type local

路由表的查看:

ip route list

管中窺豹

我們?cè)趫?zhí)行docker run -d --name xxx之后,進(jìn)入容器內(nèi)部:

## docker ps 可查看所有docker## 進(jìn)入容器docker exec -it 228ae947b20e /bin/bash

并執(zhí)行 ifconfig:

$ ifconfigeth0 Link encap:Ethernet HWaddr 22:A4:C8:79:DD:1A inet addr:192.168.65.28 Bcast:0.0.0.0 Mask:255.255.255.255 UP BROADCAST RUNNING MULTICAST MTU:1440 Metric:1 RX packets:2231528 errors:0 dropped:0 overruns:0 frame:0 TX packets:3340914 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:249385222 (237.8 MiB) TX bytes:590701793 (563.3 MiB)lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

我們看到一張叫eth0的網(wǎng)卡,它正是一個(gè)Veth Pair設(shè)備在容器的這一端。

我們?cè)偻ㄟ^(guò) route 查看該容器的路由表:

$ routeKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Ifacedefault 169.254.1.1 0.0.0.0 UG 0 0 0 eth0169.254.1.1 * 255.255.255.255 UH 0 0 0 eth0

我們可以看到這個(gè)eth0是這個(gè)容器的默認(rèn)路由設(shè)備。我們也可以通過(guò)第二條路由規(guī)則,看到所有對(duì) 169.254.1.1/16 網(wǎng)段的請(qǐng)求都會(huì)交由eth0來(lái)處理。

而Veth Pair 設(shè)備的另一端,則在宿主機(jī)上,我們同樣也可以通過(guò)查看宿主機(jī)的網(wǎng)絡(luò)設(shè)備來(lái)查看它:

$ ifconfig......eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.16.241.192 netmask 255.255.240.0 broadcast 172.16.255.255 ether 00:16:3e:0a:f3:75 txqueuelen 1000 (Ethernet) RX packets 3168620550 bytes 727592674740 (677.6 GiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2937180637 bytes 8661914052727 (7.8 TiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0......docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:16:58:92:43 txqueuelen 0 (Ethernet) 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......vethd08be47: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 ether 16:37:8d:fe:36:eb txqueuelen 0 (Ethernet) RX packets 193 bytes 22658 (22.1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 134 bytes 23655 (23.1 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0......

在宿主機(jī)上,容器對(duì)應(yīng)的Veth Pair設(shè)備是一張?zhí)摂M網(wǎng)卡,我們?cè)儆胋rctl show命令查看網(wǎng)橋:

$ brctl showbridge name bridge id STP enabled interfacesdocker0 8000.0242afb1a841 no vethd08be47

可以清楚的看到Veth Pair的一端 vethd08be47 就插在 docker0 上。

我現(xiàn)在執(zhí)行docker run 啟動(dòng)兩個(gè)容器,就會(huì)發(fā)現(xiàn)docker0上插入兩個(gè)容器的 Veth Pair的一端。如果我們?cè)谝粋€(gè)容器內(nèi)部互相ping另外一個(gè)容器的IP地址,是不是也能ping通?

$ brctl showbridge name bridge id STP enabled interfacesdocker0 8000.0242afb1a841 no veth26cf2cc veth8762ad2

容器1:

$ docker exec -it f8014a4d34d0 /bin/bash$ ifconfigeth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03 inet addr:172.17.0.3 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:76 errors:0 dropped:0 overruns:0 frame:0 TX packets:106 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:16481 (16.0 KiB) TX bytes:14711 (14.3 KiB)lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:48 errors:0 dropped:0 overruns:0 frame:0 TX packets:48 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2400 (2.3 KiB) TX bytes:2400 (2.3 KiB)$ routeKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Ifacedefault 172.17.0.1 0.0.0.0 UG 0 0 0 eth0172.17.0.0 * 255.255.0.0 U 0 0 0 eth0

容器2:

$ docker exec -it 9a6f38076c04 /bin/bash$ ifconfigeth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:133 errors:0 dropped:0 overruns:0 frame:0 TX packets:193 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:23423 (22.8 KiB) TX bytes:22624 (22.0 KiB)lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:198 errors:0 dropped:0 overruns:0 frame:0 TX packets:198 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:9900 (9.6 KiB) TX bytes:9900 (9.6 KiB)$ routeKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Ifacedefault 172.17.0.1 0.0.0.0 UG 0 0 0 eth0172.17.0.0 * 255.255.0.0 U 0 0 0 eth0

從一個(gè)容器ping另外一個(gè)容器:

# -> 容器1內(nèi)部 ping 容器2$ ping 172.17.0.3PING 172.17.0.3 (172.17.0.3): 56 data bytes64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.142 ms64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.096 ms64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.089 ms

我們看到,在一個(gè)容器內(nèi)部ping另外一個(gè)容器的ip,是可以ping通的。也就意味著,這兩個(gè)容器是可以互相通信的。

容器通信

我們不妨結(jié)合前文時(shí)所說(shuō)的,理解下為什么一個(gè)容器能訪問(wèn)另一個(gè)容器?先簡(jiǎn)單看如一幅圖:

當(dāng)在容器1里訪問(wèn)容器2的地址,這個(gè)時(shí)候目的IP地址會(huì)匹配到容器1的第二條路由規(guī)則,這條路由規(guī)則的Gateway是0.0.0.0,意味著這是一條直連規(guī)則,也就是說(shuō)凡是匹配到這個(gè)路由規(guī)則的請(qǐng)求,會(huì)直接通過(guò)eth0網(wǎng)卡,通過(guò)二層網(wǎng)絡(luò)發(fā)往目的主機(jī)。而要通過(guò)二層網(wǎng)絡(luò)到達(dá)容器2,就需要127.17.0.3對(duì)應(yīng)的MAC地址。所以,容器1的網(wǎng)絡(luò)協(xié)議棧就需要通過(guò)eth0網(wǎng)卡來(lái)發(fā)送一個(gè)ARP廣播,通過(guò)IP找到MAC地址。

所謂ARP(Address Resolution Protocol),就是通過(guò)三層IP地址找到二層的MAC地址的協(xié)議。這里說(shuō)到的eth0,就是Veth Pair的一端,另一端則插在了宿主機(jī)的docker0網(wǎng)橋上。eth0這樣的虛擬網(wǎng)卡插在docker0上,也就意味著eth0變成docker0網(wǎng)橋的“從設(shè)備”。從設(shè)備會(huì)降級(jí)成docker0設(shè)備的端口,而調(diào)用網(wǎng)絡(luò)協(xié)議棧處理數(shù)據(jù)包的資格全部交給docker0網(wǎng)橋。

所以,在收到ARP請(qǐng)求之后,docker0就會(huì)扮演二層交換機(jī)的角色,把ARP廣播發(fā)給其它插在docker0網(wǎng)橋的虛擬網(wǎng)卡上,這樣,127.17.0.3就會(huì)收到這個(gè)廣播,并把其MAC地址返回給容器1。有了這個(gè)MAC地址,容器1的eth0的網(wǎng)卡就可以把數(shù)據(jù)包發(fā)送出去。這個(gè)數(shù)據(jù)包會(huì)經(jīng)過(guò)Veth Pair在宿主機(jī)的另一端veth26cf2cc,直接交給docker0。

docker0轉(zhuǎn)發(fā)的過(guò)程,就是繼續(xù)扮演二層交換機(jī),docker0根據(jù)數(shù)據(jù)包的目標(biāo)MAC地址,在CAM表查到對(duì)應(yīng)的端口為veth8762ad2,然后把數(shù)據(jù)包發(fā)往這個(gè)端口。而這個(gè)端口,就是容器2的Veth Pair在宿主機(jī)的另一端,這樣,數(shù)據(jù)包就進(jìn)入了容器2的Network Namespace,最終容器2將響應(yīng)(Ping)返回給容器1。在真實(shí)的數(shù)據(jù)傳遞中,Linux內(nèi)核Netfilter/Iptables也會(huì)參與其中,這里不再贅述。

CAM就是交換機(jī)通過(guò)MAC地址學(xué)習(xí)維護(hù)端口和MAC地址的對(duì)應(yīng)表

這里介紹的容器間的通信方式就是docker中最常見(jiàn)的bridge模式,當(dāng)然此外還有host模式、container模式、none模式等,對(duì)其它模式有興趣的可以去閱讀相關(guān)資料。

跨主通信

好了,這里不禁問(wèn)個(gè)問(wèn)題,到目前為止只是單主機(jī)內(nèi)部的容器間通信,那跨主機(jī)網(wǎng)絡(luò)呢?在Docker默認(rèn)配置下,一臺(tái)宿主機(jī)的docker0網(wǎng)橋是無(wú)法和其它宿主機(jī)連通的,它們之間沒(méi)有任何關(guān)聯(lián),所以這些網(wǎng)橋上的容器,自然就沒(méi)辦法多主機(jī)之間互相通信。但是無(wú)論怎么變化,道理都是一樣的,如果我們創(chuàng)建一個(gè)公共的網(wǎng)橋,是不是集群中所有容器都可以通過(guò)這個(gè)公共網(wǎng)橋去連接?

當(dāng)然在正常的情況下,節(jié)點(diǎn)與節(jié)點(diǎn)的通信往往可以通過(guò)NAT的方式,但是,這個(gè)在互聯(lián)網(wǎng)發(fā)展的今天,在容器化環(huán)境下未必適用。例如在向注冊(cè)中心注冊(cè)實(shí)例的時(shí)候,肯定會(huì)攜帶IP,在正常物理機(jī)內(nèi)的應(yīng)用當(dāng)然沒(méi)有問(wèn)題,但是容器化環(huán)境卻未必,容器內(nèi)的IP很可能就是上文所說(shuō)的172.17.0.2,多個(gè)節(jié)點(diǎn)都會(huì)存在這個(gè)IP,大概率這個(gè)IP是沖突的。

如果我們想避免這個(gè)問(wèn)題,就會(huì)攜帶宿主機(jī)的IP和映射的端口去注冊(cè)。但是這又帶來(lái)一個(gè)問(wèn)題,即容器內(nèi)的應(yīng)用去意識(shí)到這是一個(gè)容器,而非物理機(jī),當(dāng)在容器內(nèi),應(yīng)用需要去拿容器所在的物理機(jī)的IP,當(dāng)在容器外,應(yīng)用需要去拿當(dāng)前物理機(jī)的IP。顯然,這并不是一個(gè)很好的設(shè)計(jì),這需要應(yīng)用去配合配置。所以,基于此,我們肯定要尋找其他的容器網(wǎng)絡(luò)解決方案。

在上圖這種容器網(wǎng)絡(luò)中,我們需要在我們已有的主機(jī)網(wǎng)絡(luò)上,通過(guò)軟件構(gòu)建一個(gè)覆蓋在多個(gè)主機(jī)之上,且能把所有容器連通的虛擬網(wǎng)絡(luò)。這種就是Overlay Network(覆蓋網(wǎng)絡(luò))。

?

?

總結(jié)

以上是生活随笔為你收集整理的Docker容器网络的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。