成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

CloudBest:干貨 | 手把手帶你搞定4大容器網(wǎng)絡(luò)問(wèn)題

Tecode / 2022人閱讀

摘要:一直以來(lái),網(wǎng)絡(luò)都是容器中令人頭疼的問(wèn)題。本文的主要目的是帶你解決容器網(wǎng)絡(luò)問(wèn)題,讓你不再對(duì)它恐懼。或者,更準(zhǔn)確地說(shuō),是單主機(jī)容器網(wǎng)絡(luò)問(wèn)題。與其創(chuàng)建完全隔離的容器,不如將范圍限制在網(wǎng)絡(luò)堆棧中。

一直以來(lái),網(wǎng)絡(luò)都是容器中令人頭疼的問(wèn)題。本文的主要目的是帶你解決容器網(wǎng)絡(luò)問(wèn)題,讓你不再對(duì)它恐懼。

使用容器總是感覺(jué)像變魔術(shù)一樣。對(duì)那些了解其內(nèi)部原理的人來(lái)說(shuō),它是一種很好的方式;而對(duì)那些不了解其內(nèi)部原理的人來(lái)說(shuō),這是一種可怕的方式。幸運(yùn)的是,我們研究容器化技術(shù)的內(nèi)部原理已經(jīng)很長(zhǎng)一段時(shí)間了。我們甚至發(fā)現(xiàn),容器只是隔離的、受限制的 Linux 進(jìn)程,鏡像并不是運(yùn)行容器所必須的,相反——要構(gòu)建一個(gè)鏡像,我們需要運(yùn)行一些容器。

image.png

現(xiàn)在,讓我們來(lái)解決下容器網(wǎng)絡(luò)問(wèn)題?;蛘?,更準(zhǔn)確地說(shuō),是單主機(jī)容器網(wǎng)絡(luò)問(wèn)題。在本文中,我們將回答以下問(wèn)題:

  • 如何虛擬化網(wǎng)絡(luò)資源,使容器認(rèn)為它們中的每一個(gè)都有一個(gè)專用的網(wǎng)絡(luò)堆棧?
  • 如何將容器變成友好的鄰居,防止它們相互干擾,并教它們?nèi)绾魏芎玫販贤ǎ?/li>
  • 怎樣從容器內(nèi)部訪問(wèn)外部世界(比如互聯(lián)網(wǎng))?
  • 如何從外部世界(即端口發(fā)布)訪問(wèn)運(yùn)行在一臺(tái)機(jī)器上的容器?

因此,很明顯,單主機(jī)容器網(wǎng)絡(luò)只不過(guò)是一些眾所周知的 Linux 工具的簡(jiǎn)單組合:

  • 網(wǎng)絡(luò)命名空間
  • 虛擬以太網(wǎng)設(shè)備(veth)
  • 虛擬網(wǎng)絡(luò)交換機(jī)(網(wǎng)橋)
  • IP 路由和網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)

不管怎樣,不需要任何代碼就可以讓網(wǎng)絡(luò)魔法發(fā)生……

前提條件

任何還算不錯(cuò)的 Linux 發(fā)行版可能都足矣。本文中的所有例子都是在一個(gè)全新的 vagrant CentOS 8 虛擬機(jī)上完成的:

$ vagrant init centos/8
$ vagrant up
$ vagrant ssh

[vagrant@localhost ~]$ uname -a
Linux localhost.localdomain 4.18.0-147.3.1.el8_1.x86_64

簡(jiǎn)單起見(jiàn),在本文中,我們不打算依賴任何成熟的容器化解決方案(例如 docker 或 podman)。相反,我們將關(guān)注基本概念,并使用最簡(jiǎn)單的工具來(lái)實(shí)現(xiàn)我們的學(xué)習(xí)目標(biāo)。

通過(guò)網(wǎng)絡(luò)命名空間隔離容器

Linux 網(wǎng)絡(luò)堆棧是由什么組成的?很明顯,是網(wǎng)絡(luò)設(shè)備的集合。還有什么?可能是路由規(guī)則集。不要忘了還有 netfilter 鉤子集,包括由 iptables 規(guī)則定義的。

我們可以快速創(chuàng)建一個(gè)不是很完善的inspect-net-stack.sh腳本:

#!/usr/bin/env bash

echo "> Network devices"
ip link

echo -e "
> Route table"
ip route

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

