自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊容器網(wǎng)絡實現(xiàn)原理

網(wǎng)絡 網(wǎng)絡管理
Linux 提供了 bridge 虛擬網(wǎng)絡設備(類似交換機的能力),并且提供了 Veth Pair 虛擬設備(Veth Pair 的虛擬設備被創(chuàng)建出來之后,總是以兩張?zhí)摂M網(wǎng)卡(Veth Peer)的形式成對出現(xiàn)。并且從其中一個網(wǎng)卡發(fā)出的數(shù)據(jù)包,可以直接出現(xiàn)在與它對應的另一張網(wǎng)卡上,哪怕這兩張網(wǎng)卡在不同的 network namespace 中)。

基本原理

Linux 容器假如使用了 network namespace,那么容器將會有自己的一個網(wǎng)絡棧,而這個網(wǎng)絡棧,就包括了:網(wǎng)卡、回環(huán)設備、路由表和 iptables 規(guī)則等。這些要素,其實就構(gòu)成了進程發(fā)起和響應網(wǎng)絡請求的基本環(huán)境,擁有了屬于自己的 IP 地址和端口。雖然容器可以直接使用宿主機網(wǎng)絡棧的方式,從而為容器提供良好的網(wǎng)絡性能,但是這樣也會不可避免地引入共享網(wǎng)絡資源的問題,比如端口沖突。

但是被隔離在自己 network namespace 中的容器如何跟同一宿主機上但處于不同 network namespace 里的容器進程進行通信呢?如何跟容器所在宿主機進行通信呢?如何訪問外部主機的?或者提供外部可訪問的服務呢?

圖片圖片

把每一個容器都看作一臺主機,它們有一套獨立的“網(wǎng)絡?!薄D敲慈萜髋c同一臺上宿主機的通信,或者與宿主機的通信,其實就相當于兩臺主機之間的通信。而實現(xiàn)兩臺主機之間通信最直接的方式,就是用一根網(wǎng)線將兩臺主機連接起來。如果想要實現(xiàn)多臺主機之間的通信,那就需要用網(wǎng)線,把它們連接在一臺交換機上。

Linux 提供了 bridge 虛擬網(wǎng)絡設備(類似交換機的能力),并且提供了 Veth Pair 虛擬設備(Veth Pair 的虛擬設備被創(chuàng)建出來之后,總是以兩張?zhí)摂M網(wǎng)卡(Veth Peer)的形式成對出現(xiàn)。并且從其中一個網(wǎng)卡發(fā)出的數(shù)據(jù)包,可以直接出現(xiàn)在與它對應的另一張網(wǎng)卡上,哪怕這兩張網(wǎng)卡在不同的 network namespace 中)。

因此,只需要通過上述虛擬設備,將 Veth Pair 一端連在 Container 上,作為 Container 的 eth0,另外一端連接在網(wǎng)橋上,同時給網(wǎng)橋分配一個 IP 地址(此時網(wǎng)橋也作為了宿主機的一個網(wǎng)絡接口),就可以實現(xiàn)容器與同一臺上宿主機的通信,或者與宿主機的通信。如圖所示,

之后在上述的基礎之上,在宿主機上通過路由和 NAT 的方式,就可以實現(xiàn)容器訪問外部服務,或者為外部提供服務。

圖片圖片

除了 bridge 的方式之外,還可以通過其他方式來實現(xiàn),但是目前普遍的方式是采用上述的方式。比如,Docker 項目默認就是使用上述方式實現(xiàn)的。除了 bridge+veth pair 的方式之外,容器還可以選擇其他的網(wǎng)絡配置方式。

  • 比如 Docker 的文檔中就提到了 macvlan 的配置方式,和 macvlan 很類似的方式還有 ipvlan。它們的相同點就是都可以在一個物理的網(wǎng)絡接口上再配置幾個虛擬的網(wǎng)絡接口。在這些虛擬的網(wǎng)絡接口上,都可以配置獨立的 IP,并且這些 IP 可以屬于不同的 Namespace。而不同點是,對于 macvlan,每個虛擬網(wǎng)絡接口都有自己獨立的 mac 地址;而 ipvlan 的虛擬網(wǎng)絡接口是和物理網(wǎng)絡接口共享同一個 mac 地址。由于 ipvlan/macvlan 網(wǎng)絡接口直接掛載在物理網(wǎng)絡接口上,此時數(shù)據(jù)包發(fā)送的過程中只會經(jīng)過容器內(nèi)的協(xié)議棧配置,比如容器自己的 iptables 規(guī)則,而不會經(jīng)過宿主機的 iptables 規(guī)則。因此,對于會用到宿主機 iptables 規(guī)則的容器,比如 Kubernetes 里使用 service 的容器,就不能工作了。

