近期在閱讀《Docker 實戰 6堂課:56個實驗動手做,掌握 Linux 容器核心技術》這本書,書中涵蓋了 Docker 的基礎知識、容器技術、網路設定及 Linux 核心概念。透過學習這本書,我深入了解了許多以前未曾完整研究過的 Linux 知識,特別是在 Linux 網路處理的部分。因此,我希望藉由這次機會,記錄自己的操作,並用我自己的理解來解釋其中的邏輯和知識點。
以下是專案的主要目標:完全模擬在 Docker 上建立兩個容器間的 bridge 網路。
我在 AWS EC2 啟用了兩個實例,network-test-1
和 network-test-2
。network-test-1
作為主機進行各種實驗和設置,network-test-2
則用來模擬和驗證對外的網路情境,以幫助更全面地測試配置效果,特別是跨主機的網路連通性。
以下所有操作皆在
network-test-1
上進行。
當 Docker 容器使用 bridge 類型網路時,會為每個容器建立一個虛擬網路介面,並將這些網路介面放入專屬的 net namespace。這樣可以確保每個容器的網路環境互相隔離,從而增強安全性和靈活性。net namespace 是 Linux 提供的一種隔離機制,可以讓不同的網路空間彼此獨立。
首先,我們建立兩個 net namespace 來模擬容器的網路隔離:
$ sudo ip netns add ns0
$ sudo ip netns add ns1
查看 network namespace:
$ ip netns list
---
ns1
ns0
新增名為 docker1
的橋接網路(bridge network):
$ sudo ip link add docker1 type bridge
# 查看網路介面
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
altname eni-0cb768586b1414a54
altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
接下來,我們將建立兩對 veth pair(虛擬網路介面對),並將它們分別與 docker1
配對。這些 veth pair 將用於在不同的 net namespace 之間建立虛擬連接,模擬容器之間的網路通訊:
$ sudo ip link add veth0 type veth peer name veth0-br
$ sudo ip link add veth1 type veth peer name veth1-br
查詢當前的網路介面清單:
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
altname eni-0cb768586b1414a54
altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
4: veth0-br@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff
5: veth0@veth0-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 9a:0e:80:cc:ac:53 brd ff:ff:ff:ff:ff:ff
6: veth1-br@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff
7: veth1@veth1-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 62:31:3a:d4:65:b9 brd ff:ff:ff:ff:ff:ff
現在,我們要把 veth0-br
和 veth1-br
網路介面與 docker1
這個 bridge 連接起來,並且將 veth0
移動到 ns0
,veth1
移動到 ns1
。這一步是為了確保 ns0
和 ns1
之間的網路流量可以通過橋接網路 docker1
,從而模擬容器之間的通訊。
以 veth0-br
為例,將 veth0-br
加入到橋接網路介面 docker1
中,讓 veth0-br
成為橋接網路 docker1
的一部分,就像將多條網線插入同一個交換機中:
veth0-br
的流量會被橋接到 docker1
,就像數據包進入了交換機。veth0-br
通訊,就像交換機內的網線可以互相通信。可以將 docker1
想像成一個虛擬的交換機,負責將不同網路介面之間的數據包互相轉發。
# 移動網路介面到指定 ns
$ sudo ip link set veth0 netns ns0
$ sudo ip link set veth1 netns ns1
# 將 veth0-br, veth1-br 添加到 docker1
$ sudo ip link set veth0-br master docker1
$ sudo ip link set veth1-br master docker1
查詢當前的網路介面清單:
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
altname eni-0cb768586b1414a54
altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
4: veth0-br@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 1000
link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff link-netns ns0
6: veth1-br@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 1000
link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff link-netns ns1
由於 veth0
和 veth1
已經被移動到其他 net namespace 當中,所以不會出現在當前的清單中。還留著的 veth0-br
和 veth1-br
,其屬性中顯示了剛剛設定的 master docker1
。
接下來,我們要為 veth0
和 veth1
設定 IP:
$ sudo ip netns exec ns0 ip addr add 172.18.0.2/24 dev veth0
$ sudo ip netns exec ns1 ip addr add 172.18.0.3/24 dev veth1
ip netns exec ns0
這個指令的前綴,意思是進入 ns0
執行後面的指令,Kubernetes 也有類似的用法。
啟動所有的網路介面:
$ sudo ip netns exec ns0 ip link set veth0 up
$ sudo ip netns exec ns1 ip link set veth1 up
$ sudo ip link set veth0-br up
$ sudo ip link set veth1-br up
$ sudo ip link set docker1 up
查看網路介面的狀態。我們可以使用指令 ip addr
或 ip link
。為了方便查看,對 ip
指令使用 -br
flag(brief 的縮寫),讓輸出結果更簡潔:
# 查詢 root namespace
$ ip -br link
---
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enX0 UP 06:9d:02:88:52:9d <BROADCAST,MULTICAST,UP,LOWER_UP>
docker1 UP fa:b6:c0:55:33:71 <BROADCAST,MULTICAST,UP,LOWER_UP>
veth0-br@if5 UP 7e:56:1a:0a:5c:8b <BROADCAST,MULTICAST,UP,LOWER_UP>
veth1-br@if7 UP 32:65:8e:22:01:80 <BROADCAST,MULTICAST,UP,LOWER_UP>
# 查詢 ns0 namespace
$ sudo ip netns exec ns0 ip -br addr
lo DOWN
veth0@if4 UP 172.18.0.2/24 fe80::980e:80ff:fecc:ac53/64
# 查詢 ns1 namespace
$ sudo ip netns exec ns1 ip -br addr
lo DOWN
veth1@if6 UP 172.18.0.3/24 fe80::6031:3aff:fed4:65b9/64
理論上,現在已經打通了 veth0
與 veth1
之間的連線,來測試一下:
# 從 ns1 ping 位於 ns0 的 veth0 IP
$ sudo ip netns exec ns1 ping -c 2 172.18.0.2
---
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.046 ms
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1052ms
乍看之下,我們已經完成了第一個目標,但實際上在安裝 Docker 後,Docker 會自動修改 iptables 和 NAT 規則,這可能導致我們手動配置的網路連線出現衝突或無法正常運作的情況。因此,在有 Docker 的環境中,上述的網路連線設定可能無法正常運作。下一篇將探討這些挑戰,並試著解決這些問題,以確保網路配置的穩定性和預期行為。
請問圖片中標示 ns1 與 ns2, 說明文章是 ns0 與 ns1,是圖示標錯了嗎?
是的,你真細心🙂
已經修正了,感謝你的指正