iT邦幫忙

2022 iThome 鐵人賽

DAY 25
0

本文目標:

  • 認識 Docker 的網路種類
  • 淺談 Docker Network 的工作原理
  • 常用指令介紹

Docker 網路的種類

為了因應各種使用場景,Docker 發展出了四種不同的 Network,本節會詳細介紹它們的特色以及適用的場景。

橋接式網路

橋接器可以將網路中的多個網段在 Data Link Layer(OSI模型第2層)上連接起來。
而 Docker 也提供了類似的功能,讓我們可以把兩個 Container 的網路環境橋接起來。

以上圖來看,web 以及 db 這兩個容器的網路會被橋接在一塊,彼此互通且共用一個內部 IP。

在學習建立橋接式網路之前,先來看看 Docker network 要如何使用:

$ docker network --help

Usage:	docker network COMMAND

Manage networks

Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  prune       Remove all unused networks
  rm          Remove one or more networks

Run 'docker network COMMAND --help' for more information on a command.

藉由 --help 選項可以得知,若要建立一個網路,需使用 docker network create

新增完網路 test_net 後,可以使用 docker network inspect 取得更詳細的資訊:

若使用完畢,可以使用 docker network rm 移除已建立的網路:

動腦時間:橋接式網路的工作原理

如果應用程式以微服務的方式進行部署,一個服務通常會有多個 container,並且這些 container 必須要有能力與其他 container 進行通訊,所以橋接式網路是一個很常見的 Docker network(在 docker-compose 上面也很常看到)。
那麼,問題來了:橋接式網路是怎麼做到的呢?
如果你有看過本系列文的文章,或許對 network namespace 有一定的了解。而橋接式網路在 Linux 上也正是利用namespace 區隔 container 之間的網路資源。
筆者最近也在網路上看到了一篇很不錯的技術文章 Container Networking Is Simple!,它向大家介紹 Container Networking 是多麽的簡單(才怪):

圖片取自:https://iximiuz.com/en/posts/container-networking-is-simple/

每一個 container 會運作在獨立的 network namespace 上,通過 veth peer,位於獨立 namespace 的 container 可以與位於 root namespace 的 veth device 取得聯繫:

$ sudo ip netns add netns1
$ sudo ip link add veth1 type veth peer name ceth1
$ sudo ip link set ceth1 netns netns1
$ sudo ip link set veth1 up
$ sudo ip addr add 172.18.0.21/16 dev veth1

$ sudo nsenter --net=/var/run/netns/netns1
$ ip link set lo up
$ ip link set ceth1 up
$ ip addr add 172.18.0.20/16 dev ceth1

上方的作法有個(很大的)缺點:它們無法與 root namespace 的其他 device 連接,而且容器之間也無法找到對方。
為了讓兩個處於不同 namespace 的 container 可以向對方溝通,我們會需要使用 bridge device:

$ sudo ip netns add netns0
$ sudo ip link add veth0 type veth peer name ceth0
$ sudo ip link set veth0 up
$ sudo ip link set ceth0 netns netns0

$ sudo nsenter --net=/var/run/netns/netns0
$ ip link set lo up
$ ip link set ceth0 up
$ ip addr add 172.18.0.10/16 dev ceth0
$ exit

$ sudo ip netns add netns1
$ sudo ip link add veth1 type veth peer name ceth1
$ sudo ip link set veth1 up
$ sudo ip link set ceth1 netns netns1

$ sudo nsenter --net=/var/run/netns/netns1
$ ip link set lo up
$ ip link set ceth1 up
$ ip addr add 172.18.0.20/16 dev ceth1
$ exit

# 建立 bridge device
$ sudo ip link add br0 type bridge
$ sudo ip link set br0 up

# 連接 veth0 與 veth1
$ sudo ip link set veth0 master br0
$ sudo ip link set veth1 master br0

如此一來,兩個 Container 就可以透過 bridge 互相溝通,同時它們也保有自己的獨立空間。
做到這一步還會有一個問題:container 仍然無法與 root namespace 取得聯繫。
為了達成這個目的,我們需要為 bridge device 分配一些 IP 位址:

$ sudo ip addr add 172.18.0.1/16 dev br0