圖片圖片

bridge 網(wǎng)絡模型通信舉例

容器->容器(同一宿主機)

  • 在容器 container1 中查看相應的路由:這個容器里有一張叫作 eth0 的網(wǎng)卡,它正是一個 Veth Pair 設備在容器里的這一端。所有對 172.17.0.0/16 網(wǎng)段的請求,都會被交給 eth0 來處理(第二條 172.17.0.0 路由規(guī)則)。
$ route
Kernel IP routing table
Destination  Gateway    Genmask   Flags  Metric  Ref  Use  Iface
default    172.17.0.1   0.0.0.0   UG    0    0   0   eth0
172.17.0.0   0.0.0.0    255.255.0.0 U    0    0   0   eth0

而這個 Veth Pair 設備的另一端,則在宿主機上。你可以通過查看宿主機的網(wǎng)絡設備看到它,如下所示??梢钥吹剑琧ontainer1 對應的 Veth Pair 設備,在宿主機上是一張?zhí)摂M網(wǎng)卡。它的名字叫作 veth9c02e56。并且,通過 brctl show 的輸出,你可以看到這張網(wǎng)卡被“插”在了 docker0 上。

# 在宿主機上
$ ifconfig
...
docker0   Link encap:Ethernet  HWaddr 02:42:d8:e4:df:c1  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:d8ff:fee4:dfc1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:309 errors:0 dropped:0 overruns:0 frame:0
          TX packets:372 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:0 
          RX bytes:18944 (18.9 KB)  TX bytes:8137789 (8.1 MB)
veth9c02e56 Link encap:Ethernet  HWaddr 52:81:0b:24:3d:da  
          inet6 addr: fe80::5081:bff:fe24:3dda/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:288 errors:0 dropped:0 overruns:0 frame:0
          TX packets:371 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:0 
          RX bytes:21608 (21.6 KB)  TX bytes:8137719 (8.1 MB)
          
