成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊容器網絡實現原理

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

基本原理

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

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

圖片圖片

把每一個容器都看作一臺主機,它們有一套獨立的“網絡棧”。那么容器與同一臺上宿主機的通信,或者與宿主機的通信,其實就相當于兩臺主機之間的通信。而實現兩臺主機之間通信最直接的方式,就是用一根網線將兩臺主機連接起來。如果想要實現多臺主機之間的通信,那就需要用網線,把它們連接在一臺交換機上。

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

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

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

圖片圖片

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

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

圖片圖片

bridge 網絡模型通信舉例

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

  • 在容器 container1 中查看相應的路由:這個容器里有一張叫作 eth0 的網卡,它正是一個 Veth Pair 設備在容器里的這一端。所有對 172.17.0.0/16 網段的請求,都會被交給 eth0 來處理(第二條 172.17.0.0 路由規則)。
$ 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 設備的另一端,則在宿主機上。你可以通過查看宿主機的網絡設備看到它,如下所示。可以看到,container1 對應的 Veth Pair 設備,在宿主機上是一張虛擬網卡。它的名字叫作 veth9c02e56。并且,通過 brctl show 的輸出,你可以看到這張網卡被“插”在了 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 容器里的路由規則,相應的路由規則的網關(gateway)是 0.0.0.0,這就意味著這是一條直連規則:即凡是匹配到這條規則的 IP 包,應該經過本機的 eth0 網卡,通過二層網絡直接發往目的主機。
    此時則需要知道 172.17.0.3 這個 IP 地址對應的 MAC 地址,因此 container1 容器的網絡協議棧,會通過 eth0 網卡發送一個 ARP 廣播,來通過 IP 地址查找對應的 MAC 地址。由于一張虛擬網卡被“插”在網橋上后,它就會變成該網橋的“從設備”。從設備會被“剝奪”調用網絡協議棧處理數據包的資格,從而“降級”成為網橋上的一個端口。這個端口唯一的作用,就是接收流入的數據包,而這些數據包的轉發或者丟棄則全交由對應的網橋處理(container1 中的 eth0 網卡對應的 Veth Pair 其實就相當于一個網絡端口,而 eth0 和它對應的 veth pari 的連接就相當于一根網線,這根“網線”將 containerd 連到了網橋上。
    所以,在收到這些 ARP 請求之后,docker0 網橋就會扮演二層交換機的角色,把 ARP 廣播轉發到其他被“插”在 docker0 上的虛擬網卡上。因此,container2 容器的 eth0 就會收到這個 ARP 請求,從而將 172.17.0.3 所對應的 MAC 地址回復給 container1 容器。
  • 有了這個目的 MAC 地址,container1 容器的 eth0 網卡就可以將數據包發出去。docker0 處理轉發的過程,則繼續扮演二層交換機的角色。此時,docker0 網橋根據數據包的目的 MAC 地址(也就是 container2 容器的 MAC 地址),在它的 CAM 表(即交換機通過 MAC 地址學習維護的端口和 MAC 地址的對應表)里查到對應的端口(Port)為:vethb4963f3,然后把數據包發往這個端口。這個端口對應著 container2 中的 eth0 網卡(相當于有根網線將他們連起來)。因此數據包就進入到了 container2 容器的 Network Namespace 里。所以,container2 容器看到的情況是,它自己的 eth0 網卡上出現了流入的數據包。這樣,container2 的網絡協議棧就會對請求進行處理,最后將響應(Pong)返回到 containerd1。

圖片圖片

需要注意的是,在實際的數據傳遞時,上述數據的傳遞過程在網絡協議棧的不同層次,都有 Linux 內核 Netfilter 參與其中。所以,如果感興趣的話,你可以通過打開 iptables 的 TRACE 功能查看到數據包的傳輸過程,具體方法如下所示,通過上述設置,你就可以在 /var/log/syslog 里看到數據包傳輸的日志了。

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

宿主機->容器

當在主機上訪問容器的時候,訪問請求的數據包,

  • 首先根據宿主機的路由規則到達 docker0 網橋。
$ 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
  • 然后再被轉發到對應的 Veth Pair 設備,最后出現在容器里。

圖片圖片

容器->外部主機

  • 當一個容器試圖連接到另外一個宿主機時,比如:ping 10.168.0.3,它發出的請求數據包,首先經過 docker0 網橋出現在宿主機上。
  • 之后根據宿主機的路由表里的路由規則(10.168.0.0/24 via eth0)),將對 10.168.0.3 的訪問請求交給宿主機的 eth0 處理。
  • 同時對數據包進行源地址替換(SNAT)。這是因為容器通常使用的是一個私有的、非路由的 IP 地址(如172.17.0.2),這些地址在宿主機的網絡之外是不可見的。宿主機網絡之外用于通信的地址,還得是宿主機。如果不進行 SNAT 的話,那么在外部網絡中無法進行正確的路由。下面是 netfilter POSTROUTING 階段的規則,包含了一條規則:source 如果是 172.17.0.0/16,但是出口用的網絡接口不是 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
  • 接下來的數據包就會經宿主機的 eth0 網卡轉發到宿主機網絡上,最終到達 10.168.0.3 對應的宿主機上(當然這兩臺宿主機本身是要連通的)。

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