分配 IP 給 bridge device 後,我們可以用 ip route show 觀察路由表的變化:

# 目標為 172.18.0.0/16 的封包會送往 bridge device(172.18.0.1)
172.18.0.0/16 dev br0 proto kernel scope link src 172.18.0.1

作者的話:
做到這一步,可以嘗試在 root namespace 端向 container 發送 icmp echo request,我們會發現雖然收不到對應的 response,不過 container 是能夠收到請求的!

如此一來,root namespace 就有能力發送封包給位於獨立環境的 container。不過,路由表這種東西是單向的,我們可以找到對方不代表對方能夠找到我們,所以,我們同樣需要在 container 設定通往 root namespace 的路由表:

$ sudo nsenter --net=/var/run/netns/netns0
$ ip route add default via 172.18.0.1

最後,如果要讓 container 有能力連線至網際網路,則需要確定幾件事情:

  • Host 有 WAN interface 以及對應的路由
  • 開啟 ip forward(sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

令 Container 連接至 Docker Network

若 Container 仍未啟動,可以在使用 docker run 時加上 --network 選項連上指定的網路:

docker run --name web --network test_net -d -p 80:80 YOUR_IMAGE_NAME
  • --name 指定 container 的名稱。
  • --network 指定要連上的網路名稱。
  • -d 表示 --detach,讓 Container 在背景運作。
  • -p 表示 PORT FORWARDING,可以將 Container 的 Port 映射到主機上。

詳細操作可以參考 Docker docs

此外,若 Container 已經在運作階段,我們可以改用 docker network connect 達到相同的目的:

docker network conntect test_net YOUR_CONTAINER_NAME

反之,若要讓 Container 與網路斷開連結,則可以使用 docker network disconnect

docker network disconnect test_net YOUR_CONTAINER_NAME

當兩個以上的 Container 都連線至同一個網路後,可以使用 docker network inspect test_net 確認網路狀態。

Overlay networks

如上圖,覆蓋網路可以令處於不同 Docker daemon 下的 Container 互相通訊。
就一般情況來看,我們並不會用到覆蓋網路,因為它是為分散式架構而生的通訊方式,如果我們有這一塊的需求並不會手工的建置 container 以及整個網路環境。

作者的話:

  • Docker 官方文件有提到 The overlay network driver creates a distributed network among multiple Docker daemon hosts. This network sits on top of (overlays) the host-specific networks, allowing containers connected to it (including swarm service containers) to communicate securely when encryption is enabled.,這段話告訴我們覆蓋網路基本上就是為了 Docker swarm 而生。但 Docker swarm 並不在本系列文的討論範圍、筆者也沒有用過這個分散式解決方案,在 Kubernetes 中,它會將容器之間的網路實作交給 CNI(Container Network Interface)處理。
  • 覆蓋網路的建立方式要比橋接式網路複雜,詳細說明可以參考 Docker Docs

Host network

若使用 Host network,Container 與 Host 之間的網路環境會是互通的:

docker run --rm -d --network host --name my_nginx nginx

Maclvan networks

Maclvan 允許使用者能將實體網卡設定多個 mac address,並將這些 address 分配給 Container 使用,使其在 network 上顯示為 physical address 而非 virtual address。maclvan 希望能讓某些只能使用物理設備連線的應用程序能夠正常運作。

關於設定 Maclvan 的方式可以參考:https://docs.docker.com/network/macvlan/。

總結

本篇文章介紹了 Docker 的網路模型,有了 Docker Network 我們就能將多個運作中的 Container 相連,使多個元件可以在分離的執行環境中互相溝通。
不過,當整個專案的網路拓墣來到一定的規模時,要啟動每一個 Container 並且設定 Docker Network 就顯得有些沒效率了,因此,之後的文章會使用 docker-compose 這個專案快速的幫我們建置理想的執行環境,所有實驗或是實作都可以基於 docker-compose 專案進行修改,大大的提升開發效率~!

References


上一篇
使用 Docker 建構容器
下一篇
使用 Docker compose 模擬網路拓墣:free5gc-compose 專案解說
系列文
5G 核心網路與雲原生開發之亂彈阿翔36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言