$ brctl show
bridge name  bridge id      STP enabled  interfaces
docker0     8000.0242d8e4dfc1 no       veth9c02e56
  • 當在 container1 容器里訪問 container2 容器的 IP 地址(比如 ping 172.17.0.3)的時候,這個目的 IP 地址會匹配到 container1 容器里的路由規(guī)則,相應的路由規(guī)則的網(wǎng)關(gateway)是 0.0.0.0,這就意味著這是一條直連規(guī)則:即凡是匹配到這條規(guī)則的 IP 包,應該經(jīng)過本機的 eth0 網(wǎng)卡,通過二層網(wǎng)絡直接發(fā)往目的主機。
    此時則需要知道 172.17.0.3 這個 IP 地址對應的 MAC 地址,因此 container1 容器的網(wǎng)絡協(xié)議棧,會通過 eth0 網(wǎng)卡發(fā)送一個 ARP 廣播,來通過 IP 地址查找對應的 MAC 地址。由于一張?zhí)摂M網(wǎng)卡被“插”在網(wǎng)橋上后,它就會變成該網(wǎng)橋的“從設備”。從設備會被“剝奪”調(diào)用網(wǎng)絡協(xié)議棧處理數(shù)據(jù)包的資格,從而“降級”成為網(wǎng)橋上的一個端口。這個端口唯一的作用,就是接收流入的數(shù)據(jù)包,而這些數(shù)據(jù)包的轉(zhuǎn)發(fā)或者丟棄則全交由對應的網(wǎng)橋處理(container1 中的 eth0 網(wǎng)卡對應的 Veth Pair 其實就相當于一個網(wǎng)絡端口,而 eth0 和它對應的 veth pari 的連接就相當于一根網(wǎng)線,這根“網(wǎng)線”將 containerd 連到了網(wǎng)橋上。
    所以,在收到這些 ARP 請求之后,docker0 網(wǎng)橋就會扮演二層交換機的角色,把 ARP 廣播轉(zhuǎn)發(fā)到其他被“插”在 docker0 上的虛擬網(wǎng)卡上。因此,container2 容器的 eth0 就會收到這個 ARP 請求,從而將 172.17.0.3 所對應的 MAC 地址回復給 container1 容器。
  • 有了這個目的 MAC 地址,container1 容器的 eth0 網(wǎng)卡就可以將數(shù)據(jù)包發(fā)出去。docker0 處理轉(zhuǎn)發(fā)的過程,則繼續(xù)扮演二層交換機的角色。此時,docker0 網(wǎng)橋根據(jù)數(shù)據(jù)包的目的 MAC 地址(也就是 container2 容器的 MAC 地址),在它的 CAM 表(即交換機通過 MAC 地址學習維護的端口和 MAC 地址的對應表)里查到對應的端口(Port)為:vethb4963f3,然后把數(shù)據(jù)包發(fā)往這個端口。這個端口對應著 container2 中的 eth0 網(wǎng)卡(相當于有根網(wǎng)線將他們連起來)。因此數(shù)據(jù)包就進入到了 container2 容器的 Network Namespace 里。所以,container2 容器看到的情況是,它自己的 eth0 網(wǎng)卡上出現(xiàn)了流入的數(shù)據(jù)包。這樣,container2 的網(wǎng)絡協(xié)議棧就會對請求進行處理,最后將響應(Pong)返回到 containerd1。

圖片圖片

需要注意的是,在實際的數(shù)據(jù)傳遞時,上述數(shù)據(jù)的傳遞過程在網(wǎng)絡協(xié)議棧的不同層次,都有 Linux 內(nèi)核 Netfilter 參與其中。所以,如果感興趣的話,你可以通過打開 iptables 的 TRACE 功能查看到數(shù)據(jù)包的傳輸過程,具體方法如下所示,通過上述設置,你就可以在 /var/log/syslog 里看到數(shù)據(jù)包傳輸?shù)娜罩玖恕?/p>

# 在宿主機上執(zhí)行
$ iptables -t raw -A OUTPUT -p icmp -j TRACE
$ iptables -t raw -A PREROUTING -p icmp -j TRACE

宿主機->容器

當在主機上訪問容器的時候,訪問請求的數(shù)據(jù)包,

  • 首先根據(jù)宿主機的路由規(guī)則到達 docker0 網(wǎng)橋。
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
  • 然后再被轉(zhuǎn)發(fā)到對應的 Veth Pair 設備,最后出現(xiàn)在容器里。

圖片圖片

容器->外部主機

  • 當一個容器試圖連接到另外一個宿主機時,比如:ping 10.168.0.3,它發(fā)出的請求數(shù)據(jù)包,首先經(jīng)過 docker0 網(wǎng)橋出現(xiàn)在宿主機上。
  • 之后根據(jù)宿主機的路由表里的路由規(guī)則(10.168.0.0/24 via eth0)),將對 10.168.0.3 的訪問請求交給宿主機的 eth0 處理。
  • 同時對數(shù)據(jù)包進行源地址替換(SNAT)。這是因為容器通常使用的是一個私有的、非路由的 IP 地址(如172.17.0.2),這些地址在宿主機的網(wǎng)絡之外是不可見的。宿主機網(wǎng)絡之外用于通信的地址,還得是宿主機。如果不進行 SNAT 的話,那么在外部網(wǎng)絡中無法進行正確的路由。下面是 netfilter POSTROUTING 階段的規(guī)則,包含了一條規(guī)則:source 如果是 172.17.0.0/16,但是出口用的網(wǎng)絡接口不是 docker 的話,則進行 SNAT。
