最簡(jiǎn)單的 Docker 網(wǎng)絡(luò)就是單機(jī)橋接網(wǎng)絡(luò)。
從名稱中可以看到兩點(diǎn),單機(jī)意味著該網(wǎng)絡(luò)只能在單個(gè) Docker 主機(jī)上運(yùn)行,并且只能與所在 Docker 主機(jī)上的容器進(jìn)行連接,橋接意味著這是 802.1.d 橋接的一種實(shí)現(xiàn)(二層交換機(jī))。
Linux Docker 創(chuàng)建單機(jī)橋接網(wǎng)絡(luò)采用內(nèi)置的橋接驅(qū)動(dòng),而 Windows Docker 創(chuàng)建時(shí)使用內(nèi)置的 NAT 驅(qū)動(dòng)。實(shí)際上,這兩種驅(qū)動(dòng)工作起來(lái)毫無(wú)差異。
下圖展示了兩個(gè)均包含相同本地橋接網(wǎng)絡(luò) mynet 的 Docker 主機(jī)。雖然網(wǎng)絡(luò)是相同的,但卻是兩個(gè)獨(dú)立的網(wǎng)絡(luò)。這意味著圖中容器無(wú)法直接進(jìn)行通信,因?yàn)椴⒉辉谝粋€(gè)網(wǎng)絡(luò)當(dāng)中。

每個(gè) Docker 主機(jī)都有一個(gè)默認(rèn)的單機(jī)橋接網(wǎng)絡(luò)。
在 Linux 上網(wǎng)絡(luò)名稱為 bridge,在 Windows 上叫作 nat。除非讀者通過(guò)命令行創(chuàng)建容器時(shí)指定參數(shù)--network,否則默認(rèn)情況下,新創(chuàng)建的容器都會(huì)連接到該網(wǎng)絡(luò)。
下面列出了 docker network ls 命令在剛完成安裝的 Docker 主機(jī)上的輸出內(nèi)容。輸出內(nèi)容做了截取處理,只展示了每個(gè)主機(jī)上的默認(rèn)網(wǎng)絡(luò)。注意,網(wǎng)絡(luò)的名稱和創(chuàng)建時(shí)使用的驅(qū)動(dòng)名稱是一致的——這只是個(gè)巧合。
//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命令就是一個(gè)信息寶藏。推薦各位小伙伴仔細(xì)閱讀該命令的輸出內(nèi)容。
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 主機(jī)上,Docker 網(wǎng)絡(luò)由 Bridge 驅(qū)動(dòng)創(chuàng)建,而 Bridge 底層是基于 Linux 內(nèi)核中久經(jīng)考驗(yàn)達(dá) 15 年之久的 Linux Bridge 技術(shù)。
這意味著 Bridge 是高性能并且非常穩(wěn)定的!同時(shí)這還表示可以通過(guò)標(biāo)準(zhǔn)的 Linux 工具來(lái)查看這些網(wǎng)絡(luò),代碼如下。
$ 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 主機(jī)之上,默認(rèn)的“bridge”網(wǎng)絡(luò)被映射到內(nèi)核中為“docker0”的 Linux 網(wǎng)橋??梢酝ㄟ^(guò) docker network inspect 命令觀察到上面的輸出內(nèi)容。
$ docker network inspect bridge | grep bridge.name
"com.docker.network.bridge.name": "docker0",
Docker 默認(rèn)“bridge”網(wǎng)絡(luò)和 Linux 內(nèi)核中的“docker0”網(wǎng)橋之間的關(guān)系如下圖所示。

下圖對(duì)上圖的內(nèi)容進(jìn)行了擴(kuò)展,在頂部補(bǔ)充了接入“bridge”網(wǎng)絡(luò)的容器。

