本文用到的環(huán)境如下:
host: centos7
docker: 通過yum install -y docker安裝乡翅,版本號為1.10.3
docker鏡像:
# Version: 0.0.1 FROM ubuntu:latest MAINTAINER paul liu "pollux.liu@msn.com" RUN apt-get update RUN apt-get install -y net-tools RUN apt-get install -y iputils-ping CMD /bin/bash
場景圖:
我的host主機接有無線路由器,通過ADSL撥號上網(wǎng)跋核,網(wǎng)卡eth0固定IP為192.168.0.200粥庄,網(wǎng)關(guān)為路由器的IP 192.168.0.1。
在host上安裝docker豺妓,并運行容器惜互。
docker0的作用
通過以下命令安裝docker,
yum install -y docker
啟用docker琳拭,
systemctl start docker
然后在host主機運行ifconfig
或ip a
命令训堆,可以看到除去host原有的網(wǎng)卡eth0和回環(huán)lo外,多了個docker0白嘁。
docker0 IP為172.17.0.1坑鱼,所在的網(wǎng)段默認為B類私網(wǎng)地址172.17.0.0/16。可以將docker0看做是host主機的一塊虛擬網(wǎng)卡鲁沥。這樣host主機就等同于配置了雙網(wǎng)卡呼股,兩塊網(wǎng)卡之間可以通信,但前提是啟用ip_forward画恰。
這是docker0的第一個身份彭谁。
運行兩個容器docker1,docker2允扇,然后在host主機上運行brctl show
查看缠局,
這里可以看出docker0的第二個身份,一個虛擬交換機考润。每運行一個容器狭园,就會產(chǎn)生一對veth,其中一端連接到docker0上糊治,另一端連接到容器的eth0上唱矛。這樣,所有連接到docker0的容器組成了一個局域網(wǎng)俊戳。如下圖:
在host主機上運行ifconfig
揖赴,也會發(fā)現(xiàn)多了兩個veth這樣的網(wǎng)絡(luò)接口。
在host主機上運行ip addr show veth6d9a691
抑胎,可以查看到該veth具有mac地址燥滑,這也正說明了docker0的虛擬交換機的身份,交換機是通過mac地址通信的阿逃,連接到交換機的設(shè)備必須具有mac地址铭拧。
由于docker0自身也具有mac地址,這個與純二層交換機是不同的恃锉,并且綁定了IP 172.17.0.1搀菩,容器默認把docker0作為了網(wǎng)關(guān)。也就是docker0還兼具路由的功能破托,因此可以把docker0看做是一個三層交換機肪跋,可以做二層數(shù)據(jù)包轉(zhuǎn)發(fā),也可以做三層路由轉(zhuǎn)發(fā)土砂。
在容器中運行route -n
查看路由如下:
在host主機上運行route -n
查看路由如下:
在host中州既,訪問本網(wǎng)段192.168.0.0是通過eth0轉(zhuǎn)發(fā)數(shù)據(jù)包的,訪問172.17.0.0網(wǎng)段是通過docker0轉(zhuǎn)發(fā)數(shù)據(jù)包的萝映,而對于其他如公網(wǎng)是通過eth0將數(shù)據(jù)包轉(zhuǎn)發(fā)給網(wǎng)關(guān)192.168.0.1吴叶,再由該網(wǎng)關(guān)進行數(shù)據(jù)包轉(zhuǎn)發(fā)的,比如上網(wǎng)序臂。
容器是如何連接到外部網(wǎng)絡(luò)的
在容器中運行ping sohu.com
或ping 192.168.0.200
都可以ping通蚌卤。
默認情況下,不需要再額外做任何配置,在一臺host主機上逊彭,通過docker0咸灿,各容器之間可以互通,并且可以通過host的eth0連接外網(wǎng)诫龙。
通俗的講析显,通過docker0組成了一個網(wǎng)段為172.17.0.0/16的以太網(wǎng),docker容器發(fā)起請求時签赃,如果是相同網(wǎng)段則經(jīng)由docker0轉(zhuǎn)發(fā)到目標機器谷异,如果是不同網(wǎng)段,則經(jīng)由docker0锦聊,轉(zhuǎn)發(fā)到host的另一塊網(wǎng)卡eth0上歹嘹,由eth0負責下一步的數(shù)據(jù)包轉(zhuǎn)發(fā),比如公網(wǎng)地址孔庭。
下面進一步分析一下報文是怎么發(fā)送到外面的尺上。
容器內(nèi)部發(fā)送一條公網(wǎng)請求報文,通過eth0圆到,在veth被接收怎抛。此時報文已經(jīng)來到了主機上,通過查詢主機的路由表(route -n
)芽淡,如果發(fā)現(xiàn)報文應(yīng)該通過主機的eth0马绝,從默認網(wǎng)關(guān)發(fā)送出去,那么報文就被從docker0轉(zhuǎn)發(fā)給主機的eth0挣菲,但前提是首先啟用ip_forward功能富稻,才能在host主機的docker0和eth0兩個網(wǎng)卡間傳遞數(shù)據(jù)包。
由于目標地址并不屬于host主機所在網(wǎng)段白胀,那么會匹配機器上的 iptables中的nat表POSTROUTING鏈中的規(guī)則椭赋。
在host主機運行命令iptables -L -n -t nat --line-numbers
,查看nat表或杠,這里只看POSTROUTING鏈:
第一行中說明哪怔,對于源地址為172.17.0.0/16網(wǎng)段的數(shù)據(jù)包,發(fā)出去之前通過MQSQUERADE偽裝向抢。linux內(nèi)核會修改數(shù)據(jù)包源地址為host主機eth0的地址(也就是192.168.0.200)认境,然后把報文轉(zhuǎn)發(fā)出去。對于外部來說笋额,報文是從主機eth0發(fā)送出去的。
局域網(wǎng)內(nèi)的機器由于都是私有IP篷扩,是無法直接訪問互聯(lián)網(wǎng)的(數(shù)據(jù)包可以發(fā)出去兄猩,但回不來。)如果要上網(wǎng),除了可以通過硬件路由器枢冤,也可以通過軟件路由鸠姨,在iptables的nat表中的POSTROUTING鏈中添加SNAT規(guī)則。
測試一下淹真,在host主機運行命令iptables -t nat -D POSTROUTING 1
將第一條規(guī)則刪掉讶迁,那么在容器中就運行命令ping sohu.com
就ping不通了。但仍然可以ping通host主機核蘸。
在host主機運行命令以下命令恢復(fù):
iptables -t nat -I POSTROUTING -s 172.17.0.0/16 -o eth0 -j SNAT --to-source 192.168.0.200
或者
iptables -t nat -I POSTROUTING -s 172.17.0.0/16 -j MASQUERADE
關(guān)于SNAT和MASQUERADE巍糯,這篇文章已經(jīng)有過描述,可以參考:Docker前傳之linux iptables
外部網(wǎng)絡(luò)如何訪問容器提供的服務(wù)
新建一Dockerfile客扎,用以運行nginx容器:
# Version: 0.0.1 FROM paulliu/ubuntu_ip RUN apt-get install -y nginx EXPOSE 80
在host主機運行構(gòu)建命令構(gòu)建鏡像docker build -t paulliu/nginx .
在host主機運行容器啟動命令docker run -d -p 80 --name nginx1 paulliu/nginx nginx -g "daemon off;"
在host主機查看容器的端口映射docker port nginx1 80
在host主機運行命令iptables -nat -L -n
可以看到在PREROUTING鏈中多了以下DNAT規(guī)則:
也就是在容器啟動時通過-p 80
將host主機192.168.0.200:32773映射為容器172.17.0.4:80祟峦。
注意:docker容器每次啟動時獲取的IP地址未必是一樣的,而且-p 80
是在host主機上隨機選擇一個端口號進行映射徙鱼,每次啟動的端口號也未必是一樣的宅楞。但iptables中相關(guān)的規(guī)則是自動變更的。
在host主機運行curl localhost:32773
或者在其他主機運行curl 192.168.0.200:32773
結(jié)果如下: