容器網絡很簡單

和容器打交道感覺就像魔法。理解的人就會得心應手,不理解的會覺得很難。幸運的是,我們已經對容器技術有一定的掌握,甚至發現容器只是被隔離和限制的 Linux 進程。運行容器不一定需要鏡像,相反構建鏡像的時候反而需要運行容器。

今天我們來說說容器網絡問題。或者,更準確地說,是單主機容器網絡問題。在本文中,我們將回答以下幾個問題:

在回答以上問題時,我們將使用 Linux 工具從零開始創建容器網絡。因此,很明顯,單主機容器網絡只不過是各種 Linux 工具的簡單組合:

準備工作

任何 Linux 發行版就足夠了。本文中的所有示例都是在一個全新的 Linux 虛擬機上完成的:

接下來所有的命令在 root 用戶下執行

# uname -aLinux instance-20220709-1624 5.4.17-2136.308.9.el8uek.x86_64 #2 SMP Mon Jun 13 20:36:40 PDT 2022 x86_64 x86_64 x86_64 GNU/Linux

爲了簡化示例,在本文中,我們不打算依賴任何成熟的容器化解決方案 (例如 docker 或 podman)。相反,我們將專注於基本概念,並使用最簡單的工具來實現我們的學習目標。

網絡命名空間隔離容器網絡

Linux 網絡棧由什麼組成? 很明顯,網絡設備的集合。還有什麼呢?比如,路由規則的集合。不要忘記,還有一組 netfilter 鉤子,包括由 iptables 規則定義的鉤子。

我們可以快速寫一個不全面的 inspect-net-stack.sh 腳本:

#!/usr/bin/env bashecho "> Network devices"
ip link

echo -e "\n> Route table"
ip route

echo -e "\n> Iptables rules"
iptables --list-rules

在運行腳本之前,我們稍微修改一下 iptables 規則,使它們易於識別:

#iptables -N ROOT_NS

接着,在我的虛擬機上執行 inspect 腳本會產生以下輸出:

# sh inspect-net-stack.sh
> Network devices1: 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff> Route tabledefault via 10.0.0.1 dev ens3
default via 10.0.0.1 dev ens3 proto dhcp metric 100
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13 metric 100
169.254.0.0/16 dev ens3 scope link
169.254.0.0/16 dev ens3 proto dhcp scope link metric 100> Iptables rules-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N BareMetalInstanceServices
-N ROOT_NS

我們對這個輸出很感興趣,因爲我們希望確保即將創建的每個容器網絡都將得到一個獨立的網絡堆棧。

您可能已經聽說過,用於容器隔離的 Linux 命名空間之一被稱爲網絡命名空間。在 man ip-netns 中,“網絡命名空間在邏輯上是網絡堆棧的另一個副本,具有自己的路由、防火牆規則和網絡設備。” 爲了簡單起見,該命名空間是本文中使用的唯一命名空間。與其創建完全隔離的容器,我們寧願將範圍限制爲僅網絡棧。

創建網絡命名空間的一種方法是 ip 工具——是標準 iproute2 集合的一部分。

# ip netns
# ip netns
netns0

如何使用剛剛創建的命名空間?有一個實用的 Linux 命令叫做 nsenter。它能進入一個或多個指定的命名空間,然後執行給定的程序:

# nsenter --net=/var/run/netns/netns0  bash
# lsinspect-net-stack.sh
# sh inspect-net-stack.sh
> Network devices1: 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
> Route table
> Iptables rules-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

從上面的輸出可以清楚地看到,在 netns0 名稱空間中運行的 bash 進程看到的是一個完全不同的網絡堆棧。沒有路由規則,沒有自定義 iptables 鏈,只有一個環回網絡設備。到目前爲止,一切順利……

網絡命名空間可視化

注意:上面的 nsenter 命令在 netns0 網絡名稱空間中啓動了一個嵌套的 bash 會話。不要忘記退出它,或者使用一個新的終端繼續。

使用虛擬機以太網設備 Veth 連接容器和主機網絡

如果我們不能與專用的網絡棧進行通信,那麼該網絡棧就沒什麼用了。幸運的是,Linux 爲此提供了合適的工具—虛擬以太網設備 veth。從 man veth 命令中可知,它們可以作爲網絡命名空間之間的隧道,創建到另一個命名空間中的物理網絡設備的橋樑,但也可以用作獨立的網絡設備。

虛擬以太網設備總是成對使用。我們看一下創建命令就會清楚了:

# ip link add veth0 type veth peer name ceth0

通過這個命令,我們創建了一對相互連接的虛擬以太網設備。名稱 veth0 和 ceth0 是任意選擇的:

# ip link1: 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
3: ceth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 66:8a:fe:30:be:72 brd ff:ff:ff:ff:ff:ff
4: veth0@ceth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:87:96:fe:97:6b brd ff:ff:ff:ff:ff:ff

創建後的 veth0 和 ceth0 都駐留在主機的網絡堆棧上 (也稱爲 root 網絡命名空間)。爲了連接 root 命名空間和 netns0 命名空間,我們需要在 root 命名空間中保留一個設備,並將另一個設備移動到 netns0 中:

# ip link set ceth0 netns netns0
# ip link1: 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
4: veth0@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:87:96:fe:97:6b brd ff:ff:ff:ff:ff:ff link-netns netns0

一旦我們激活設備並分配適當的 IP 地址,任何發送到其中一個設備上的數據包都會立即出現在連接兩個名稱空間的對等設備上。讓我們從 root 命名空間開始:

# ip link set veth0 up
# ip addr add 172.18.0.11/16 dev veth0

然後在 netns0 命名空間也執行類似命令激活 ceth0:

# 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 addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: ceth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 66:8a:fe:30:be:72 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.10/16 scope global ceth0
       valid_lft forever preferred_lft forever
    inet6 fe80::648a:feff:fe30:be72/64 scope link
       valid_lft forever preferred_lft forever

通過 veth 設備連接網絡命名空間

我們檢查網絡連接:

# 從netns0, ping root命名空間的veth0ping -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.065 ms
64 bytes from 172.18.0.11: icmp_seq=2 ttl=64 time=0.023 ms

--- 172.18.0.11 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1008ms
rtt min/avg/max/mdev = 0.023/0.044/0.065/0.021 ms
# 退出 `netns0`
# exit
# 從root命名空間ping ceth0
# ping -c 2 172.18.0.10PING 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.045 ms
64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.025 ms

--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1039ms
rtt min/avg/max/mdev = 0.025/0.035/0.045/0.010 ms

同時,如果我們試圖從 netns0 命名空間訪問其他地址是不會成功的:

# 在root命名空間執行以下命令
# ip addr show dev ens32: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.13/21 brd 10.0.7.255 scope global dynamic ens3
       valid_lft 83445sec preferred_lft 83445sec
    inet6 fe80::17ff:fe01:ae0d/64 scope link
       valid_lft forever preferred_lft forever
# 記住ip:10.0.2.13
# nsenter --net=/var/run/netns/netns0 
#進入netns0名空間
# ping 10.0.2.13connect: Network is unreachable
# 試試其他公網IP
# ping 8.8.8.8connect: Network is unreachable

不過,這很容易解釋。netns0 路由表中根本沒有針對此類數據包的路由。唯一的一條路由顯示瞭如何到達 172.18.0.0/16 網絡:

# 在netns0命名空間執行以下命令
# ip route172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10

Linux 有很多增加路由表的方法。其中一種方法是從直接連接的網絡接口中提取路由。記住,netns0 中的路由表在創建名稱空間之後是空的。但隨後我們在那裏添加了 ceth0 設備,併爲其分配了一個 172.18.0.10/16 的 IP 地址。由於我們使用的不是一個簡單的 IP 地址,而是地址和網絡掩碼的組合,網絡棧會從中提取路由信息。每個去往 172.18.0.0/16 網絡的報文都要通過 ceth0 設備發送。但其他數據包會被丟棄。類似地,在 root 命名空間中有一個新的路由:

# ip route
# ...省略部分...
172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11

到這裏,我們已經準備好回答第一個問題了。我們已經知道了如何隔離、虛擬化和連接 Linux 網絡棧

使用虛擬交換機 (bridge) 連接容器

容器化的整個理念歸結爲高效的資源共享。也就是說,每臺機器只有一個容器是不合理的。相反,我們的目標是在共享環境中運行儘可能多的獨立進程。那麼,如果我們按照上面的虛擬以太網 veth 方法在同一個主機上配置多個容器會發生什麼呢?讓我們添加第二個容器:

# ip netns add netns1
# ip link add veth1 type veth peer name ceth1
# ip link set ceth1 netns netns1
# ip link set veth1 up
# ip addr add 172.18.0.21/16 dev veth1
# 進入netns1名空間
# 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不通veth1
# ping -c 2 172.18.0.21PING 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 1030ms
pipe 2
##但是netns1中存在一條路有
# ip route172.18.0.0/16 dev ceth1 proto kernel scope link src 172.18.0.20
# 退出`netns1`
# exit
#從root名空間也ping不通ceth1
# ping -c 2 172.18.0.20PING 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 1062ms
pipe 2
#從`netns0`可以ping通veth1
# nsenter --net=/var/run/netns/netns0
# ping -c 2 172.18.0.21PING 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.066 ms
64 bytes from 172.18.0.21: icmp_seq=2 ttl=64 time=0.033 ms