“bridge”網(wǎng)絡(luò)在主機(jī)內(nèi)核中映射到名為“docker0”的 Linux 網(wǎng)橋,該網(wǎng)橋可以通過(guò)主機(jī)以太網(wǎng)接口的端口映射進(jìn)行反向關(guān)聯(lián)。
接下來(lái)使用 docker network create 命令創(chuàng)建新的單機(jī)橋接網(wǎng)絡(luò),名為“localnet”。
/Linux
$ docker network create -d bridge localnet
//Windows
> docker network create -d nat localnet
新的網(wǎng)絡(luò)創(chuàng)建成功,并且會(huì)出現(xiàn)在 docker network ls 命名的輸出內(nèi)容當(dāng)中。如果使用 Linux,那么在主機(jī)內(nèi)核中還會(huì)創(chuàng)建一個(gè)新的 Linux 網(wǎng)橋。
接下來(lái)通過(guò)使用 Linux brctl 工具來(lái)查看系統(tǒng)中的 Linux 網(wǎng)橋。小伙伴可能需要通過(guò)命令 apt-get install bridge-utils 來(lái)安裝 brctl 二進(jìn)制包,或者根據(jù)所使用的 Linux 發(fā)行版選擇合適的命令。
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242aff9eb4f no
br-20c2e8ae4bbb 8000.02429636237c no
輸出內(nèi)容中包含了兩個(gè)網(wǎng)橋。第一行是前文提過(guò)的 docker0 網(wǎng)橋,該網(wǎng)橋?qū)?yīng) Docker 中的默認(rèn)網(wǎng)絡(luò) bridge;第二個(gè)網(wǎng)橋(br-20c2e8ae4bbb)與新建的“localnet”Docker 橋接網(wǎng)絡(luò)相對(duì)應(yīng)。
兩個(gè)網(wǎng)橋目前都沒(méi)有開(kāi)啟 STP,并且也都沒(méi)有任何設(shè)備接入(對(duì)應(yīng)的 interfaces 列為空)。
目前,主機(jī)上的網(wǎng)橋配置如下圖所示。

接下來(lái)創(chuàng)建一個(gè)新的容器,并接入到新建橋接網(wǎng)絡(luò) localnet 當(dāng)中。如果是在 Windows 上進(jìn)行操作,需要將命令中“alpine sleep 1d”替換為“microsoft/powershell:nanoserver pwsh.exe -Command Start-Sleep 86400”。
$ docker container run -d --name c1 \
--network localnet \
alpine sleep 1d
容器現(xiàn)在接入了 localnet 網(wǎng)絡(luò)當(dāng)中。讀者可以通過(guò) docker network inspect 命令來(lái)確認(rèn)。
$ 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": ""
}
},
輸出內(nèi)容表明“c1”容器已經(jīng)位于橋接(Bridge/NAT)網(wǎng)絡(luò) localnet 之上。
如果再次運(yùn)行 brctl show 命令,就能看到 c1 的網(wǎng)絡(luò)接口連接到了 br-20c2e8ae4bbb 網(wǎng)橋。
$ brctl show
bridge name bridge id STP enabled interfaces
br-20c2e8ae4bbb 8000.02429636237c no vethe792ac0
docker0 8000.0242aff9eb4f no
下圖展示了上述關(guān)系。

