Website logo

Robert Chang

技術部落格

Docker - Network 的基礎指令

講了很多關於 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 這個指令是非常萬用的,不論今天搭配的是什麼 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...
# 這邊和 Container 同理,是這個 network 的專屬 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

或許你會問,driver 是什麼呢?

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

因為 bridge 這個 driver 是 Docker 預設的虛擬網路 driver。

如果不指定 driver 的話,就會使用 bridge 當作 driver,使用 bridge 當作 driver 的虛擬網路通常適用在單主機的容器們需要互相溝通的時候;為什麼會特別強調是單主機,是因為後面的章節會介紹另外一種 driver 叫做 overlay,這個虛擬網路是可以跨機器溝通的。

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

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

在建立了專屬的虛擬網路後,就可以把容器連接到這個新的虛擬網路中,該怎麼做呢?使用 --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 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
    }
  }
...
]

等於是現在這兩個虛擬網路的世界中,都同時享有 postgres 的服務了。

同理,要斷開某個虛擬網路的連接的時候,使用下方指令:

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

$ 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 虛擬網路的安全問題。

上一篇文章Docker - 網路世界 ( 下 )

下一篇文章Docker - 虛擬網路中的 DNS 有什麼不同?