Chain POSTROUTING (policy ACCEPT 2469K packets, 176M bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0
  • 接下來的數(shù)據(jù)包就會經(jīng)宿主機的 eth0 網(wǎng)卡轉(zhuǎn)發(fā)到宿主機網(wǎng)絡上,最終到達 10.168.0.3 對應的宿主機上(當然這兩臺宿主機本身是要連通的)。

當遇到容器連不通“外網(wǎng)”的時候,你都應該先試試 docker0 網(wǎng)橋能不能 ping 通,然后查看一下跟 docker0 和 Veth Pair 設備相關的 iptables 規(guī)則是不是有異常,往往就能夠找到問題的答案了。

圖片圖片

外部主機->容器

如果一個容器想要對外提供服務的話,此時需要使用 NAT 的能力,將提供服務的端口映射到 Docker 宿主機端口上。在實現(xiàn)原理上,其實就是在 netfilter PREROUTING 階段進行目的地址轉(zhuǎn)換(DNAT)。

  • 當外部主機訪問宿主機的 8090 端口,也就是訪問 10.168.0.2:8090 的時候。
  • 訪問數(shù)據(jù)包會先到宿主機網(wǎng)絡,先經(jīng)過 netfilter PREROUTING 階段。PREROUTING 中有一條規(guī)則是對于目的端口是 8090 的數(shù)據(jù)包來說,它的目的地址和端口會被替換為 172.17.0.2:8090。因此,任何訪問宿主機 8090 端口的數(shù)據(jù)包,都會被轉(zhuǎn)發(fā)到容器 172.17.0.2:8090 上。
Chain PREROUTING (policy ACCEPT 2587K packets, 122M bytes)
 pkts bytes target     prot opt in     out     source               destination
2637K  125M DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0
37821 2032K DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8090 to:172.17.0.2:8090

bridge 網(wǎng)絡模擬創(chuàng)建

當我們在使用容器的時候,容器引擎比如 Docker,會自動搭建好上述的網(wǎng)絡拓撲。下面從頭開始搭建上述的網(wǎng)絡拓撲。主要用到的是 ip netns 這個命令,通過它來對 network namespace 進行操作。

  • 首先,我們先啟動一個不帶網(wǎng)絡配置的容器??梢钥吹剑趩拥娜萜髦?,Network Namespace 里就只有 loopback 一個網(wǎng)絡設備,而沒有了 eth0 網(wǎng)絡設備了。
# docker run -d --name if-test --network none centos:8.1.1911 sleep 36000
cf3d3105b11512658a025f5b401a09c888ed3495205f31e0a0d78a2036729472
# docker exec -it if-test ip addr
1: 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
  • 完成剛才的設置以后,在這個容器的 Network Namespace 里建立 veth??梢詧?zhí)行一下這個腳本完成 veth 的創(chuàng)建和通信。
pid=$(ps -ef | grep "sleep 36000" | grep -v grep | awk '{print $2}')
echo $pid
ln -s /proc/$pid/ns/net /var/run/netns/$pid
 
# Create a pair of veth interfaces
ip link add name veth_host type veth peer name veth_container
# Put one of them in the new net ns
ip link set veth_container netns $pid
 
# In the container, setup veth_container
ip netns exec $pid ip link set veth_container name eth0
ip netns exec $pid ip addr add 172.17.1.2/16 dev eth0
ip netns exec $pid ip link set eth0 up
ip netns exec $pid ip route add default via 172.17.0.1
 
# In the host, set veth_host up
ip link set veth_host up

完成這些操作之后,就創(chuàng)建了一對 veth 虛擬設備接口,示意圖如下所示。

假如 veth_host 上加上一個 IP,172.17.1.1/16,就可以從容器里就可以 ping 通這個 IP 了。這也證明了從容器到宿主機可以利用這對 veth 接口來通訊了。

首先呢,我們先找到這個容器里運行的進程"sleep 36000"的 pid,通過 "/proc/$pid/ns/net"這個文件得到 Network Namespace 的 ID,這個 Network Namespace ID 既是這個進程的,也同時屬于這個容器。

然后我們在"/var/run/netns/"的目錄下建立一個符號鏈接,指向這個容器的 Network Namespace。完成這步操作之后,在后面的"ip netns"操作里,就可以用 pid 的值作為這個容器的 Network Namesapce 的標識了。

接下來呢,我們用 ip link 命令來建立一對 veth 的虛擬設備接口,分別是 veth_container 和 veth_host。從名字就可以看出來,veth_container 這個接口會被放在容器 Network Namespace 里,而 veth_host 會放在宿主機的 Host Network Namespace。

