虛擬技術和云飛速發展的今天,云和容器已經深入人心,每個IT人都或多或少的使用容器和云。但是用歸用,很多人對其底層的原理確實知之甚少。很多人想把容器當成虛擬機來用,卻遭遇大大的阻礙,如果理解容器原理的話,你就會發現容器實際上是孤立且受限制的linux進程,和其他進程也一樣。運行容器實際上并不需要鏡像,相反,要構建鏡像,則必須要運行一些容器。但是不敢怎么著,容器是要用來使用的,必然要聯網的,所以容器聯網的原理是有限需要了解的,為此本文我們就來一起學習容器的網絡原理。

網絡命名空間
大家可能都知道Linux網絡實際上是在內存空間的堆棧數據,包括Linux網絡設備的集合、路由規則、netfilter hook集等(包括iptables規則定義的)。為了獲取Linux的這些網絡堆棧數據,需要用到幾個工具,包括netstat –ie ,ifconfig,ip link,ip route和iptables –list-rules等。
ip link
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:e3:2a:77 brd ff:ff:ff:ff:ff:ff
ip route
default via 10.0.2.2 dev eth0 proto dhcp metric 100
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100
iptables –list-rules
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
用于容器隔離的Linux命名空間之一稱為網絡命名空間。從邏輯上講,網絡命名空間是網絡堆棧的另一個副本,有其自己的路由,防火墻規則和網絡設備。為了簡單起見,這是我們將在本文中使用的唯一命名空間。與其創建完全隔離的容器,不如將范圍限制為僅網絡堆棧。ip工具在現在OS默認自帶的iproute2集合的一部分,這些工具可以用來創建網絡命名空間。
sudo ip netns add netns0
ip netns
netns0
為了使用這個剛新建的的命名空間netns0,可以使用Linux命令稱nsenter。它輸入一個或多個指定的命名空間,然后執行給定的程序:
sudo nsenter --net=/var/run/netns/netns0 bash
ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
從上面的輸出中可以明顯看出,在命名空間內運行的bash進程netns0看到了完全不同的網絡堆棧。沒有路由規則,也沒有自定義iptables鏈,只有一個環回網絡設備。

鏈接容器網絡和主機網絡
如果不能與專用網絡堆棧通信,那么容器也就沒啥用。為此Linux提供了合適的工具,虛擬以太網設備veth,這個設備是虛擬以太網設備,可以用來充當網絡命名空間之間的隧道,以創建到另一個命名空間中的物理網絡設備的橋梁,但也可以用作獨立的網絡設備。”
虛擬以太網設備需要成對使用,用下面命令創建一組互連的虛擬以太網設備veth0和ceth0:
sudo ip link add veth0 type veth peer name ceth0
然后查看網絡信息
ip link
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff
5: ceth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 66:2d:24:e3:49:3f brd ff:ff:ff:ff:ff:ff
6: veth0@ceth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 96:e8:de:1d:22:e0 brd ff:ff:ff:ff:ff:ff
雙方veth0并ceth0創建所在的主機的網絡堆棧后(也稱為根網絡命名空間)。要將根命名空間與該netns0命名空間連接,需要將其中一臺設備保留在根命名空間中,然后將另一臺設備移至netns0:
sudo ip link set ceth0 netns netns0
網絡信息:
ip link
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff
6: veth0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 96:e8:de:1d:22:e0 brd ff:ff:ff:ff:ff:ff link-netns netns0
啟用設備并分配給予適當的IP地址,其中一個設備上發生的數據包都會立即在連接兩個命名空間的對等設備接收到:
sudo ip link set veth0 up
sudo ip addr add 172.18.0.11/16 dev veth0
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
這時候的網絡信息
ip link
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
5: ceth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 66:2d:24:e3:49:3f brd ff:ff:ff:ff:ff:ff link-netnsid 0
圖示:

為了檢查連接性,我們從netns0(172.18.0.11),ping veth0(172.18.0.10)
ping -c 2 172.18.0.11
PING 172.18.0.11 (172.18.0.11) 56(84) bytes of data.
64 bytes from 172.18.0.11: icmp_seq=1 ttl=64 time=0.038 ms
64 bytes from 172.18.0.11: icmp_seq=2 ttl=64 time=0.040 ms
--- 172.18.0.11 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 58ms
rtt min/avg/max/mdev = 0.038/0.039/0.040/0.001 ms
從根命名空間ping ceth0(172.18.0.10)
ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.046 ms
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.046/0.059/0.073/0.015 ms
同時,如果嘗試從netns0命名空間訪問其他任何地址是ping不通的。
ip addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:e3:27:77 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0
valid_lft 84057sec preferred_lft 84057sec
inet6 fe80::5054:ff:fee3:2777/64 scope link
valid_lft forever preferred_lft forever
sudo nsenter --net=/var/run/netns/netns0
ping 10.0.2.15
connect: Network is unreachable
ping 8.8.8.8
connect: Network is unreachable
這是顯而易見的,因為netns0路由表中根本沒有這類數據包的路由。唯一的條目顯示了如何連接172.18.0.0/16網絡:
ip route
172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10
Linux有很多設置路由表的方法。其中之一是從直接連接的網絡接口提取路由。請記住,netns0命名空間創建后,其中的路由表為空。但是隨后在ceth0此處添加了設備并為其分配了IP地址172.18.0.10/16。由于使用的不是簡單的IP地址,而是地址和網絡掩碼的組合,因此網絡堆棧設法從中提取路由信息。每個發往172.18.0.0/16網絡的數據包都將通過ceth0設備發送。但是任何其他數據包將被丟棄。同樣,根命名空間中新增加了一條路由:
ip route
172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11
我們知道如何隔離、虛擬化和連接Linux網絡堆棧。
通過網橋(虛擬路由器)容器互聯
容器化的整個思想歸結為有效的資源共享。也就是說,每臺機器顯然不可能只有一個容器。相反,會盡最大可能在共享環境中運行更多的容器。試想,按照同樣步驟,在上面的基礎上,再增加第二個容器:
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
然后,檢查連通性:
從netns1 ping 根命名空間:
ping -c 2 172.18.0.21
PING 172.18.0.21 (172.18.0.21) 56(84) bytes of data.
From 172.18.0.20 icmp_seq=1 Destination Host Unreachable
From 172.18.0.20 icmp_seq=2 Destination Host Unreachable
--- 172.18.0.21 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 55ms
pipe 2
ping不通,看一下路由:
ip route:
172.18.0.0/16 dev ceth1 proto kernel scope link src 172.18.0.20
有路有!
從根命名空間ping netns1:
ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
From 172.18.0.11 icmp_seq=1 Destination Host Unreachable
From 172.18.0.11 icmp_seq=2 Destination Host Unreachable
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 23ms
pipe 2
從netns0可以ping通veth1
sudo nsenter --net=/var/run/netns/netns0
ping -c 2 172.18.0.21
PING 172.18.0.21 (172.18.0.21) 56(84) bytes of data.
64 bytes from 172.18.0.21: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 172.18.0.21: icmp_seq=2 ttl=64 time=0.046 ms
--- 172.18.0.21 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 33ms
rtt min/avg/max/mdev = 0.037/0.041/0.046/0.007 ms
但是,也ping不同通netns1
ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
From 172.18.0.10 icmp_seq=1 Destination Host Unreachable
From 172.18.0.10 icmp_seq=2 Destination Host Unreachable
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 63ms
pipe 2
顯然有問題了,netns1 ping不同根命名空間。由于兩個容器都位于同一IP網絡中172.18.0.0/16,所以可以用veth1和netns0容器與主機進行對話。什么導致以上問題呢?對問題出在路由沖突了!
檢查根命名空間中的路由表:
ip route
172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11
172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21
在添加了第二veth對之后,根網絡堆棧也添加新路由172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21,但這和已有的路由沖突了。如果刪除第一條路由sudo ip route delete 172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11并重新檢查連接性, netns1將恢復連接的連通性,netns0會處于不確定狀態。

線上用上述方法,veth行不通了。這是就需要另外的可行方法了。這方法就是Linux網橋,另外一個虛擬化網絡工具。Linux網橋的行為上類似于網絡交換機。它在與其連接的接口之間轉發數據包。并且由于它是交換機,因此可以在L2(即以太網)級別上進行操作。我們就來嘗試網橋來建容器網絡,先清空之前的網絡命名空間:
sudo ip netns delete netns0
sudo ip netns delete netns1
sudo ip link delete veth0
sudo ip link delete ceth0
sudo ip link delete veth1
sudo ip link delete ceth1
快速重新創建兩個容器。
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
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
確保主機上沒有新路由:
ip route
default via 10.0.2.2 dev eth0 proto dhcp metric 100
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100
最后,創建網橋接口:
sudo ip link add br0 type bridge
sudo ip link set br0 up
附加veth0和veth1到網橋br0:
sudo ip link set veth0 master br0
sudo ip link set veth1 master br0
新的網絡拓撲圖:

檢查容器之間的連通性:
sudo nsenter --net=/var/run/netns/netns0
ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
64 bytes from 172.18.0.20: icmp_seq=1 ttl=64 time=0.259 ms
64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.051 ms
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.051/0.155/0.259/0.104 ms
sudo nsenter --net=/var/run/netns/netns1
ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.089 ms
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 36ms
rtt min/avg/max/mdev = 0.037/0.063/0.089/0.026 ms
OK!一切正常。這種方法可行。由于還沒有配置veth0和veth1。分配給ceth0和ceth1兩個IP地址。由于它們都在同一以太網段上(請記住,我們已將它們連接到虛擬交換機),因此在L2級別具有連通性:
sudo nsenter --net=/var/run/netns/netns0
ip neigh
172.18.0.20 dev ceth0 lladdr 6e:9c:ae:02:60:de STALE
sudo nsenter --net=/var/run/netns/netns1
ip neigh
172.18.0.10 dev ceth1 lladdr 66:f3:8c:75:09:29 STALE
容器鏈接外網(IP路由和偽裝)
現在容器可以互通互聯。但是與主機(即根命名空間)還不能連通:
sudo nsenter --net=/var/run/netns/netns0
ping 10.0.2.15 # eth0 address
connect: Network is unreachable
查看路由:
ip route
172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10
根命名空間無法與容器無路由:
ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
From 213.51.1.123 icmp_seq=1 Destination Net Unreachable
From 213.51.1.123 icmp_seq=2 Destination Net Unreachable
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 3ms
ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
From 213.51.1.123 icmp_seq=1 Destination Net Unreachable
From 213.51.1.123 icmp_seq=2 Destination Net Unreachable
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 3ms
為了在根和容器命名空間之間建立連接,需要將IP地址分配給網橋網絡接口:
sudo ip addr add 172.18.0.1/16 dev br0
將IP地址分配給網橋接口后,便在主機路由表上獲得了一條路由:
ip route
172.18.0.0/16 dev br0 proto kernel scope link src 172.18.0.1
ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.036 ms
64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.049 ms
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 11ms
rtt min/avg/max/mdev = 0.036/0.042/0.049/0.009 ms
ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
64 bytes from 172.18.0.20: icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.056 ms
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 0.056/0.057/0.059/0.007 ms
容器也具有ping橋接接口的功能,但是仍然無法與主機的接口eth0連接,需要為容器添加默認路由:
sudo nsenter --net=/var/run/netns/netns0
ip route add default via 172.18.0.1
ping -c 2 10.0.2.15
PING 10.0.2.15 (10.0.2.15) 56(84) bytes of data.
64 bytes from 10.0.2.15: icmp_seq=1 ttl=64 time=0.036 ms
64 bytes from 10.0.2.15: icmp_seq=2 ttl=64 time=0.053 ms
--- 10.0.2.15 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 14ms
rtt min/avg/max/mdev = 0.036/0.044/0.053/0.010 ms
這配置更改基本上將主機變成了路由器,并且網橋接口成為了容器的默認網關,拓撲圖示:

