跳至主要内容

3.2 操作 Docker 虛擬網路

上一小節説了很多關於 Docker 虛擬網路的技術,但實際操作起來到底是如何呢?

讓我們從 docker network list 開始,大家應該會開始發現 Docker 的指令其實就是透過一開始提到的 Management Command 當作主詞,後面搭配的動詞其實都是相同的。

$ docker network list
NETWORK ID NAME DRIVER SCOPE
8089a4c2e32a bridge bridge local
0df5a43ad470 host host local
1ba8d9b1e033 none null local

如果沒有特別建立虛擬網路的話,剛開始使用 Docker 的新手應該就只有上方這三個虛擬網路,而 bridge 則是我們前面介紹過 Docker 默認使用的虛擬網路,藉由 NAT 的技術潛伏在主機的防火牆後方。

第二個會看到的是 host 這個虛擬網路,這是一個特殊的服務,它跳過了 Docker 的虛擬網路,直接把容器連接到主機的網路介面上。

這麼做有好處也有壞處,壞處是它讓容器的安全性降低了,直接連接到本機的網路介面是有風險的,好處則是它可以提高網路的效能,畢竟少了多層級的訪問當然會更快!

最後一個看到的則是 none 這個虛擬網路,其實連接到這個虛擬網路相當於沒有連接到任何網路的意思;有些時候或許我們想要斷開某些曝光在網路上,我們可以選擇先暫時將容器連接到 none 的虛擬網路。

接著是上一小節也有使用過的 docker network inspect 這個指令,要記得 inspect 這個指令是非常萬用的 Command,不論今天搭配的是什麼 Docker 的物件,都可以養成習慣用 inspect 來看看詳細資訊。

回應的內容就像上一小節有提到的,可以在裡面看到 Gateway 以及 subnet 的 IP 位置,預設都會是 172.17 開頭,其實這個 IP 位置本身也是可以自訂的,有興趣可以自己上網去研究,這邊就不多做介紹。

同時 inspect 也會列出這個虛擬網路上所連接的容器。

$ docker network inspect bridge
[
..
"Containers": {
"68095f33b6ccb53eb2f1aba489fe642b97dd8...": {
"Name": "nginx2",
"EndpointID":"27e0b715a4e35965dfe65....",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"c2713832bc9abc526e993b2537bee5322fc....": {
"Name": "nginx",
"EndpointID": "e1e747ed84ab55c5cb8e....",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
...
]

這邊可以看到上一小節示範的兩個 nginx 正連接在 bridge 這個虛擬網路上。

建立自己的虛擬網路

說了那麼多不如直接來建立虛擬網路吧!

$ docker network create app # 這邊的名字你可以取你喜歡的
e4ad3a30af60ff7ce3cf60758fb948b39c03db04
# 這邊和容器同理,是這個虛擬網路的專屬 ID

接著我們來列出所有的虛擬網路,確認真的有建立成功。

$ docker network list
NETWORK ID NAME DRIVER SCOPE
e4ad3a30af60 app bridge local
8089a4c2e32a bridge bridge local
0df5a43ad470 host host local
1ba8d9b1e033 none null local

添加原先的容器到新的虛擬網路

在建立了專屬的虛擬網路後,我們可以把容器連接到這個新的虛擬網路中,該怎麼做呢?使用 --network 的參數。

$ docker container rm -f $(docker container ls -aq)
# 我們先清除舊有的容器

$ docker container run --detach --publish 80:80 --network app --name nginx nginx # 不換行
eb893a93e76abf6078eeab

接著確認一下有沒有正確地連接上 app 這個虛擬網路,再次利用 inspect 的方法來檢查。

$ docker network inspect app
[
..
"Containers": {
"eb893a93e76abf6078eeab8202eeaa028e...": {
"Name": "nginx",
"EndpointID":"85139bd78edc99c8c04bf72cfd....",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
...
]

如預期般的連接上 app 虛擬網路;但在實務上,我們很有可能需要替正在運作的容器添加新的虛擬網路,這時候中斷容器的服務在重新用 --network 的方式似乎不太合理,畢竟中斷正在運作的服務並不是一件樂見的事情。

添加運作中的容器到新的虛擬網路

這邊來模擬一下真實情況,我們先開啟一個 postgres 的容器且讓他連接到預設的 bridge 虛擬網路。

$ docker container run --detach --name pg --env POSTGRES_PASSWORD=mysecretpassword postgres # 不換行
b70adc2d760d90b1a05a4f78a42621da73b96dc7b

接著我們要怎麼對一個正在運作中的容器添加新的虛擬網路呢?可以透過 docker network connect 的指令:

$ docker network connect app pg
# 沒有反應是正常的,這個指令不會有 response

那我們要如何驗證這個容器同時連接到兩個虛擬網路呢?這時候 inspect 又登場囉!

$ docker container inspect pg
[
...
"Networks": {
"app": {
"IPAMConfig": {},
"Links": null,
"Aliases": [
"b70adc2d760d"
],
"NetworkID": "e4ad3a30af60ff7ce3cf60758f....",
"EndpointID": "47ece379323488f79c863ad78....",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:03",
"DriverOpts": {}
},
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "8089a4c2e32ae238ee3198112e98....",
"EndpointID": "50a742d0efe7336746ed9a53951606...",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
...
]

於是現在 app 以及 bridge 虛擬網路,都享有 postgres 的服務了。

中斷虛擬網路的連接

要斷開某個虛擬網路的連接的時候,使用 docker network disconnect 的指令:

$ docker network disconnect app pg
# 沒有反應是正常的,這個指令不會有 response

接著再次使用 inspect 來確認是否有中斷連接:

$ docker container inspect pg
[
...
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "8089a4c2e32ae238ee3198112e98f4....",
"EndpointID": "50a742d0efe7336746ed9a5395160....",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
...
]

確實只剩下原先的 bridge 虛擬網路連接而已。

經過了幾次的操作後,應該會對於 Docker 這種模組化且可拆卸的使用方式感到驚艷,一開始在學習的時候也很難體會 Docker 到底好用在哪,為什麼是人人都推必學的好技術,但隨著使用時間漸漸的增長,一定會感受到 Docker 實用的部分。

什麼是 driver?

在列出虛擬網路時會注意到,有一個 driver 的欄位。

$ docker network list
NETWORK ID NAME DRIVER SCOPE
e4ad3a30af60 app bridge local
8089a4c2e32a bridge bridge local
0df5a43ad470 host host local
1ba8d9b1e033 none null local

為什麼我們自己建立的虛擬網路也是使用 bridge 這個 driver 呢?

因為 bridge 這個 driver 是 Docker 預設的虛擬網路 driver,如果不指定 driver 的話,就會使用 bridge 作為 driver。

使用 bridge 當作 driver 的虛擬網路通常適用在單主機的容器們需要互相溝通的情況;為什麼會特別強調是單主機呢?因為後面的 Docker Swarm 篇 會介紹另外一種 driver 叫做 overlay,這種虛擬網路是可以跨機器跨平台溝通的。

而 driver 也可以使用第三方的服務,但本書的教學範圍就先以 bridge 以及 overlay 為主,就能夠做到單機部署應用程式,或是跨主機部署。