在運(yùn)行它之前,讓我們稍微修改下 iptables 規(guī)則,讓其更容易識(shí)別:

$ sudo iptables -N ROOT_NS

之后,在我的機(jī)器上執(zhí)行 inspect 腳本會(huì)產(chǎn)生以下輸出:

$ sudo ./inspect-net-stack.sh
> Network devices
1: lo:  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:  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

> Route table
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 rules
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N ROOT_NS

之所以對(duì)這個(gè)輸出感興趣,是因?yàn)槲覀兿氪_保即將創(chuàng)建的每個(gè)容器都將獲得一個(gè)多帶帶的網(wǎng)絡(luò)堆棧。你可能已經(jīng)聽(tīng)說(shuō)過(guò),用于容器隔離的其中一個(gè) Linux 名稱空間是網(wǎng)絡(luò)命名空間(network namespace)。按照man ip-netns的說(shuō)法,“網(wǎng)絡(luò)命名空間在邏輯上是網(wǎng)絡(luò)堆棧的另一個(gè)副本,有自己的路由、防火墻規(guī)則和網(wǎng)絡(luò)設(shè)備?!?簡(jiǎn)單起見(jiàn),這將是我們?cè)诒疚闹惺褂玫奈ㄒ幻臻g。與其創(chuàng)建完全隔離的容器,不如將范圍限制在網(wǎng)絡(luò)堆棧中。

創(chuàng)建網(wǎng)絡(luò)命名空間的一種方法是ip工具——是事實(shí)標(biāo)準(zhǔn) iproute2 工具集的一部分:

$ sudo ip netns add netns0
$ ip netns
netns0

如何開(kāi)始使用剛剛創(chuàng)建的命名空間?有一個(gè)可愛(ài)的 Linux 命令叫做nsenter。它輸入一個(gè)或多個(gè)指定的名稱空間,然后執(zhí)行給定的程序:

$ sudo nsenter --net=/var/run/netns/netns0 bash
# The newly created bash process lives in netns0

$ sudo ./inspect-net-stack.sh
> Network devices
1: lo:  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命名空間內(nèi)運(yùn)行的 bash 進(jìn)程看到的是一個(gè)完全不同的網(wǎng)絡(luò)堆棧。沒(méi)有路由規(guī)則,沒(méi)有自定義 iptables 鏈,只有一個(gè)環(huán)回網(wǎng)絡(luò)設(shè)備。到目前為止,一切順利……

image.png

使用虛擬以太網(wǎng)設(shè)備(veth)將容器連接到主機(jī)

如果我們不能與一個(gè)專用的網(wǎng)絡(luò)堆棧通信,那么它就沒(méi)那么有用了。幸運(yùn)的是,Linux 為此提供了一個(gè)合適工具——虛擬以太網(wǎng)設(shè)備!按照man veth的說(shuō)法,“veth 設(shè)備是虛擬以太網(wǎng)設(shè)備。它們可以作為網(wǎng)絡(luò)命名空間之間的隧道,創(chuàng)建一個(gè)連接到另一個(gè)命名空間中物理網(wǎng)絡(luò)設(shè)備的橋,但也可以作為獨(dú)立的網(wǎng)絡(luò)設(shè)備使用?!?/p>

虛擬以太網(wǎng)設(shè)備總是成對(duì)出現(xiàn)。不用擔(dān)心,讓我們看一下創(chuàng)建命令就會(huì)明白了:

$ sudo ip link add veth0 type veth peer name ceth0

通過(guò)這個(gè)命令,我們剛剛創(chuàng)建了一對(duì)相互連接的虛擬以太網(wǎng)設(shè)備。名稱veth0和ceth0是任起的:

$ ip link
1: lo:  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:  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:  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:  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

創(chuàng)建后,veth0和ceth0都駐留在主機(jī)的網(wǎng)絡(luò)堆棧(也稱為根網(wǎng)絡(luò)命名空間)上。為了連接根命名空間和netns0命名空間,我們需要將一個(gè)設(shè)備保留在根命名空間中,并將另一個(gè)設(shè)備移到netns0中:

$ sudo ip link set ceth0 netns netns0
# List all the devices to make sure one of them disappeared from the root stack
$ ip link
1: lo:  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:  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:  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

一旦我們打開(kāi)設(shè)備并分配了正確的 IP 地址,任何出現(xiàn)在其中一臺(tái)設(shè)備上的數(shù)據(jù)包都會(huì)立即出現(xiàn)在連接兩個(gè)命名空間的對(duì)端設(shè)備上。讓我們從根命名空間開(kāi)始:

$ sudo ip link set veth0 up
$ sudo ip addr add 172.18.0.11/16 dev veth0

接下來(lái)是etns0:

$ sudo nsenter --net=/var/run/netns/netns0
$ ip link set lo up  # whoops
$ ip link set ceth0 up
$ ip addr add 172.18.0.10/16 dev ceth0
$ ip link
1: lo:  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:  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

image.png

通過(guò) veth 設(shè)備連接網(wǎng)絡(luò)命名空間

現(xiàn)在可以檢查下連接了:

# From `netns0` ping roots veth0
$ 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

# Leave `netns0`
$ exit

# From root namespace ping ceth0
$ 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

同時(shí),如果我們?cè)噲D從netns0命名空間訪問(wèn)任何其他地址,都會(huì)失?。?/p>

# Inside root namespace
$ ip addr show dev eth0
2: eth0:  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

# Remember this 10.0.2.15

$ sudo nsenter --net=/var/run/netns/netns0

# Try hosts eth0
$ ping 10.0.2.15
connect: Network is unreachable

# Try something from the Internet
$ ping 8.8.8.8
connect: Network is unreachable

不過(guò),這很容易解釋。對(duì)于這樣的數(shù)據(jù)包,在netns0的路由表中沒(méi)有路由。其中,唯一的條目顯示了如何到達(dá)172.18.0.0/16網(wǎng)絡(luò):

# From `netns0` namespace:
$ ip route
172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10

Linux 有很多方法來(lái)填充路由表。其中之一是從直接連接的網(wǎng)絡(luò)接口提取路由。記住,在命名空間創(chuàng)建后,netns0的路由表是空的。但隨后我們添加了ceth0設(shè)備,并為它分配了一個(gè) IP 地址172.18.0.10/16。由于我們使用的不是一個(gè)簡(jiǎn)單的 IP 地址,而是地址和網(wǎng)絡(luò)掩碼的組合,網(wǎng)絡(luò)堆棧會(huì)設(shè)法從中提取路由信息。每個(gè)發(fā)往172.18.0.0/16網(wǎng)絡(luò)的數(shù)據(jù)包將通過(guò)ceth0設(shè)備發(fā)送。但是任何其他的包都會(huì)被丟棄。類似地,在根命名空間中有一條新路由:

# From `root` namespace:
$ ip route
# ... omited lines ...
172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11

現(xiàn)在,我們已經(jīng)回答了我們的第一個(gè)問(wèn)題。我們現(xiàn)在知道了如何隔離、虛擬化和連接 Linux 網(wǎng)絡(luò)堆棧。

通過(guò)虛擬網(wǎng)絡(luò)交換機(jī)(網(wǎng)橋)實(shí)現(xiàn)容器互連

容器化的整個(gè)理念可以歸結(jié)為有效的資源共享。也就是說(shuō),每臺(tái)機(jī)器一個(gè)容器的情況并不常見(jiàn)。相反,我們的目標(biāo)是在共享環(huán)境中運(yùn)行盡可能多的隔離進(jìn)程。那么,如果我們按照上面的veth方法將多個(gè)容器放在同一主機(jī)上,會(huì)發(fā)生什么呢?讓我們添加第二個(gè)容器:

# From root namespace
$ 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

我最喜歡的部分,檢查連接:

# From `netns1` we cannot reach the root namespace!
$ 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

# But there is a route!
$ ip route
172.18.0.0/16 dev ceth1 proto kernel scope link src 172.18.0.20

# Leaving `netns1`
$ exit

# From root namespace we cannot reach the `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

# From `netns0` we CAN reach `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

# But we still cannot reach `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

有點(diǎn)不對(duì)勁……netns1遇到問(wèn)題。由于某些原因,它不能與根通信,我們也不能從根命名空間訪問(wèn)它。然而,由于兩個(gè)容器都位于同一個(gè) IP 網(wǎng)絡(luò) 172.18.0.0/16 中,我們現(xiàn)在可以從netns0容器與主機(jī)的veth1進(jìn)行通信。非常有趣……

我花了些時(shí)間才想明白,但顯然我們面臨的是路由沖突。讓我們檢查下根命名空間中的路由表:

$ ip route
# ... omited lines ... #
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

雖然在添加了第二個(gè)veth對(duì)后,根的網(wǎng)絡(luò)堆棧學(xué)習(xí)到了新的路由172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21,但是,現(xiàn)有的路由中已經(jīng)有一條針對(duì)同一網(wǎng)絡(luò)的路由。當(dāng)?shù)诙€(gè)容器試圖 pingveth1設(shè)備時(shí),將選擇第一個(gè)路由,這會(huì)破壞連接。如果我們刪除第一條路由sudo ip route delete 172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11,并重新檢查連接,情況就會(huì)反過(guò)來(lái),即netns1的連接將恢復(fù),但netns0就有問(wèn)題了。

image.png

我相信,如果我們?yōu)閚etns1選擇另一個(gè) IP 網(wǎng)絡(luò),一切就沒(méi)問(wèn)題了。然而,多個(gè)容器位于一個(gè) IP 網(wǎng)絡(luò)中是一個(gè)合理的用例。因此,我們需要以某種方式調(diào)整veth方法…

看看 Linux 網(wǎng)橋——另一種虛擬網(wǎng)絡(luò)設(shè)施!Linux 網(wǎng)橋的行為就像一個(gè)網(wǎng)絡(luò)交換機(jī)。它會(huì)在連接到它的接口之間轉(zhuǎn)發(fā)數(shù)據(jù)包。因?yàn)樗且粋€(gè)交換機(jī),所以它是在 L2(即以太網(wǎng))層完成這項(xiàng)工作的。

讓我們?cè)囍僮飨掳?。但首先,我們需要清理現(xiàn)有的設(shè)置,因?yàn)榈侥壳盀橹?,我們所做的一些配置更改?shí)際上已經(jīng)不再需要了。刪除網(wǎng)絡(luò)命名空間就足夠了:

$ sudo ip netns delete netns0
$ sudo ip netns delete netns1

# But if you still have some leftovers...
$ sudo ip link delete veth0
$ sudo ip link delete ceth0
$ sudo ip link delete veth1
$ sudo ip link delete ceth1

快速重建兩個(gè)容器。注意,我們沒(méi)有給新的veth0和veth1設(shè)備分配任何 IP 地址:

$ 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

確保主機(jī)上沒(méi)有新路由:

$ 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

最后,創(chuàng)建網(wǎng)橋接口:

$ sudo ip link add br0 type bridge
$ sudo ip link set br0 up

現(xiàn)在,將veth0和veth1兩端都連接到網(wǎng)橋上:

$ sudo ip link set veth0 master br0
$ sudo ip link set veth1 master br0

image.png

然后檢查容器之間的連接:

$ 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

真令人愉快!一切正常。使用這種新方法,我們根本沒(méi)有配置veth0和veth1。我們只在ceth0和ceth1端分配了兩個(gè) IP 地址。但是,由于它們都在同一個(gè)以太網(wǎng)段(記住,我們將它們連接到虛擬交換機(jī)),所以 L2 層上有連接:

$ sudo nsenter --net=/var/run/netns/netns0
$ ip neigh
172.18.0.20 dev ceth0 lladdr 6e:9c:ae:02:60:de STALE
$ exit

$ sudo nsenter --net=/var/run/netns/netns1
$ ip neigh
172.18.0.10 dev ceth1 lladdr 66:f3:8c:75:09:29 STALE
$ exit

恭喜,我們學(xué)會(huì)了 如何將容器變成友好的鄰居,防止它們相互干擾,并保持連接性。

訪問(wèn)外部世界(IP 路由和偽裝)

容器之間可以通信了。但它們可以和主機(jī)(即根命名空間)通信嗎?

$ sudo nsenter --net=/var/run/netns/netns0
$ ping 10.0.2.15  # eth0 address
connect: Network is unreachable

很明顯,netns0中沒(méi)有相應(yīng)的路由:

$ ip route
172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10

根命名空間也不能和容器通信:

# Use exit to leave `netns0` first:
$ 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

為了在根命名空間和容器命名空間之間建立連接,我們需要為網(wǎng)橋網(wǎng)絡(luò)接口分配 IP 地址:

$ sudo ip addr add 172.18.0.1/16 dev br0