容器現在與根命名空間相連,接著我們配置讓他們可以對外鏈接。默認情況下,在Linux中禁用數據包轉發(即路由器功能),我們需要打開它:
sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
然后,檢查連接性:
sudo nsenter --net=/var/run/netns/netns0
ping 8.8.8.8
好吧,有問題,仍然不通。由于容器的IP地址是私有的,容器可以數據包發送到外界,但是目標服務器將無法將數據包發送回容器。為了解決此問題,需要進行網絡地址轉換(NAT)。在進入外部網絡之前,由容器發起的數據包將其源IP地址替換為主機的外部接口地址。主機還將跟蹤所有現有的映射,并在到達主機時將還原IP地址,然后再將數據包轉發回容器。聽起來很復雜,但是做起來不難,可以使用iptables進行轉化,只需要一個命令即可實現:
sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/16 ! -o br0 -j MASQUERADE
該命令非常簡單。在nat表的POSTROUTING鏈中添加了一條新規則,要求偽裝源自172.18.0.0/16網絡的所有數據包,但不偽裝網橋接口。
再,檢查連接性:
sudo nsenter --net=/var/run/netns/netns0
ping -c 2 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=43.2 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=36.8 ms
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 36.815/40.008/43.202/3.199 ms
注意,默認情況會遵循,允許采取策略,請不要實際生產環境中使用,這非常危險。Docker默認情況下限制所有內容,然后僅對已知路徑啟用路由。
配置對外訪問(端口發布)
將容器端口發布到主機的某些(或全部)接口是一種已知的做法。但是端口發布的真正含義是什么?
假設有一個在容器中運行的服務器:
sudo nsenter --net=/var/run/netns/netns0
Python3 -m http.server --bind 172.18.0.10 5000
現在用curl嘗試從主機向該服務器進程發送HTTP請求:
curl 172.18.0.10:5000
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
但是,要從外部訪問該服務器,要怎么訪問呢?現在對外唯一IP地址是主機的外部接口地址eth0:
curl 10.0.2.15:5000
curl: (7) Failed to connect to 10.0.2.15 port 5000: Connection refused
因此,需要找到一種方法,將到達主機eth0接口上端口5000的所有數據包轉發到172.18.0.10:5000目的地。換句話說,需要在主機的接口上發布容器的端口5000 eth0。這也可以使用iptables。
sudo iptables -t nat -A PREROUTING -d 10.0.2.15 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000
sudo iptables -t nat -A OUTPUT -d 10.0.2.15 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000
此外,還要啟用iptables攔截橋接網絡上的流量:
sudo modprobe br_netfilter
Try,again:
curl 10.0.2.15:5000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
ok了
了解Docker網絡驅動程序
現在,我們再深入一下,了解一些Docker網絡模式。首先從--network host模式開始。嘗試比較以下命令ip link和的輸出sudo docker run -it --rm --network host alpine ip link。實際上,兩者是完全一樣的。即在該host模式下,Docker根本不使用網絡命名空間隔離,并且容器在根網絡命名空間中工作,并與主機共享網絡堆棧。
下一個要檢查的模式是--network none。該sudo docker run -it --rm --network none alpine ip link命令的輸出僅顯示單個環回網絡接口。這與即在添加任何veth設備之前,新創建的網絡命名空間非常相似。
最后但并非最不重要的一點是--network bridge(默認)模式。這就是本文中我們使用的方法。
無特權容器和網絡
本文中我們使用了很多sudo操作,如果沒有root特權就無法配置網絡。另外一個云原生標準的容器管理器podman,可以作為docker管理器替換。蟲蟲之前的文章中對它做過專門介紹。podman一個不錯的功能之一就是它專注于無root容器。podman建立根網絡的方法非常接近docker,為了實現無root容器,podman依賴于slirp4netns項目:
從Linux 3.8開始,非特權用戶可以與user_namespaces(7)一起創建network_namespaces(7)。但是,非特權網絡命名空間并不是很有用,因為在主機和網絡命名空間上創建veth(4)對仍然需要root特權。(即沒有互聯網連接)
slirp4netns允許通過將網絡命名空間中的TAP設備連接到用戶模式TCP / IP堆棧(“slirp”),以完全無特權的方式將網絡命名空間連接到Internet。
結論
本文中,我們介紹了在容器網絡的詳細實現細節方法,了解這些細節對我們熟悉容器網絡和排查容器網絡相關故障時候非常有意義。當然組織容器網絡的方法不可能的只有這種方法(當然,可能是使用最廣泛的一種方法)。實際上,還有其他很多的實現方式方式,可以通過官方或第三方插件實現,有興趣可以查看相關的文檔。