圖片圖片

外部主機->容器

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

  • 當外部主機訪問宿主機的 8090 端口,也就是訪問 10.168.0.2:8090 的時候。
  • 訪問數據包會先到宿主機網絡,先經過 netfilter PREROUTING 階段。PREROUTING 中有一條規則是對于目的端口是 8090 的數據包來說,它的目的地址和端口會被替換為 172.17.0.2:8090。因此,任何訪問宿主機 8090 端口的數據包,都會被轉發到容器 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 網絡模擬創建

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

  • 首先,我們先啟動一個不帶網絡配置的容器。可以看到,在啟動的容器中,Network Namespace 里就只有 loopback 一個網絡設備,而沒有了 eth0 網絡設備了。
# 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。可以執行一下這個腳本完成 veth 的創建和通信。
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

完成這些操作之后,就創建了一對 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,因為這時候接口已經在容器的 Network Namesapce 里了,eth0 就不會和宿主機上的 eth0 沖突了。

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

  • 完成上述的 veth 設備創建之后,就可以讓數據包從容器的 network namespace 發送到 host network namespace 了。到了 Host Network Namespace 之后就需要考慮,如何將數據包繼續從 eth0 接口發送出去。
  • 首先將第一步中建立的 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

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

  • 此時,數據包是在宿主機網絡中。由于要訪問外網的一個 IP 地址,因此此時宿主機要做的工作是轉發。因此,需要打開宿主機上的轉發功能。
$ iptables -P FORWARD ACCEPT
$ cat /proc/sys/net/ipv4/ip_forward
0
$ echo 1 > /proc/sys/net/ipv4/ip_forward
  • 完成上述的配置之后,可以從容器中 ping 通外網的 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. 容器網絡(一):https://morven.life/posts/networking-4-docker-sigle-host/
責任編輯:武曉燕 來源: 多選參數
相關推薦

2024-05-09 09:55:08

2024-12-23 15:05:29

2023-11-28 07:55:05

Calico容器網絡

2022-12-11 20:09:50

網絡編程通信

2021-07-14 14:05:24

Fragment項目結構

2024-09-13 16:47:06

模型量化AI

2020-02-19 19:18:02

緩存查詢速度淘汰算法

2025-01-15 15:47:36

2023-06-30 07:51:44

springboot初始化邏輯

2022-05-24 12:34:32

Docker容器Linux容器進程

2020-07-14 07:27:48

容器IoCSpring

2023-04-28 08:43:46

2022-06-21 07:51:06

Redis高可用哨兵進程

2023-11-07 08:13:53

分布式網絡

2022-03-04 08:45:11

Docker開源Linux

2023-07-03 09:59:00

并發編程并發容器

2022-01-05 00:03:32

場景容器Airflow

2023-02-15 13:57:13

JavaSPI動態擴展

2022-07-26 07:14:52

Docker宿主命令

2021-04-19 10:45:52

Webpack熱更新前端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 中文字幕 国产 | 在线中文字幕av | 91久久久久久久 | 91原创视频在线观看 | 欧美成人一区二免费视频软件 | 喷水毛片| 免费黄色片在线观看 | 黄视频网址 | 日本成人区 | 99爱在线 | 成年视频在线观看福利资源 | 美女黄视频网站 | 九九免费视频 | 欧美 日韩 中文 | 在线免费观看一区二区 | 久久久精品网 | 91久久久久久久久久久久久 | 日韩中文字幕 | 亚洲一级淫片 | www.色午夜.com | 亚洲欧美日韩国产综合 | 国产美女在线观看 | 国产网站在线免费观看 | 精品无码三级在线观看视频 | 中国91av| 一区二区视频在线 | 在线免费观看成年人视频 | 精品欧美视频 | 亚洲精品久久久久国产 | 在线观看免费国产 | av无遮挡 | 欧美精品久久久久久 | 久久av一区二区三区 | 日本人做爰大片免费观看一老师 | 亚洲精品区| 麻豆av电影网 | 久久免费精品视频 | 日韩精品视频在线播放 | 欧美日韩专区 | 日本视频中文字幕 | aa级毛片毛片免费观看久 |