一旦我們給網(wǎng)橋接口分配了 IP 地址,我們的主機(jī)路由表上就會(huì)多一條路由:

$ ip route
# ... omitted lines ...
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 網(wǎng)橋接口的能力,但它們?nèi)匀粺o(wú)法連接到主機(jī)的eth0。我們需要為容器添加默認(rèn)路由:

$ 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

# And repeat the change for `netns1`

這項(xiàng)更改基本上把主機(jī)變成了路由器,網(wǎng)橋接口成了容器的默認(rèn)網(wǎng)關(guān)。

image.png

很好,我們將容器與根命名空間連接起來(lái)了。現(xiàn)在,讓我們嘗試將它們與外部世界連接起來(lái)。默認(rèn)情況下,在 Linux 中數(shù)據(jù)包轉(zhuǎn)發(fā)(即路由器功能)是禁用的。我們需要打開(kāi)它:

# In the root namespace
sudo bash -c echo 1 > /proc/sys/net/ipv4/ip_forward

又到我最喜歡的部分了,檢查連接:

$ sudo nsenter --net=/var/run/netns/netns0
$ ping 8.8.8.8
# hangs indefinitely long for me...

還是不行。我們漏了什么嗎?如果容器向外部世界發(fā)送數(shù)據(jù)包,那么目標(biāo)服務(wù)器將不能將數(shù)據(jù)包發(fā)送回容器,因?yàn)槿萜鞯?IP 地址是私有的。也就是說(shuō),只有本地網(wǎng)絡(luò)才知道特定 IP 的路由規(guī)則。世界上有很多容器共享完全相同的私有 IP 地址172.18.0.10。

解決這個(gè)問(wèn)題的方法叫做網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)。在進(jìn)入外部網(wǎng)絡(luò)前,由容器發(fā)出的數(shù)據(jù)包將其源 IP 地址替換為主機(jī)的外部接口地址。主機(jī)還將跟蹤所有現(xiàn)有的映射,并且在數(shù)據(jù)包到達(dá)時(shí),它會(huì)在將其轉(zhuǎn)發(fā)回容器之前還原 IP 地址。聽(tīng)起來(lái)很復(fù)雜,但我有個(gè)好消息要告訴你!有了 iptables 模塊,我們只需要一個(gè)命令就可以實(shí)現(xiàn):

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

這個(gè)命令相當(dāng)簡(jiǎn)單。我們正在向POSTROUTING鏈的nat表添加一條新規(guī)則,要求偽裝所有源自172.18.0.0/16網(wǎng)絡(luò)的數(shù)據(jù)包,但不是通過(guò)網(wǎng)橋接口。檢查連接:

$ 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

注意,我們遵循的是默認(rèn)允許(by default - allow)策略,這在現(xiàn)實(shí)世界中可能相當(dāng)危險(xiǎn)。對(duì)于每個(gè)鏈,主機(jī)默認(rèn)的 iptables 策略都是ACCEPT:

sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

相反,作為一個(gè)很好的例子,Docker 默認(rèn)限制了一切,然后只啟用已知路徑的路由。以下是在 CentOS 8 機(jī)器上(在 5005 端口上暴露了單個(gè)容器)Docker 守護(hù)進(jìn)程生成的轉(zhuǎn)儲(chǔ)規(guī)則:

$ sudo iptables -t filter --list-rules
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-ISOLATION-STAGE-1
-N DOCKER-ISOLATION-STAGE-2
-N DOCKER-USER
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATEDESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN

$ sudo iptables -t nat --list-rules
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P POSTROUTING ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5005 -j DNAT --to-destination 172.17.0.2:5000

$ sudo iptables -t mangle --list-rules
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT

$ sudo iptables -t raw --list-rules
-P PREROUTING ACCEPT

-P OUTPUT ACCEPT

從外部訪問(wèn)容器(端口發(fā)布)

我們都知道,有一種做法是將容器端口發(fā)布到主機(jī)的部分(或全部)接口。但端口發(fā)布的真正含義是什么?

假設(shè)我們有一個(gè)在容器內(nèi)運(yùn)行的服務(wù)器

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

如果我們?cè)噲D從主機(jī)向這個(gè)服務(wù)器進(jìn)程發(fā)送一個(gè) HTTP 請(qǐng)求,一切都沒(méi)問(wèn)題(好吧,根命名空間和所有容器接口之間都有連接,為什么沒(méi)有呢?):

