當 Docker 啟動時,會自動在主機上創建一個 docker0 虛擬網橋,實際上是 Linux 的一個 bridge,可以理解為一個軟件交換機。它會在掛載到它的網口之間進行轉發。
同時,Docker 隨機分配一個本地未占用的私有網段(在 RFC1918 中定義)中的一個地址給 docker0 接口。比如典型的 172.17.42.1 ,掩碼為 255.255.0.0 。此后啟動的容器內的網口也會自動分配一個同一網段( 172.17.0.0/16 )的地址。
當創建一個 Docker 容器的時候,同時會創建了一對 veth pair 接口(當數據包發送到一個接口時,另外一個接口也可以收到相同的數據包)。這對接口一端在容器內,即 eth0 ;另一端在本地並被掛載到docker0 網橋,名稱以 veth 開頭(例如 vethAQI2QT )。通過這種方式,主機可以跟容器通信,容器之間也可以相互通信。Docker 就創建了在主機和所有容器之間一個虛擬共享網絡。
配置
DNS
Docker 沒有為每個容器專門定制鏡像,那么怎么自定義配置容器的主機名和 DNS 配置呢? 秘訣就是它利用虛擬文件來掛載到來容器的 3 個相關配置文件。
在容器中使用 mount 命令可以看到掛載信息:
$ mount ... /dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ... /dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ... tmpfs on /etc/resolv.conf type tmpfs ... ...
這種機制可以讓宿主主機 DNS 信息發生更新后,所有 Docker 容器的 dns 配置通過 /etc/resolv.conf 文件立刻得到更新。
如果用戶想要手動指定容器的配置,可以利用下面的選項。
-h HOSTNAME or --hostname=HOSTNAME 設定容器的主機名,它會被寫到容器內的 /etc/hostname 和 /etc/hosts 。但它在容器外部看不到,既不會在 docker ps 中顯示,也不會在其他的容器的 /etc/hosts 看到。
--link=CONTAINER_NAME:ALIAS 選項會在創建容器的時候,添加一個其他容器的主機名到 /etc/hosts 文件中,讓新容器的進程可以使用主機名 ALIAS 就可以連接它。
--dns=IP_ADDRESS 添加 DNS 服務器到容器的 /etc/resolv.conf 中,讓容器用這個服務器來解析所有不在 /etc/hosts 中的主機名。
--dns-search=DOMAIN 設定容器的搜索域,當設定搜索域為 .example.com 時,在搜索一個名為 host 的 主機時,DNS 不僅搜索host,還會搜索 host.example.com 。 注意:如果沒有上述最后 2 個選項, Docker 會默認用主機上的 /etc/resolv.conf 來配置容器。
容器訪問控制
容器的訪問控制,主要通過 Linux 上的 iptables 防火牆來進行管理和實現。 iptables 是 Linux 上默認的防火牆軟件,在大部分發行版中都自帶。
容器訪問外部網絡
容器要想
訪問
外部網
絡
,需要本地系
統
的
轉發
支持。在
Linux
系
統
中,
檢查轉發
是否打開。
[root@master ~]# sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1
如果為 0,說明沒有開啟轉發,則需要手動打開。
$sysctl -w net.ipv4.ip_forward=1
容器之間訪問
容器之間相互訪問,需要兩方面的支持。
- 容器的網絡拓撲是否已經互聯。默認情況下,所有容器都會被連接到 docker0 網橋上。
- 本地系統的防火牆軟件 -- iptables 是否允許通過。
訪問所有端口
當啟動 Docker 服務時候,默認會添加一條轉發策略到 iptables 的 FORWARD 鏈上。策略為通過( ACCEPT )還是禁止( DROP )取決於配置 --icc=true (缺省值)還是 --icc=false 。當然,如果手 動指定 --iptables=false 則不會添加 iptables 規則。
可見,默認情況下,不同容器之間是允許網絡互通的。如果為了安全考慮,可以在 /etc/default/docker文件中配置 DOCKER_OPTS=--icc=false 來禁止它
訪問指定端口
在通過 -icc=false 關閉網絡訪問后,還可以通過 --link=CONTAINER_NAME:ALIAS 選項來訪問容器的開 放端口。
例如,在啟動 Docker 服務時,可以同時使用 icc=false --iptables=true 參數來關閉允許相互的網絡 訪問,並讓 Docker 可以修改系統中的 iptables 規則。
此時,系統中的 iptables 規則可能是類似
$ sudo iptables -nL ... Chain FORWARD (policy ACCEPT) target prot opt source destination DROP all -- 0.0.0.0/0 0.0.0.0/0 ...
之后,啟動容器( docker run )時使用 --link=CONTAINER_NAME:ALIAS 選項。Docker 會在 iptable 中為 兩個容器分別添加一條 ACCEPT 規則,允許相互訪問開放的端口(取決於 Dockerfile 中的 EXPOSE 行)。
當添加了 --link=CONTAINER_NAME:ALIAS 選項后,添加了 iptables 規則
$ sudo iptables -nL ... Chain FORWARD (policy ACCEPT) target prot opt source destination ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80 ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80 DROP all -- 0.0.0.0/0 0.0.0.0/0
注意: --link=CONTAINER_NAME:ALIAS 中的 CONTAINER_NAME 目前必須是 Docker 分配的名字,或使用 --name 參數指定的名字。主機名則不會被識別
映射容器端口到宿主主機的實現
容器訪問外部實現
容器所有到外部網絡的連接,源地址都會被NAT成本地系統的IP地址。這是使用 iptables 的源地址偽裝 操作實現的。
查看主機的 NAT 規則。
Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 ......
其中,上述規則將所有源地址在 172.17.0.0/16 網段,目標地址為其他網段(外部網絡)的流量動態偽 裝為從系統網卡發出。MASQUERADE 跟傳統 SNAT 的好處是它能動態從網卡獲取地址.
外部訪問容器實現
容器允許外部訪問,可以在 docker run 時候通過 -p 或 -P 參數來啟用。 不管用那種辦法,其實也是在本地的 iptable 的 nat 表中添加相應的規則。
使用 -p80:80 時:
Chain DOCKER (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.15:80 使用 -P 時: $ iptables -t nat -nL Chain DOCKER (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2
端口映射實現
IP:host_port:container_port 或 -p IP::port 來指定允許訪問容器的主機上的 IP、接口等,以制 定更嚴格的規則。
如果希望永久綁定到某個固定的 IP 地址,可以在 Docker 配置文件 /etc/default/docker 中指定 DOCKER_OPTS="--ip=IP_ADDRESS" ,之后重啟 Docker 服務即可生效。
配置 docker0 網橋
Docker 服務默認會創建一個 docker0 網橋(其上有一個 docker0 內部接口),它在內核層連通了其他 的物理或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網絡。
Docker 默認指定了 docker0 接口 的 IP 地址和子網掩碼,讓主機和容器之間可以通過網橋相互通信,它還給出了 MTU(接口允許接收的最大傳輸單元),通常是 1500 Bytes,或宿主主機網絡路由上支持的默認值。這些值都可以在服務啟動的時候進行配置。
- --bip=CIDR -- IP 地址加掩碼格式,例如 192.168.1.5/24
- --mtu=BYTES -- 覆蓋默認的 Docker mtu 配置
也可以在配置文件中配置 DOCKER_OPTS,然后重啟服務。 由於目前 Docker 網橋是 Linux 網橋,用戶可 以使用 brctl show 來查看網橋和端口連接信息。
[root@master ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.56847afe9799 no veth6278b4d veth9321eba vethc12c3b4 vethe54ad11
每次創建一個新容器的時候,Docker 從可用的地址段中選擇一個空閑的 IP 地址分配給容器的 eth0 端口。 使用本地主機上 docker0 接口的 IP 作為所有容器的默認網關.
自定義網橋
除了默認的 docker0 網橋,用戶也可以指定網橋來連接各個容器。 在啟動 Docker 服務的時候,使用 -b BRIDGE 或 --bridge=BRIDGE 來指定使用的網橋。
如果服務已經運行,那需要先停止服務,並刪除舊的網橋。
$ sudo service docker stop $ sudo ip link set dev docker0 down $ sudo brctl delbr docker0 然后創建一個網橋 bridge0 $ sudo brctl addbr bridge0 $ sudo ip addr add 192.168.5.1/24 dev bridge0 $ sudo ip link set dev bridge0 up 查看確認網橋創建並啟動 ip addr show bridge0 4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff inet 192.168.5.1/24 scope global bridge0 valid_lft forever preferred_lft forever
配置
Docker
服
務
,默
認橋
接到
創
建的網
橋
上
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker $ sudo service docker start
啟動 Docker 服務。 新建一個容器,可以看到它已經橋接到了 bridge0 上。 可以繼續用 brctl show 命令查看橋接的信息。另外,在容器中可以使用 ip addr 和 ip route 命令來 查看 IP 地址配置和路由信息。
