dokcer0 網(wǎng)橋
在 Ubuntn 上安裝 docker 后,宿主機上默認被創(chuàng)建了一個名為 docker0 的網(wǎng)卡,其 IP 為 172.17.0.1/16:
有了這個網(wǎng)卡,宿主機還會在內(nèi)核的路由表中添加一條到達相應(yīng)網(wǎng)絡(luò)的靜態(tài)路由記錄:
這條路由信息表示所有目的 IP 為 172.17.0.0/16 的數(shù)據(jù)包都會從 docker0 網(wǎng)卡發(fā)出。接下來我們創(chuàng)建一個名為 mycon 的容器,并觀察其網(wǎng)絡(luò)配置:
在 mycon 容器內(nèi)可以看到兩塊網(wǎng)卡:lo 和 eth0。其中 lo 是容器的回環(huán)網(wǎng)卡,eth0 是容器與外界通信的網(wǎng)卡,eth0 的 IP 信息為 172.17.0.2/16,和宿主機上的網(wǎng)卡 bridge0 在同一網(wǎng)段中。查看 mycon 的路由信息:
mycon 容器的默認網(wǎng)關(guān)正是宿主機的 docker0 網(wǎng)卡。通過 ping 命令測試與外網(wǎng)的連通性,此時容器 mycon 是可以連通外網(wǎng)的,這就說明 mycon 的 eth0 網(wǎng)卡與宿主機的 docker0 網(wǎng)卡是連通的。
下面我們來查看宿主機的網(wǎng)絡(luò)設(shè)備:
發(fā)現(xiàn)多了一個以 "veth" 開頭的網(wǎng)卡,這是一個 veth 設(shè)備。而 veth 設(shè)備總是成對出現(xiàn)的,那么與 veth7537a16 配對的就應(yīng)該是 mycon 容器中的 eth0 了。既然 mycon 容器中的 eth0 是與 docker0 連通的,那么 veth7537a16 也應(yīng)該是與 docker0 連通的。因此docker0 并不是一個簡單的網(wǎng)卡設(shè)備,而是一個網(wǎng)橋!下圖展示了 docker bridge 網(wǎng)絡(luò)模式的拓撲圖:
事實上,docker 創(chuàng)建了 docker0 網(wǎng)橋,并以 veth pair 連接各個容器的網(wǎng)絡(luò),容器中的數(shù)據(jù)通過 eth0 發(fā)送到 docker0 網(wǎng)橋上,并由 docker0 網(wǎng)橋完成轉(zhuǎn)發(fā)。這里網(wǎng)橋的概念等同于交換機,為連在其上的設(shè)備轉(zhuǎn)發(fā)數(shù)據(jù)幀。網(wǎng)橋上的 veth 網(wǎng)卡設(shè)備相當于交換機上的端口,可以將多個容器連接在它們上面,這些端口工作在二層,所以是不需要配置 IP 信息的。上圖中的 docker0 網(wǎng)橋就為連在其上的容器轉(zhuǎn)發(fā)數(shù)據(jù)幀,使得同一臺宿主機上的 docker 容器之間可以相互通信。既然 docker0 是二層設(shè)備,那么它為什么還需要 IP 呢?其實,docker0 是一個普通的 linux 網(wǎng)橋,是可以為它配置 IP 的,我們可以認為它的內(nèi)部有一個可以用于配置 IP 的網(wǎng)卡。Docker0 的 IP 地址作為所連接的容器的默認網(wǎng)關(guān)地址!
docker0 網(wǎng)橋是在 docker daemon 啟動時自動創(chuàng)建的,其默認 IP 為 172.17.0.1/16,之后通過 bridge 驅(qū)動創(chuàng)建的容器都會在 docker0 的子網(wǎng)范圍內(nèi)選取一個未占用的 IP 使用,并連接到 docker0 網(wǎng)橋上。Docker daemon 提供了如下參數(shù)可以幫助用戶自定義 docker0 的設(shè)置。
--bip=CIDR:設(shè)置 docker0 的 IP 地址和子網(wǎng)范圍,使用 CIDR 格式,如 192.168.1.0/24。這個參數(shù)僅僅是配置 docker0 的,對用戶自定義的網(wǎng)橋無效。
--fixed-cidr=CIDR:限制 docker 容器可以獲取的 IP 地址范圍。Docker 容器默認可以獲取的 IP 范圍為 docker 網(wǎng)橋的整個子網(wǎng)范圍,此參數(shù)可以將其縮小到某個子網(wǎng)范圍內(nèi),所以這個參數(shù)必須在 docker 網(wǎng)橋的子網(wǎng)范圍內(nèi)。
--mtu=BYTES:指定 docker0 網(wǎng)橋的最大傳輸單元(MTU)。
除了使用 docker0 網(wǎng)橋外,用戶還可以使用自定義的網(wǎng)橋,然后通過 --bridge=BRIDGE 參數(shù)傳遞給 docker daemon。比如我們可以創(chuàng)建一個自定義網(wǎng)橋 br0:
$ sudo ip link add name br0 type bridge $ sudo ifconfig br0 188.18.0.1
然后在啟動 docker daemon 時設(shè)置參數(shù) --bridge=br0 即可。
iptables 規(guī)則
在安裝 docker 時,會默認在宿主機中添加一些 iptables 規(guī)則,用于 docker 容器之間已經(jīng)容器與外界的通信。我們可以通過 iptables-save 命令查看到 nat 表上 POSTROUTING 鏈上的有這么一條規(guī)則:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
這條規(guī)則關(guān)系著 docker 容器與外界的通信,其含義是將不是從網(wǎng)卡 docker0 發(fā)出的且源地址為 172.17.0.0/16 的數(shù)據(jù)包(容器中發(fā)出的數(shù)據(jù)包)做 SNAT。這樣一來,從 docker 容器中訪問外網(wǎng)的流量,在外部看來就是從宿主機上發(fā)出的,外部感覺不到 docker 容器的存在。
當外界想要訪問 docker 容器運行的服務(wù)時該怎么辦呢?接下來我們將啟動一個簡單的 web 服務(wù)器:
$ docker run -d -p 3000:3000 --name=myweb ljfpower/nodedemo
然后觀察 iptables 規(guī)則的變化:
$ sudo iptables-save … *nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 3000 -j DNAT --to-destination 172.17.0.3:3000 … *filter -A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 3000 -j ACCEPT …
可以看到,在 nat 表和 filter 表中的 DOCKER 鏈中分別增加來一條規(guī)則,這兩條規(guī)則將訪問宿主機 3000 端口的請求轉(zhuǎn)發(fā)到 172.17.0.3 的 3000 端口上(提供服務(wù)的 docker 容器的 IP 和端口),所以外界訪問 docker 容器是通過 iptables 做 DNAT 實現(xiàn)的。
Docker 默認的 forward 規(guī)則允許所有的外部 IP 訪問容器,我們可以通過在 filter 的 DOCKER 鏈上添加規(guī)則來對外部的 IP 訪問做出限制,比如只允許源 IP 為 192.168.21.212(筆者是在局域網(wǎng)內(nèi)演示的)的數(shù)據(jù)包訪問容器,添加的規(guī)則如下:
$ sudo iptables -I DOCKER -i docker0 ! -s 192.168.21.212 -j DROP
不僅僅是與外界通信,docker 容器之間相互通信也受到 iptables 規(guī)則的限制。同一臺宿主機上的 docker 容器默認都連在 docker0 網(wǎng)橋上,它們屬于同一個子網(wǎng),這是滿足通信的第一步。同時,docker daemon 會在 filter 表的 FORWARD 鏈中增加一條 ACCEPT 的規(guī)則(--icc=true):
-A FORWARD -i docker0 -o docker0 -j ACCEPT
這是滿足容器間相互通信的第二步。當 docker daemon 的啟動參數(shù) -icc(icc 參數(shù)表示是否允許容器間相互通信) 設(shè)置為 false 時,上面的規(guī)則被設(shè)置為 DROP,容器間的相互通信就被禁止了,這時如果想讓兩個容器通信就需要在 docker run 命令中使用 --link 選項。
在 docker 容器和外界的通信過程中,還涉及了數(shù)據(jù)包在多個網(wǎng)卡之間的轉(zhuǎn)發(fā)(比如從 docker0 網(wǎng)卡到宿主機 eth0 網(wǎng)卡),這需要內(nèi)核將 ip forward 功能打開,就是把內(nèi)核參數(shù) ip_forward 設(shè)置為 1。Docker daemon 在啟動的時候會執(zhí)行這個操作,我們可以通過下面的命令進行檢查:
$ cat /proc/sys/net/ipv4/ip_forward