# From root namespace
$ curl 172.18.0.10:5000

# ... omited lines ...

但是,如果我們要從外部訪問(wèn)該服務(wù)器,我們將使用哪個(gè) IP 地址?我們知道的唯一 IP 地址可能是主機(jī)的外部接口地址eth0:

$ curl 10.0.2.15:5000
curl: (7) Failed to connect to 10.0.2.15 port 5000: Connection refused

因此,我們需要找到一種方法,將任何到達(dá)主機(jī)eth0接口 5000 端口的數(shù)據(jù)包轉(zhuǎn)發(fā)到目的地172.18.0.10:5000。或者,換句話說(shuō),我們需要在主機(jī)的eth0接口上發(fā)布容器的 5000 端口。iptables 拯救了我們!

# External traffic
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
# Local traffic (since it doesnt pass the PREROUTING chain)
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 攔截橋接網(wǎng)絡(luò)上的流量:

sudo modprobe br_netfilter

測(cè)試時(shí)間!

curl 10.0.2.15:5000


# ... omited lines ...

理解 Docker 網(wǎng)絡(luò)驅(qū)動(dòng)

好的,我們能用這些無(wú)用的知識(shí)做什么呢?例如,我們可以試著理解一些 Docker 網(wǎng)絡(luò)模式!

https://docs.docker.com/network/#network-drivers

讓我們從--network host模式開(kāi)始。試著比較下命令ip link和sudo docker run -it——rm——network host alpine ip link的輸出。想不到,它們居然一模一樣!即在host模式下,Docker 不使用網(wǎng)絡(luò)命名空間隔離,容器工作在根網(wǎng)絡(luò)命名空間中,并與主機(jī)共享網(wǎng)絡(luò)堆棧。

下一個(gè)模式是--network none。sudo docker run -it --rm --network none alpine ip link命令只顯示了一個(gè)環(huán)回網(wǎng)絡(luò)接口。這與我們對(duì)新創(chuàng)建的網(wǎng)絡(luò)命名空間的觀察非常相似。也就是在我們添加任何veth設(shè)備之前。

最后但同樣重要的是--network bridge(默認(rèn))模式。這正是我們?cè)谡恼轮性噲D再現(xiàn)的。我建議你試用下ip和iptables命令,并從主機(jī)和容器的角度檢查網(wǎng)絡(luò)堆棧。

附:無(wú)根容器和網(wǎng)絡(luò)

podman容器管理器的一個(gè)很好的特性是針對(duì)無(wú)根容器的。然而,你可能已經(jīng)注意到,我們?cè)诒疚闹惺褂昧舜罅縮udo升級(jí)。換句話說(shuō),權(quán)限就不可能配置網(wǎng)絡(luò)。Podman 的 rootfull 網(wǎng)絡(luò)方法和 docker 非常接近。

https://www.redhat.com/sysadmin/container-networking-podman

但是當(dāng)涉及到無(wú)根容器時(shí),podman 依賴于 slirp4netns 項(xiàng)目:
從 Linux 3.8 開(kāi)始,非特權(quán)用戶可以創(chuàng)建 network_namespaces(7) 和 user_namespaces(7) 了。但是,非特權(quán)網(wǎng)絡(luò)命名空間并不是很有用,因?yàn)樵谥鳈C(jī)和網(wǎng)絡(luò)命名空間之間創(chuàng)建 veth(4) 對(duì)仍然需要 root 特權(quán)。(即沒(méi)有網(wǎng)絡(luò)連接)

通過(guò)將網(wǎng)絡(luò)命名空間中的 TAP 設(shè)備連接到用戶模式 TCP/IP 堆棧(“slirp”),slirp4netns 允許以完全非特權(quán)的方式將網(wǎng)絡(luò)命名空間連接到網(wǎng)絡(luò)。

無(wú)根網(wǎng)絡(luò)有很大的局限性:“從技術(shù)上講,容器本身沒(méi)有 IP 地址,因?yàn)闆](méi)有根權(quán)限,網(wǎng)絡(luò)設(shè)備關(guān)聯(lián)就無(wú)法實(shí)現(xiàn)。此外,無(wú)根容器無(wú)法 ping,因?yàn)樗鄙?ping 命令所需的 CAP_NET_RAW 安全能力。”但這總比完全沒(méi)有連接好。