--- 172.18.0.21 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1062ms
rtt min/avg/max/mdev = 0.033/0.049/0.066/0.017 ms

有點不對勁……netns1 現象和 netns0 不一樣。由於某些原因,它不能與 root 通信,從 root 名空間我們也不能訪問它。但是,由於兩個容器都位於同一個 IP 網絡 172.18.0.0/16 中,我們現在可以通過 netns0 名空間與主機的 veth1 通信。這確實有點意思...

我花了些時間才弄明白,很顯然我們遇到了路由衝突。讓我們檢查一下 root 命名空間中的路由表:

# 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 對後,root 名空間網絡棧學習到新的路由 172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21,但之前已經有一條路由生效了。當第二個容器嘗試 ping veth1 設備時,根據路由匹配順序會選擇第一條路由,從而中斷連接。如果我們刪除第一個路由 sudo ip route delete 172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11,並重新檢查連通性,情況將變成 netns1 的連接將被恢復,但 netns0 將處於不確定狀態。

路由衝突

我相信如果我們爲 netns1 選擇另一個 IP 子網絡,一切都會工作正常。但是,在一個 IP 子網絡中有多個容器是合理的。因此,我們需要以某種方式調整 veth 方法……

看看 Linux 網橋吧——這是另一種虛擬網絡工具!Linux 網橋就像一個網絡交換機。實現與它相連的接口之間轉發數據包。由於它是一個交換機,它在 L2(即以太網) 網絡層工作。

讓我們一起來試試網橋功能。但首先,我們需要清理前面的設置,到目前爲止我們所做的一些配置更改實際上已經不需要了。刪除網絡命名空間就足夠了:

#ip netns delete netns0
#ip netns delete netns1

快速重新創建兩個容器網絡。注意,我們沒有爲新的 veth0 和 veth1 設備分配任何 IP 地址:

# ip netns add netns0
# ip link add veth0 type veth peer name ceth0
# ip link set veth0 up
# ip link set ceth0 netns netns0
# 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
#----------
#ip netns add netns1
# ip link add veth1 type veth peer name ceth1
# ip link set veth1 up
# ip link set ceth1 netns netns1
# 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

確保主機上沒有新的路由:

# ip routedefault via 10.0.0.1 dev ens3
default via 10.0.0.1 dev ens3 proto dhcp metric 100
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13 metric 100
169.254.0.0/16 dev ens3 scope link
169.254.0.0/16 dev ens3 proto dhcp scope link metric 100

接着,創建網橋:

# ip link add br0 type bridge
# ip link set br0 up

下面,將 veth0 和 veth1 虛擬設備掛載到網橋上:

# ip link set veth0 master br0
# ip link set veth1 master br0

再測試下連通性:

# nsenter --net=/var/run/netns/netns0
# ping -c 2  172.18.0.20PING 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.069 ms
64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.030 ms

--- 172.18.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1047ms
rtt min/avg/max/mdev = 0.030/0.049/0.069/0.020 ms
# nsenter --net=/var/run/netns/netns1
#ping -c 2  172.18.0.10PING 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.025 ms

--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1035ms
rtt min/avg/max/mdev = 0.025/0.031/0.037/0.006 ms

一切工作正常。使用這種新方法,我們根本沒有配置 veth0 和 veth1。我們分配的僅有兩個 IP 地址在 ceth0 和 ceth1 兩端。但由於它們都在同一個以太網段上 (記住,我們將它們連接到虛擬交換機),因此在 L2 層上有連接:

# nsenter --net=/var/run/netns/netns0
# ip neigh172.18.0.20 dev ceth0 lladdr 7a:63:31:c0:53:0b STALE
# nsenter --net=/var/run/netns/netns1
# ip neigh172.18.0.10 dev ceth1 lladdr 76:02:44:fa:2d:2f STALE

恭喜你,學會了如何把容器變成友好的鄰居,防止它們干擾,但保持連接。

容器和外界通信 (IP 路由和僞裝)

現在容器之間可以相互通信。但是它們能與主機 (即 root 名空間) 通信嗎?

# nsenter --net=/var/run/netns/netns0
# ping 10.0.2.13connect: Network is unreachable

這很明顯,netns0 中沒有路由:

# ip route172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10

root 命名空間也不能與容器通信:

# exit
logout
# ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.

--- 172.18.0.10 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1048ms# ping -c 2 172.18.0.20PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.

--- 172.18.0.20 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1023ms

