最簡單的 Docker 網絡就是單機橋接網絡。
從名稱中可以看到兩點,單機意味著該網絡只能在單個 Docker 主機上運行,并且只能與所在 Docker 主機上的容器進行連接,橋接意味著這是 802.1.d 橋接的一種實現(二層交換機)。
Linux Docker 創建單機橋接網絡采用內置的橋接驅動,而 Windows Docker 創建時使用內置的 NAT 驅動。實際上,這兩種驅動工作起來毫無差異。
下圖展示了兩個均包含相同本地橋接網絡 mynet 的 Docker 主機。雖然網絡是相同的,但卻是兩個獨立的網絡。這意味著圖中容器無法直接進行通信,因為并不在一個網絡當中。
每個 Docker 主機都有一個默認的單機橋接網絡。
在 Linux 上網絡名稱為 bridge,在 Windows 上叫作 nat。除非讀者通過命令行創建容器時指定參數--network,否則默認情況下,新創建的容器都會連接到該網絡。
下面列出了 docker network ls 命令在剛完成安裝的 Docker 主機上的輸出內容。輸出內容做了截取處理,只展示了每個主機上的默認網絡。注意,網絡的名稱和創建時使用的驅動名稱是一致的——這只是個巧合。
//Linux
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
333e184cd343 bridge bridge local
//Windows
> docker network ls
NETWORK ID NAME DRIVER SCOPE
095d4090fa32 nat nat local
docker network inspect命令就是一個信息寶藏。推薦各位小伙伴仔細閱讀該命令的輸出內容。
docker network inspect bridge
[
{
"Name": "bridge", << 在 Windows 上是nat
"Id": "333e184...d9e55",
"Created": "2018-01-15T20:43:02.566345779Z",
"Scope": "local",
"Driver": "bridge", << 在 Windows 上是nat
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
}
]
在 Linux 主機上,Docker 網絡由 Bridge 驅動創建,而 Bridge 底層是基于 Linux 內核中久經考驗達 15 年之久的 Linux Bridge 技術。
這意味著 Bridge 是高性能并且非常穩定的!同時這還表示可以通過標準的 Linux 工具來查看這些網絡,代碼如下。
$ ip link show docker0
3: docker0: mtu 1500 qdisc...
link/ether 02:42:af:f9:eb:4f brd ff:ff:ff:ff:ff:ff
在 Linux Docker 主機之上,默認的“bridge”網絡被映射到內核中為“docker0”的 Linux 網橋??梢酝ㄟ^ docker network inspect 命令觀察到上面的輸出內容。
$ docker network inspect bridge | grep bridge.name
"com.docker.network.bridge.name": "docker0",
Docker 默認“bridge”網絡和 Linux 內核中的“docker0”網橋之間的關系如下圖所示。
下圖對上圖的內容進行了擴展,在頂部補充了接入“bridge”網絡的容器。
“bridge”網絡在主機內核中映射到名為“docker0”的 Linux 網橋,該網橋可以通過主機以太網接口的端口映射進行反向關聯。
接下來使用 docker network create 命令創建新的單機橋接網絡,名為“localnet”。
/Linux
$ docker network create -d bridge localnet
//Windows
> docker network create -d nat localnet
新的網絡創建成功,并且會出現在 docker network ls 命名的輸出內容當中。如果使用 Linux,那么在主機內核中還會創建一個新的 Linux 網橋。
接下來通過使用 Linux brctl 工具來查看系統中的 Linux 網橋。小伙伴可能需要通過命令 apt-get install bridge-utils 來安裝 brctl 二進制包,或者根據所使用的 Linux 發行版選擇合適的命令。
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242aff9eb4f no
br-20c2e8ae4bbb 8000.02429636237c no
輸出內容中包含了兩個網橋。第一行是前文提過的 docker0 網橋,該網橋對應 Docker 中的默認網絡 bridge;第二個網橋(br-20c2e8ae4bbb)與新建的“localnet”Docker 橋接網絡相對應。
兩個網橋目前都沒有開啟 STP,并且也都沒有任何設備接入(對應的 interfaces 列為空)。
目前,主機上的網橋配置如下圖所示。
接下來創建一個新的容器,并接入到新建橋接網絡 localnet 當中。如果是在 Windows 上進行操作,需要將命令中“alpine sleep 1d”替換為“microsoft/powershell:nanoserver pwsh.exe -Command Start-Sleep 86400”。
$ docker container run -d --name c1 \
--network localnet \
alpine sleep 1d
容器現在接入了 localnet 網絡當中。讀者可以通過 docker network inspect 命令來確認。
$ docker network inspect localnet --format
'{{json .Containers}}' {
"4edcbd...842c3aa": {
"Name": "c1",
"EndpointID": "43a13b...3219b8c13",
"MacAddress": "02:42:ac:14:00:02",
"IPv4Address": "172.20.0.2/16",
"IPv6Address": ""
}
},
輸出內容表明“c1”容器已經位于橋接(Bridge/NAT)網絡 localnet 之上。
如果再次運行 brctl show 命令,就能看到 c1 的網絡接口連接到了 br-20c2e8ae4bbb 網橋。
$ brctl show
bridge name bridge id STP enabled interfaces
br-20c2e8ae4bbb 8000.02429636237c no vethe792ac0
docker0 8000.0242aff9eb4f no
下圖展示了上述關系。
如果在相同網絡中繼續接入新的容器,那么在新接入容器中是可以通過“c1”的容器名稱來 ping 通的。這是因為新容器都注冊到了指定的 Docker DNS 服務,所以相同網絡中的容器可以解析其他容器的名稱。
提示:Linux 上默認的 Bridge 網絡是不支持通過 Docker DNS 服務進行域名解析的。自定義橋接網絡可以!
下面一起來測試一下。
⒈ 創建名為“c2”的容器,并接入“c1”所在的 localnet 網絡。
//Linux
$ docker container run -it --name c2 \
--network localnet \
alpine sh
//Windows
> docker container run -it --name c2 `
--network localnet `
microsoft/powershell:nanoserver
當前終端會切換到“c2”容器中。
⒉ 在“c2”容器中,通過“c1”容器名稱執行 ping 命令。
> ping c1
Pinging c1 [172.26.137.130] with 32 bytes of data:
Reply from 172.26.137.130: bytes=32 time=1ms TTL=128
Reply from 172.26.137.130: bytes=32 time=1ms TTL=128
Control-C
命令生效了!這是因為 c2 容器運行了一個本地 DNS 解析器,該解析器將請求轉發到了 Docker 內部 DNS 服務器當中。
DNS 服務器中記錄了容器啟動時通過 --name 或者 --net-alias 參數指定的名稱與容器之間的映射關系。
如果讀者仍處于容器中,可以嘗試運行一些網絡相關的命令。這是一種很好的了解 Docker 容器網絡工作原理的方式。下面的片段是在之前創建的 Windows 容器“c2”中運行 ipconfig 命令的輸出內容??梢栽谇懊?span style="background-color:#dddddd"> docker network inspect nat 命令輸出中找到對應的 IP 地址。
> ipconfig
Windows IP Configuration
Ethernet adapter Ethernet:
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::14d1:10c8:f3dc:2eb3%4
IPv4 Address. . . . . . . . . . . : 172.26.135.0
Subnet Mask . . . . . . . . . . . : 255.255.240.0
Default Gateway . . . . . . . . . : 172.26.128.1
到目前為止,前面提到的橋接網絡中的容器只能與位于相同網絡中的容器進行通信。但是,可以使用端口映射(Port Mapping)來繞開這個限制。
端口映射允許將某個容器端口映射到 Docker 主機端口上。對于配置中指定的 Docker 主機端口,任何發送到該端口的流量,都會被轉發到容器。下圖中展示了具體流量動向。
如上圖所示,容器內部應用開放端口為 80。該端口被映射到了 Docker 主機的 10.0.0.15 接口的 5000 端口之上。最終結果就是訪問 10.0.0.15:5000 的所有流量都被轉發到了容器的 80 端口。
接下來通過示例了解將容器上運行著 Web 服務的端口 80,映射到 Docker 主機端口 5000 的過程。
示例使用 Linux 的 Nginx。如果使用 Windows 的話,可以將 Nginx 替換為某個 Windows 的 Web 服務鏡像。
⒈ 運行一個新的 Web 服務容器,并將容器 80 端口映射到 Docker 主機的 5000 端口。
$ docker container run -d --name web \
--network localnet \
--publish 5000:80 \
nginx
⒉ 確認端口映射。
$ docker port web
80/tcp -> 0.0.0.0:5000
這表示容器 80 端口已經映射到 Docker 主機所有接口上的 5000 端口。
⒊ 通過 Web 瀏覽器訪問 Docker 主機 5000 端口,驗證配置是否生效,如下圖所示。為了完成測試,需要知道 Docker 主機的 IP 地址或者 DNS 名稱。如果使用 Windows 版 Docker 或者 Mac 版 Docker,可以使用 localhost 或者 127.0.0.1。
外部系統現在可以通過 Docker 主機的 TCP 端口 5000,來訪問運行在橋接網絡上的 Nginx 容器了。
端口映射工作原理大致如此,但這種方式比較笨重并且不能擴展。舉個例子,在只有單一容器的情況下,它可以綁定到主機的任意端口。這意味著其他容器就不能再使用已經被 Nginx 容器占用的 5000 端口了。這也是單機橋接網絡只適用于本地開發環境以及非常小的應用的原因。