https://www.redhat.com/sysadmin/container-networking-podman

小結(jié)

本文探討的組織容器網(wǎng)絡(luò)的方法只是其中一種可能的方法(可能是使用最廣泛的一種)。還有很多其他的方法,通過(guò)官方或第三方插件實(shí)現(xiàn),但它們都嚴(yán)重依賴于 Linux 網(wǎng)絡(luò)可視化工具。因此,容器化可以被視為虛擬化技術(shù)。

翻譯自:https://iximiuz.com/en/posts/container-networking-is-simple/

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/126125.html

相關(guān)文章

  • #yyds干貨盤點(diǎn)#學(xué)不懂Netty?看不懂源碼?不存在的,這篇文章把手帶你閱讀Netty源碼

    摘要:簡(jiǎn)單來(lái)說(shuō)就是把注冊(cè)的動(dòng)作異步化,當(dāng)異步執(zhí)行結(jié)束后會(huì)把執(zhí)行結(jié)果回填到中抽象類一般就是公共邏輯的處理,而這里的處理主要就是針對(duì)一些參數(shù)的判斷,判斷完了之后再調(diào)用方法。 閱讀這篇文章之前,建議先閱讀和這篇文章關(guān)聯(lián)的內(nèi)容。 1. 詳細(xì)剖析分布式微服務(wù)架構(gòu)下網(wǎng)絡(luò)通信的底層實(shí)現(xiàn)原理(圖解) 2. (年薪60W的技巧)工作了5年,你真的理解Netty以及為什么要用嗎?(深度干貨)...

    zsirfs 評(píng)論0 收藏0
  • CloudBest:年度復(fù)盤丨盤點(diǎn)2020無(wú)處不在的「云原生」

    摘要:華為云華為云在云原生這場(chǎng)游戲中,最具競(jìng)爭(zhēng)力的玩家之一。年,金山云在云原生領(lǐng)域推出了三款重磅產(chǎn)品星曜裸金屬服務(wù)器云服務(wù)器和云盤。在線上智博會(huì)上,浪潮云發(fā)布了經(jīng)過(guò)全新迭代升級(jí)的浪潮云,進(jìn)一步提升平臺(tái)云原生服務(wù)能力。面對(duì)數(shù)字時(shí)代復(fù)雜系統(tǒng)的不確定性,傳統(tǒng)的 IT 應(yīng)用架構(gòu)研發(fā)交付周期長(zhǎng)、維護(hù)成本高、創(chuàng)新升級(jí)難,煙囪式架構(gòu),開(kāi)放性差、組件復(fù)用度低,這些都成為了企業(yè)業(yè)務(wù)快速增長(zhǎng)的瓶頸。而云原生以其敏捷、...

    Tecode 評(píng)論0 收藏0
  • Java后端

    摘要:,面向切面編程,中最主要的是用于事務(wù)方面的使用。目標(biāo)達(dá)成后還會(huì)有去構(gòu)建微服務(wù),希望大家多多支持。原文地址手把手教程優(yōu)雅的應(yīng)用四手把手實(shí)現(xiàn)后端搭建第四期 SpringMVC 干貨系列:從零搭建 SpringMVC+mybatis(四):Spring 兩大核心之 AOP 學(xué)習(xí) | 掘金技術(shù)征文 原本地址:SpringMVC 干貨系列:從零搭建 SpringMVC+mybatis(四):Sp...

    joyvw 評(píng)論0 收藏0
  • 【 全干貨 】5 分鐘帶你看懂 Docker !

    摘要:本文從定義,作用,技術(shù)架構(gòu),安裝和使用等全方位帶你看懂。如圖中左邊紅框中和右邊的紅框中都唯一表示為同一個(gè)鏡像。最后,于開(kāi)發(fā)者而言提供了一種開(kāi)發(fā)環(huán)境的管理辦法,與測(cè)試人員而言保證了環(huán)境的同步,于運(yùn)維人員提供了可移植的標(biāo)準(zhǔn)化部署流程。 作者丨唐文廣:騰訊工程師,負(fù)責(zé)無(wú)線研發(fā)部地圖測(cè)試。 導(dǎo)語(yǔ):Docker,近兩年才流行起來(lái)的超輕量級(jí)虛擬機(jī),它可以讓你輕松完成持續(xù)集成、自動(dòng)交付、自動(dòng)部署...

    Edison 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<