之后,用 ip link set veth_container netns $pid 把 veth_container 這個接口放入到容器的 Network Namespace 中。

再然后我們要把 veth_container 重新命名為 eth0,因為這時候接口已經(jīng)在容器的 Network Namesapce 里了,eth0 就不會和宿主機上的 eth0 沖突了。

最后對容器內(nèi)的 eht0,我們還要做基本的網(wǎng)絡 IP 和缺省路由配置。因為 veth_host 已經(jīng)在宿主機的 Host Network Namespace 了,就不需要我們做什么了,這時我們只需要 up 一下這個接口就可以了。

  • 完成上述的 veth 設備創(chuàng)建之后,就可以讓數(shù)據(jù)包從容器的 network namespace 發(fā)送到 host network namespace 了。到了 Host Network Namespace 之后就需要考慮,如何將數(shù)據(jù)包繼續(xù)從 eth0 接口發(fā)送出去。
  • 首先將第一步中建立的 veth_host 這個設備,接入到 docker0 這個 bridge 上即可。如果之前你在 veth_host 上設置了 IP 的,就需先運行一下"ip addr delete 172.17.1.1/16 dev veth_host",把 IP 從 veth_host 上刪除。
# ip addr delete 172.17.1.1/16 dev veth_host 
ip link set veth_host master docker0

上述完成之后,網(wǎng)絡拓撲如下所示。容器和 docker0 組成了一個子網(wǎng),docker0 上的 IP 就是這個子網(wǎng)的網(wǎng)關 IP。

  • 此時,數(shù)據(jù)包是在宿主機網(wǎng)絡中。由于要訪問外網(wǎng)的一個 IP 地址,因此此時宿主機要做的工作是轉(zhuǎn)發(fā)。因此,需要打開宿主機上的轉(zhuǎn)發(fā)功能。
$ iptables -P FORWARD ACCEPT
$ cat /proc/sys/net/ipv4/ip_forward
0
$ echo 1 > /proc/sys/net/ipv4/ip_forward
  • 完成上述的配置之后,可以從容器中 ping 通外網(wǎng)的 IP 地址了。
# docker exec -it if-test ping 39.106.233.176
PING 39.106.233.176 (39.106.233.176) 56(84) bytes of data.
64 bytes from 39.106.233.176: icmp_seq=1 ttl=77 time=359 ms
64 bytes from 39.106.233.176: icmp_seq=2 ttl=77 time=346 ms
^C
--- 39.106.233.176 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1ms
rtt min/avg/max/mdev = 345.889/352.482/359.075/6.593 ms

相關鏈接

  1. The Layers of the OSI Model Illustrated:https://www.lifewire.com/layers-of-the-osi-model-illustrated-818017
  2. 極客時間.張磊.《深入剖析Kubernetes》
  3. 容器網(wǎng)絡(一):https://morven.life/posts/networking-4-docker-sigle-host/
責任編輯:武曉燕 來源: 多選參數(shù)
相關推薦

2024-05-09 09:55:08

2024-12-23 15:05:29

2023-11-28 07:55:05

Calico容器網(wǎng)絡

2022-12-11 20:09:50

網(wǎng)絡編程通信

2021-07-14 14:05:24

Fragment項目結(jié)構(gòu)

2024-09-13 16:47:06

模型量化AI

2020-02-19 19:18:02

緩存查詢速度淘汰算法

2025-01-15 15:47:36

2022-05-24 12:34:32

Docker容器Linux容器進程

2023-06-30 07:51:44

springboot初始化邏輯

2023-04-28 08:43:46

2020-07-14 07:27:48

容器IoCSpring

2022-06-21 07:51:06

Redis高可用哨兵進程

2023-11-07 08:13:53

分布式網(wǎng)絡

2023-02-15 13:57:13

JavaSPI動態(tài)擴展

2023-07-03 09:59:00

并發(fā)編程并發(fā)容器

2022-03-04 08:45:11

Docker開源Linux

2022-01-05 00:03:32

場景容器Airflow

2017-05-16 15:33:42

Python網(wǎng)絡爬蟲核心技術框架

2021-11-16 08:29:36

Docker容器網(wǎng)絡
點贊
收藏

51CTO技術棧公眾號