爲了建立 root 命名空間和容器名稱空間之間的連接,我們需要爲網橋分配 IP 地址:

# ip addr add 172.18.0.1/16 dev br0
# ping -c 2 172.18.0.20PING 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.091 ms
64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.028 ms

--- 172.18.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1049ms

一旦我們給網橋分配了 IP 地址,我們就在主機路由表中得到了一條路由:

# ip route
# ...省略部分內容...
172.18.0.0/16 dev br0 proto kernel scope link src 172.18.0.1

容器能夠 ping 橋接接口,但它們仍然無法連接到主機的 eth0。我們需要爲容器添加默認路由:

# nsenter --net=/var/run/netns/netns0
# ip route add default via 172.18.0.1
# ping -c 2 10.0.2.13PING 10.0.2.13 (10.0.2.13) 56(84) bytes of data.
64 bytes from 10.0.2.13: icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from 10.0.2.13: icmp_seq=2 ttl=64 time=0.040 ms

--- 10.0.2.13 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1022ms
rtt min/avg/max/mdev = 0.040/0.045/0.050/0.005 ms

這個改變基本上把主機變成了路由器,網橋變成了容器的默認網關。

很好,我們將容器與 root 命名空間連接起來。現在,讓我們試着把他們和外界聯繫起來。缺省情況下,Linux 系統關閉報文轉發功能,即路由器功能。我們需要打開它:

# 在root命名空間
#bash -c "echo 1 > /proc/sys/net/ipv4/ip_forward"

還是看看連通性:

# nsenter --net=/var/run/netns/netns0[root@instance-20220709-1624 opc]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.# 長時間卡住

還是無法連接主機外部網絡,難道我們還少了什麼沒做?如果容器發送數據到外部,目標服務器無法發送返回包到容器,因爲容器 IP 是局域網 IP。也就是說只有主機內部纔有路由規則到容器 IP。而且全網存在大量容器使用相同的局域網 IP:172.18.0.10 地址。解決局域網訪問外部網絡的方法是 NAT 網絡地址轉換。數據包在出主機之前,由容器發出的數據包的源 IP 地址將被主機的外部接口地址取代。主機還將跟蹤所有局域網 IP 的映射關係,在到達時,它將恢復源 IP 地址,然後將數據包轉發給容器。聽起來很複雜,但我有好消息告訴你!感謝 iptables 模塊,我們只需要一個命令就可以實現:

#sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/16 ! -o br0 -j MASQUERADE

這個命令相當簡單。我們在 POSTROUTING 鏈的 nat 表中添加了一條新規則,要求僞裝所有來自 172.18.0.0/16 網絡的報文,但不是通過網橋。

外部網絡訪問容器內部服務

將容器端口映射到主機的一些 (或全部) 接口是一種已知的實踐。暴露容器端口究竟意味着什麼?
假設容器內部運行着一個 web 服務:

# nsenter --net=/var/run/netns/netns0
# python3 -m http.server --bind 172.18.0.10 5000

如果我們試圖從主機向這個服務器進程發送一個 HTTP 請求,一切都會正常工作 (好吧,root 名空間和所有容器接口之間有一個連接)。

# 從root名空間執行下面命令
# curl 172.18.0.10:5000<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
# ... 省略部分內容 ...

但是,如果我們從外部世界訪問這個服務器,我們將使用什麼 IP 地址?我們可能知道的唯一 IP 地址是主機的外部接口地址 ens3:

#curl 10.0.2.13:5000curl: (7) Failed to connect to 10.0.2.13 port 5000: Connection refused

因此,我們需要找到一種方法,將到達主機 ens3 接口上端口 5000 的任何包轉發到 172.18.0.10:5000 目的地。或者,換句話說,我們需要在主機的 ens3 接口上發佈容器的端口 5000。又是 Iptables 來拯救!

# 外部流量
# iptables -t nat -A PREROUTING -d 10.0.2.13 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000
# 本地流量
# iptables -t nat -A OUTPUT -d 10.0.2.13 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000

另外,我們需要啓用 iptables 在網橋網絡上攔截流量:

modprobe br_netfilter

測試時間:

#curl 10.0.2.13:5000<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
# ... 忽略部分內容 ...

看看服務端輸出:

#python3 -m http.server --bind 172.18.0.10 5000Serving HTTP on 172.18.0.10 port 5000 (http://172.18.0.10:5000/) ...
172.18.0.1 - - [09/Jul/2022 12:56:07] "GET / HTTP/1.1" 200 -
10.0.2.13 - - [09/Jul/2022 12:58:43] "GET / HTTP/1.1" 200 -
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/2PTxdzg7qF12eaUcbCckGw