前言
近期在閱讀《Docker 實戰 6堂課:56個實驗動手做,掌握 Linux 容器核心技術》這本書,書中涵蓋了 Docker 的基礎知識、容器技術、網路設定及 Linux 核心概念。透過學習這本書,我深入了解了許多以前未曾完整研究過的 Linux 知識,特別是在 Linux 網路處理的部分。因此,我希望藉由這次機會,記錄自己的操作,並用我自己的理解來解釋其中的邏輯和知識點。
目標
以下是專案的主要目標:完全模擬在 Docker 上建立兩個容器間的 bridge 網路。
- ns0 的網路請求必須能透過 veth0,經由 docker1 傳送到 ns1,反之亦然。
- ns0 和 ns1 必須要能連到外網。
準備環境
我在 AWS EC2 啟用了兩個實例,network-test-1
和 network-test-2
。network-test-1
作為主機進行各種實驗和設置,network-test-2
則用來模擬和驗證對外的網路情境,以幫助更全面地測試配置效果,特別是跨主機的網路連通性。
以下所有操作皆在
network-test-1
上進行。
建立 net namespace 與 bridge
當 Docker 容器使用 bridge 類型網路時,會為每個容器建立一個虛擬網路介面,並將這些網路介面放入專屬的 net namespace。這樣可以確保每個容器的網路環境互相隔離,從而增強安全性和靈活性。net namespace 是 Linux 提供的一種隔離機制,可以讓不同的網路空間彼此獨立。
首先,我們建立兩個 net namespace 來模擬容器的網路隔離:
1sudo ip netns add ns02sudo ip netns add ns1
查看 network namespace:
1ip netns list2---3ns14ns0
新增名為 docker1
的橋接網路(bridge network):
1sudo ip link add docker1 type bridge2# 查看網路介面3ip link list4---51: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 10006 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:0072: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 10008 link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff9 altname eni-0cb768586b1414a5410 altname device-number-0.0113: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 100012 link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
建立 veth pair
接下來,我們將建立兩對 veth pair(虛擬網路介面對),並將它們分別與 docker1
配對。這些 veth pair 將用於在不同的 net namespace 之間建立虛擬連接,模擬容器之間的網路通訊:
1sudo ip link add veth0 type veth peer name veth0-br2sudo ip link add veth1 type veth peer name veth1-br
查詢當前的網路介面清單:
1ip link list2---31: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 10004 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:0052: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 10006 link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff7 altname eni-0cb768586b1414a548 altname device-number-0.093: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 100010 link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff114: veth0-br@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 100012 link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff135: veth0@veth0-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 100014 link/ether 9a:0e:80:cc:ac:53 brd ff:ff:ff:ff:ff:ff156: veth1-br@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 10003 collapsed lines
16 link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff177: veth1@veth1-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 100018 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
想像成一個虛擬的交換機,負責將不同網路介面之間的數據包互相轉發。
1# 移動網路介面到指定 ns2sudo ip link set veth0 netns ns03sudo ip link set veth1 netns ns14# 將 veth0-br, veth1-br 添加到 docker15sudo ip link set veth0-br master docker16sudo ip link set veth1-br master docker1
查詢當前的網路介面清單:
1$ ip link list2---31: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 10004 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:0052: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 10006 link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff7 altname eni-0cb768586b1414a548 altname device-number-0.093: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 100010 link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff114: veth0-br@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 100012 link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff link-netns ns0136: veth1-br@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 100014 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
。
設置 IP 與啟動網路介面
接下來,我們要為 veth0
和 veth1
設定 IP:
1sudo ip netns exec ns0 ip addr add 172.18.0.2/24 dev veth02sudo ip netns exec ns1 ip addr add 172.18.0.3/24 dev veth1
ip netns exec ns0
這個指令的前綴,意思是進入 ns0
執行後面的指令,Kubernetes 也有類似的用法。
啟動所有的網路介面:
1sudo ip netns exec ns0 ip link set veth0 up2sudo ip netns exec ns1 ip link set veth1 up3sudo ip link set veth0-br up4sudo ip link set veth1-br up5sudo ip link set docker1 up
查看網路介面的狀態。我們可以使用指令 ip addr
或 ip link
。為了方便查看,對 ip
指令使用 -br
flag(brief 的縮寫),讓輸出結果更簡潔:
1# 查詢 root namespace2$ ip -br link3---4lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>5enX0 UP 06:9d:02:88:52:9d <BROADCAST,MULTICAST,UP,LOWER_UP>6docker1 UP fa:b6:c0:55:33:71 <BROADCAST,MULTICAST,UP,LOWER_UP>7veth0-br@if5 UP 7e:56:1a:0a:5c:8b <BROADCAST,MULTICAST,UP,LOWER_UP>8veth1-br@if7 UP 32:65:8e:22:01:80 <BROADCAST,MULTICAST,UP,LOWER_UP>9# 查詢 ns0 namespace10$ sudo ip netns exec ns0 ip -br addr11lo DOWN12veth0@if4 UP 172.18.0.2/24 fe80::980e:80ff:fecc:ac53/6413# 查詢 ns1 namespace14$ sudo ip netns exec ns1 ip -br addr15lo DOWN1 collapsed line
16veth1@if6 UP 172.18.0.3/24 fe80::6031:3aff:fed4:65b9/64
驗證網路連線
理論上,現在已經打通了 veth0
與 veth1
之間的連線,來測試一下:
1# 從 ns1 ping 位於 ns0 的 veth0 IP2sudo ip netns exec ns1 ping -c 2 172.18.0.23---4PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.564 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.077 ms664 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.046 ms7
8--- 172.18.0.2 ping statistics ---92 packets transmitted, 2 received, 0% packet loss, time 1052ms
所以,我們完成了嗎?
乍看之下,我們已經完成了第一個目標,但實際上在安裝 Docker 後,Docker 會自動修改 iptables 和 NAT 規則,這可能導致我們手動配置的網路連線出現衝突或無法正常運作的情況。因此,在有 Docker 的環境中,上述的網路連線設定可能無法正常運作。下一篇將探討這些挑戰,並試著解決這些問題,以確保網路配置的穩定性和預期行為。