如果在相同網(wǎng)絡(luò)中繼續(xù)接入新的容器,那么在新接入容器中是可以通過(guò)“c1”的容器名稱來(lái) ping 通的。這是因?yàn)樾氯萜鞫甲?cè)到了指定的 Docker DNS 服務(wù),所以相同網(wǎng)絡(luò)中的容器可以解析其他容器的名稱。
提示:Linux 上默認(rèn)的 Bridge 網(wǎng)絡(luò)是不支持通過(guò) Docker DNS 服務(wù)進(jìn)行域名解析的。自定義橋接網(wǎng)絡(luò)可以!
下面一起來(lái)測(cè)試一下。
⒈ 創(chuàng)建名為“c2”的容器,并接入“c1”所在的 localnet 網(wǎng)絡(luò)。
//Linux
$ docker container run -it --name c2 \
--network localnet \
alpine sh
//Windows
> docker container run -it --name c2 `
--network localnet `
microsoft/powershell:nanoserver
當(dāng)前終端會(huì)切換到“c2”容器中。
⒉ 在“c2”容器中,通過(guò)“c1”容器名稱執(zhí)行 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
命令生效了!這是因?yàn)?c2 容器運(yùn)行了一個(gè)本地 DNS 解析器,該解析器將請(qǐng)求轉(zhuǎn)發(fā)到了 Docker 內(nèi)部 DNS 服務(wù)器當(dāng)中。
DNS 服務(wù)器中記錄了容器啟動(dòng)時(shí)通過(guò) --name 或者 --net-alias 參數(shù)指定的名稱與容器之間的映射關(guān)系。
如果讀者仍處于容器中,可以嘗試運(yùn)行一些網(wǎng)絡(luò)相關(guān)的命令。這是一種很好的了解 Docker 容器網(wǎng)絡(luò)工作原理的方式。下面的片段是在之前創(chuàng)建的 Windows 容器“c2”中運(yùn)行 ipconfig 命令的輸出內(nèi)容??梢栽谇懊?span style="background-color:#dddddd"> docker network inspect nat 命令輸出中找到對(duì)應(yīng)的 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
到目前為止,前面提到的橋接網(wǎng)絡(luò)中的容器只能與位于相同網(wǎng)絡(luò)中的容器進(jìn)行通信。但是,可以使用端口映射(Port Mapping)來(lái)繞開(kāi)這個(gè)限制。
端口映射允許將某個(gè)容器端口映射到 Docker 主機(jī)端口上。對(duì)于配置中指定的 Docker 主機(jī)端口,任何發(fā)送到該端口的流量,都會(huì)被轉(zhuǎn)發(fā)到容器。下圖中展示了具體流量動(dòng)向。

如上圖所示,容器內(nèi)部應(yīng)用開(kāi)放端口為 80。該端口被映射到了 Docker 主機(jī)的 10.0.0.15 接口的 5000 端口之上。最終結(jié)果就是訪問(wèn) 10.0.0.15:5000 的所有流量都被轉(zhuǎn)發(fā)到了容器的 80 端口。
接下來(lái)通過(guò)示例了解將容器上運(yùn)行著 Web 服務(wù)的端口 80,映射到 Docker 主機(jī)端口 5000 的過(guò)程。
示例使用 Linux 的 Nginx。如果使用 Windows 的話,可以將 Nginx 替換為某個(gè) Windows 的 Web 服務(wù)鏡像。
⒈ 運(yùn)行一個(gè)新的 Web 服務(wù)容器,并將容器 80 端口映射到 Docker 主機(jī)的 5000 端口。
$ docker container run -d --name web \
--network localnet \
--publish 5000:80 \
nginx
⒉ 確認(rèn)端口映射。
$ docker port web
80/tcp -> 0.0.0.0:5000
這表示容器 80 端口已經(jīng)映射到 Docker 主機(jī)所有接口上的 5000 端口。
⒊ 通過(guò) Web 瀏覽器訪問(wèn) Docker 主機(jī) 5000 端口,驗(yàn)證配置是否生效,如下圖所示。為了完成測(cè)試,需要知道 Docker 主機(jī)的 IP 地址或者 DNS 名稱。如果使用 Windows 版 Docker 或者 Mac 版 Docker,可以使用 localhost 或者 127.0.0.1。

外部系統(tǒng)現(xiàn)在可以通過(guò) Docker 主機(jī)的 TCP 端口 5000,來(lái)訪問(wèn)運(yùn)行在橋接網(wǎng)絡(luò)上的 Nginx 容器了。
端口映射工作原理大致如此,但這種方式比較笨重并且不能擴(kuò)展。舉個(gè)例子,在只有單一容器的情況下,它可以綁定到主機(jī)的任意端口。這意味著其他容器就不能再使用已經(jīng)被 Nginx 容器占用的 5000 端口了。這也是單機(jī)橋接網(wǎng)絡(luò)只適用于本地開(kāi)發(fā)環(huán)境以及非常小的應(yīng)用的原因。