返回的結(jié)果為 1,表示內(nèi)核的 ip forward 功能已經(jīng)打開。
容器的 DNS 和主機名(hostname)
使用同一個 docker 鏡像可以創(chuàng)建很多個 docker 容器,但是這些容器的 hostname 并不相同,也就是說 hostname 并沒有被寫入到鏡像中。實際上容器中的 /etc 目錄下有 3 個文件是在容器啟動后被虛擬文件覆蓋掉的,分別是 /etc/hostname、/etc/hosts 和 /etc/resolv.conf,通過在容器中運行 mount 命令可以看到它們:
# mount … /dev/mapper/ubuntu--vg-root on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/mapper/ubuntu--vg-root on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/mapper/ubuntu--vg-root on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered) …
這種方式既能解決主機名的問題,同時也能讓 DNS 及時更新(改變 resolv.conf)。由于這些文件的維護方法會隨著 docker 版本的升級而不斷變化,所以盡量不要修改這些文件,而是通過 docker 提供的相關(guān)參數(shù)進行設(shè)置,其參數(shù)配置方式如下。
-h HOSTNAME 或者 --hostname=HOSTNAME:設(shè)置容器的 hostname,此名稱會寫入到 /etc/hostname 和 /etc/hosts 文件中,也可以在容器的 bash 提示符中看到。
--dns=IP_ADDRESS…:為容器配置 DNS,會被寫入到 /etc/resolv.conf 文件中。
這兩個參數(shù)都是針對容器的需要在創(chuàng)建容器時進行設(shè)置。比如下面的 demo:
$ docker run -it --name mycon -h lion --dns=8.8.8.8 ubuntu:14.04
總結(jié)
本文主要通過演示 docker0 網(wǎng)橋相關(guān)的功能來探索 docker 網(wǎng)絡(luò)中的 bridge 驅(qū)動的實現(xiàn)機制。從本文中不難看出,linux 系統(tǒng)中,docker 的 bridge 驅(qū)動是依賴于系統(tǒng)的 ip forward 以及 iptables 等核心功能的。因此在學習 docker 的過程中,適當?shù)难a充 linux 相關(guān)的知識也是十分必要的!
審核編輯:黃飛
-
網(wǎng)橋
+關(guān)注
關(guān)注
0文章
139瀏覽量
17304 -
容器
+關(guān)注
關(guān)注
0文章
508瀏覽量
22397 -
Bridge
+關(guān)注
關(guān)注
0文章
15瀏覽量
12070 -
Docker
+關(guān)注
關(guān)注
0文章
513瀏覽量
12795
原文標題:拿捏Docker 網(wǎng)絡(luò) bridge 驅(qū)動
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
AUTOSAR架構(gòu)深度解析 精選資料推薦
AUTOSAR架構(gòu)深度解析 精選資料分享
Docker五種存儲驅(qū)動原理詳解

docker存儲驅(qū)動的工作原理
嵌入式Linux與物聯(lián)網(wǎng)軟件開發(fā)C語言內(nèi)核深度解析書籍的介紹

詳解docker的四種網(wǎng)絡(luò)模式
docker的4種網(wǎng)絡(luò)模式
Docker容器的四種網(wǎng)絡(luò)模式
Docker網(wǎng)絡(luò)的基本概念和原理與用法

評論