目錄
一、今天我們要搞懂的實驗
二、前置網路知識
2.1、docker預設為我們建立的網路
2.2、怎麼理解docker0網橋
#2.3、什麼是veth-pair技術?
四、容器网络互通原理
五、实验环境
首頁 運維 Docker 一定要搞懂Docker容器間網路互聯原理

一定要搞懂Docker容器間網路互聯原理

Feb 06, 2022 am 06:00 AM
docker

這篇文章為大家帶來了docker容器間網路互聯原理的相關知識,希望對大家有幫助。

一定要搞懂Docker容器間網路互聯原理

一、今天我們要搞懂的實驗

一定要搞懂Docker容器間網路互聯原理

#如上紅字所描述:同一個宿主機上的不同容器之間的網路如何互通的? ? ?

二、前置網路知識

2.1、docker預設為我們建立的網路

我們安裝完docker之後,docker daemon會為我們自動建立3個網絡,如下:

Copy~]# docker network ls
NETWORK ID     NAME                DRIVER    SCOPE
e71575e3722a   bridge              bridge    local
ab8e3d45575c   host                host      local
0c9b7c1134ff   none                null      local
登入後複製

其實docker有4種網絡通訊模型,分別是:bridge、host、none、container

##預設的使用的網路模型是bridge,也是我們生產上會使用到的網路模型。

下文跟大家分享docker容器互通原理到時候呢,用到的也是bridge網路模型


2.2、怎麼理解docker0網橋

#另外,當我們安裝完docker之後,docker會為我們創建一個叫docker0的網路設備

透過ifconfig指令可以查看到它,看起來它貌似和eth0網路地位相當,像是一張網卡。然而並不是,docker0其實是一個Linux網橋

Copy[root@vip ~]# 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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:b4:97:ee brd ff:ff:ff:ff:ff:ff
    inet 10.4.7.99/24 brd 10.4.7.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:feb4:97ee/64 scope link
       valid_lft forever preferred_lft forever

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:db:fe:ff:db brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:dbff:fefe:ffdb/64 scope link
       valid_lft forever preferred_lft forever
登入後複製

何以見得?可以透過下面的指令查看作業系統上的網橋資訊

Copy ~]# yum install bridge-utils
 ~]# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242f0a8c0be   no      veth86e2ef2
                                                                    vethf0a8bcb
登入後複製

那大家怎麼理解Linux網橋的概念呢?

其實大家可以把docker0理解成一台虛擬的交換器!然後像下面這樣類比著理解,就會豁然開朗

一定要搞懂Docker容器間網路互聯原理

1、它好比是大學在機房上課時,老師旁邊的那個大大的交換機設備。

2、把機房裡的電腦都連接在交換器上,類比成docker 容器作為一台設備都連接著宿主機上的docker0。

3、把交換器和機房中的機器的ip在同一個網段,類比成docker0、和你啟動的docker容器的ip也同屬於172網段。

Copy# docker0 ip是:
 ~]# ifconfig
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:db:fe:ff:db brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:dbff:fefe:ffdb/64 scope link
       valid_lft forever preferred_lft forever

# 进入容器中查看ip是:
/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 13  bytes 1102 (1.0 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
登入後複製

類比成這樣:

一定要搞懂Docker容器間網路互聯原理


#2.3、什麼是veth-pair技術?

我們剛才做類比理解docker0的時候說:把機房裡的電腦都連接在交換器上,類比成docker 容器作為一台設備都連接著宿主機上的docker0。那具體的實現落地實現用的是啥技術呢?

答案是:veth pair

veth pair的全名是:virtual ethernet,就是虛擬的乙太網路卡。

說到乙太網路卡大家都不陌生呀,不就是我們常見的那種叫eth0或是ens的網路設備嗎?

那這個veth pair是怎麼玩的呢?有啥用呢?大家可以看下面這張圖

一定要搞懂Docker容器間網路互聯原理一定要搞懂Docker容器間網路互聯原理

veth-pair裝置總是會成對的出現,用於連接兩個不同network-namespace.

就上圖來說,從network-namespace1的veth0發送的資料會出現在network-namespace2的veth1裝置中。

雖然這種特性很好,但是如果出現有多個容器,你會發現組織架構會越來越複雜,越來越亂

一定要搞懂Docker容器間網路互聯原理

#不過好在我們已經循序漸進的了解Linux網橋(docker0),以及這裡的veth-pair設備,於是我們可以把整體的架構圖重新繪製成下面這樣

一定要搞懂Docker容器間網路互聯原理

因為不同容器有自己隔離後的network-namespace所以他們都有自己的網路協定堆疊

那我們能不能找到容器裡面的網路卡和實體機上的哪張卡是一對網路vethpair設備呢?

如下:

Copy# 进入容器
~]# docker exec -ti 545ed62d3abf /bin/bash

/# apt-get install ethtool
/# ethtool -S eth0
NIC statistics:
     peer_ifindex: 55
登入後複製

回到宿主機

Copy~]# ip addr
    ...
55: vethf0a8bcb@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether ae:eb:5c:2f:7d:c3 brd ff:ff:ff:ff:ff:ff link-netnsid 10
    inet6 fe80::aceb:5cff:fe2f:7dc3/64 scope link
       valid_lft forever preferred_lft forever
登入後複製

意思是就是說,容器545ed62d3abf的eth0網卡和宿主機透過ip addr指令查看的網路設備標號55的設備組成一對vethpair設備,彼此流量互通!

三、同一個區域網路中不同主機的互聯原理

先看個簡單的,同一個區域網路中的不同主機A、B之間是如何互聯交換資料的。如下圖

一定要搞懂Docker容器間網路互聯原理#

那,既然是同一个局域网中,说明A、B的ip地址在同一个网段,如上图就假设它们都在192.168.1.0网段。

还得再看下面这张OSI 7层网络模型图。

主机A向主机B发送数据,对主机A来说数据会从最上层的应用层一路往下层传递。比如应用层使用的http协议、传输层使用的TCP协议,那数据在往下层传递的过程中,会根据该层的协议添加上不同的协议头等信息。

一定要搞懂Docker容器間網路互聯原理

根据OSI7层网络模型的设定,对于接受数据的主机B来说,它会接收到很多数据包!这些数据包会从最下层的物理层依次往上层传递,依次根据每一层的网络协议进行拆包。一直到应用层取出主机A发送给他的数据。

那么问题来了,主机B怎么判断它收到的数据包是否是发送给自己的呢?万一有人发错了呢?

答案是:根据MAC地址,逻辑如下。

Copyif 收到的数据包.MAC地址 == 自己的MAC地址{
  // 接收数据
  // 处理数据包
}else{
 // 丢弃
}
登入後複製

那对于主机A来说,它想发送给主机B数据包,还不能让主机B把这个数据包扔掉,它只能中规中矩的按以太网网络协议要求封装将要发送出去的数据包,往下传递到数据链路层(这一层传输的数据要求,必须要有目标mac地址,因为数据链路层是基于mac地址做数据传输的)。

那数据包中都需要哪些字段呢?如下:

Copysrc ip = 192.168.1.2  //源ip地址,交换机
dst ip = 192.168.1.3  //目标ip地址
//本机的mac地址(保证从主机B回来的包正常送达主机A,且主机A能正常处理它)
src mac = 主机A的mac地址
dst mac = 主机B的mac地址//目标mac地址
登入後複製

其中的dst ip好说,我们可以直接固定写,或者通过DNS解析域名得到目标ip。

那dst mac怎么获取呢?

这就不得不说ARP协议了! ARP其实是一种地址解析协议,它的作用就是:以目标ip为线索,找到目的ip所在机器的mac地址。也就是帮我们找到dst mac地址!大概的过程如下几个step

一定要搞懂Docker容器間網路互聯原理

推荐阅读:白日梦的DNS笔记

简述这个过程:主机A想给主机B发包,那需要知道主机B的mac地址。

  1. 主机A查询本地的arp 高速缓存中是否已经存在dst ip和dst mac地址的映射关系了,如果已存在,那就直接用。
  2. 本地arp高速缓存中不存在dst ip和dst mac地址的映射关系的话那就只能广播arp请求包,同一网段的所有机器都能收到arp请求包。
  3. 收到arp请求包的机器会对比arp包中的src ip是否是自己的ip,如果不是则直接丢弃该arp包。如果是的话就将自己的mac地址写到arp响应包中。并且它会把请求包中src ip和src mac的映射关系存储在自己的本地。

补充:

交换机本身也有学习能力,他会记录mac地址和交换机端口的映射关系。比如:mac=a,端口为1。

那当它接收到数据包,并发现mac=a时,它会直接将数据扔向端口1。

嗯,在arp协议的帮助下,主机A顺利拿到了主机B的mac地址。于是数据包从网络层流转到数据链路层时已经被封装成了下面的样子:

Copysrc ip = 192.168.1.2
src mac = 主机A的mac地址
dst ip = 192.168.1.3
dst mac = 主机B的mac地址
登入後複製

网络层基于ip地址做数据做转发

数据链路基于mac地址做数据转发

根据OIS7层网络模型,我们都知道数据包经过物理层发送到机器B,机器B接收到数据包后,再将数据包向上流转,拆包。流转到主机B的数据链路层。

那主机B是如何判断这个在数据链路层的包是否是发给自己的呢?

答案前面说了,根据目的mac地址判断。

Copy// 主机B
if 收到的数据包.MAC地址 == 自己的MAC地址{
  if dst ip == 本机ip{
    // 本地处理数据包
  }else{
    // 查询路由表,根据路由表的规则,将数据包转某个某卡、或者默认网关
  }
}else{
 // 直接丢弃
}
登入後複製

这个例子比较简单,dst ip就是主机B的本机ip 所以它自己会处理这个数据包。

那数据包处理完之后是需要给主机A一个响应包,那问题又来了,响应包该封装成什么样子呢?对主机B来说响应包也需要src ip、src mac、dst ip、dst mac

Copysrc ip = 192.168.1.3
src mac = 主机B的mac地址
dst ip = 192.168.1.2
src mac = 主机A的mac地址 (之前通过arp记录在自己的arp高速缓存中了,所以,这次直接用)
登入後複製

同样的道理,响应包也会按照如下的逻辑被主机A接受,处理。

Copy// 主机A
if 收到的数据包.MAC地址 == 自己的MAC地址{
  if dst ip == 本机ip{
    // 本地处理数据包
  }else{
    // 查询路由表,根据路由表的规则,将数据包转某个某卡、或者默认网关
  }
}else{
 // 直接丢弃
}
登入後複製

这一次,让我在百度告诉你,当你请求www.baidu.com时都发生了什么?

四、容器网络互通原理

有了上面那些知识储备呢?再看我们今天要探究的问题,就不难了。

如下红字部分:同一个宿主机上的不同容器是如何互通的?

一定要搞懂Docker容器間網路互聯原理

那我们先分别登陆容器记录下他们的ip

Copy9001的ip是:172.17.0.2
9002的ip是:172.17.0.3
登入後複製

先看实验效果:在9001上curl9002

Copy/# curl 172.7.88.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
...
登入後複製

实验结果是网络互通!

我们再完善一下上面的图,把docker0、以及两个容器的ip补充上去,如下图:

一定要搞懂Docker容器間網路互聯原理

Docker容器间网络互联原理,讲不明白算我输

那两台机器之前要通信是要遵循OSI网络模型、和以太网协议的。

我们管172.17.0.2叫做容器2

我们管172.17.0.3叫做容器3

比如我们现在是从:容器2上curl 容器3,那么容器2也必须按照以太网协议将数据包封装好,如下

Copysrc ip = 172.17.0.2
src mac = 容器2的mac地址
dst ip = 172.17.0.3
dst mac = 容器3的mac地址 ???
登入後複製

那现在的问题是容器3的mac地址是多少?

删掉所有容器,重新启动,方便实验抓包

容器2会先查自己的本地缓存,如果之前没有访问过,那么缓存中也没有任何记录!

Copy:/# arp -n
登入後複製

不过没关系,还有arp机制兜底,于是容器2会发送arp请求包,大概如下

Copy1、这是一个arp请求包
2、我的ip地址是:172.17.0.2
3、我的mac地址是:容器2的mac地址
4、请问:ip地址为:172.17.0.3的机器,你的mac地址是多少?
登入後複製

容器2会查询自己的路由表,将这个arp请求从自己的gateway发送出去

Copy/# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.7.88.1      0.0.0.0         UG    0      0        0 eth0
172.7.88.0      0.0.0.0         255.255.255.0   U     0      0        0 eth0
登入後複製

我们发现容器2的网关对应的网络设备的ip就是docker0的ip地址,并且经由eth0发送出去!

哎?eth0不就是我们之前说的veth-pair设备吗?

并且我们通过下面的命令可以知道它的另一端对应着宿主机上的哪个网络设备:

Copy/# ethtool -S eth0
NIC statistics:
     peer_ifindex: 53
登入後複製

而且我们可以下面的小实验,验证上面的观点是否正确

Copy# 在容器中ping百度
~]# ping 220.181.38.148

# 在宿主机上抓包
~]# yum install tcpdump -y
~]# tcpdump -i ${vethpair宿主机侧的接口名} host 220.181.38.148

...
登入後複製

所以说从容器2的eth0出去的arp请求报文会同等的出现在宿主机的第53个网络设备上。

通过下面的这张图,你也知道第53个网络设备其实就是下图中的veth0-1

一定要搞懂Docker容器間網路互聯原理

所以这个arp请求包会被发送到docker0上,由docker0拿到这个arp包发现,目标ip是172.17.0.3并不是自己,所以docker0会进一步将这个arp请求报文广播出去,所有在172.17.0.0网段的容器都能收到这个报文!其中就包含了容器3!

那容器3收到这个arp报文后,会判断,哦!目标ip就是自己的ip,于是它将自己的mac地址填充到arp报文中返回给docker0!

同样的我们可以通过抓包验证,在宿主机上

Copy# 在172.17.0.2容器上ping172.17.0.3
/# ping 172.17.0.3

~]# tcpdump -i vethdb0d222
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vethdb0d222, link-type EN10MB (Ethernet), capture size 262144 bytes

17:25:30.218640 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28
17:25:30.218683 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28
17:25:30.218686 IP 172.17.0.2.54014 > 172.17.0.3.http: Flags [S], seq 3496600258, win 29200, options [mss 1460,sackOK,TS val 4503202 ecr 0,nop,wscale 7], length 0
登入後複製

于是容器2就拿到了容器3的mac地址,以太网数据包需要的信息也就齐全了!如下:

Copysrc ip = 172.17.0.2
src mac = 容器2的mac地址
dst ip = 172.17.0.3
dst mac = 容器3的mac地址
登入後複製

再之后容器2就可以和容器3正常互联了!

容器3会收到很多数据包,那它怎么知道哪些包是发给自己的,那些不是呢?可以参考如下的判断逻辑

Copyif 响应包.mac == 自己的mac{
 // 说明这是发给自己包,所以不能丢弃
  if 响应包.ip == 自己的ip{
    // 向上转发到osi7层网络模型的上层
  }else{
    // 查自己的route表,找下一跳
  }
}else{
 // 直接丢弃
}
登入後複製

五、实验环境

Copy# 下载
 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/changwu/nginx:1.7.9-nettools

# 先启动1个容器
 ~]# docker run --name mynginx1 -i -t -d -p 9001:80 nginx-1.7.9-nettools:latest
eb569b938c07e95ccccbfc654c1fee6364eea55b20f5394382ff42b4ccf96312

~]# docker run --name mynginx2 -i -t -d -p 9002:80 nginx-1.7.9-nettools:latest
545ed62d3abfd63aa9c3ae196e9d7fe6f59bbd2e9ae4e6f2bd378f23587496b7

# 验证
~]# curl 127.0.0.1:9001
登入後複製

推荐学习:《docker视频教程

以上是一定要搞懂Docker容器間網路互聯原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1317
25
PHP教程
1268
29
C# 教程
1246
24
docker怎麼退出容器 docker怎麼退出容器 Apr 15, 2025 pm 12:15 PM

退出 Docker 容器的四種方法:容器終端中使用 Ctrl D 快捷鍵容器終端中輸入 exit 命令宿主機終端中使用 docker stop &lt;container_name&gt; 命令宿主機終端中使用 docker kill &lt;container_name&gt; 命令(強制退出)

docker內的文件怎麼拷貝到外面 docker內的文件怎麼拷貝到外面 Apr 15, 2025 pm 12:12 PM

Docker 中將文件拷貝到外部主機的方法:使用 docker cp 命令:執行 docker cp [選項] &lt;容器路徑&gt; &lt;主機路徑&gt;。使用數據卷:在主機上創建目錄,在創建容器時使用 -v 參數掛載該目錄到容器內,實現文件雙向同步。

docker怎麼啟動容器 docker怎麼啟動容器 Apr 15, 2025 pm 12:27 PM

Docker 容器啟動步驟:拉取容器鏡像:運行 "docker pull [鏡像名稱]"。創建容器:使用 "docker create [選項] [鏡像名稱] [命令和參數]"。啟動容器:執行 "docker start [容器名稱或 ID]"。檢查容器狀態:通過 "docker ps" 驗證容器是否正在運行。

docker容器名稱怎麼查 docker容器名稱怎麼查 Apr 15, 2025 pm 12:21 PM

可以通過以下步驟查詢 Docker 容器名稱:列出所有容器(docker ps)。篩選容器列表(使用 grep 命令)。獲取容器名稱(位於 "NAMES" 列中)。

docker怎麼重啟 docker怎麼重啟 Apr 15, 2025 pm 12:06 PM

重啟 Docker 容器的方法:獲取容器 ID(docker ps);停止容器(docker stop &lt;container_id&gt;);啟動容器(docker start &lt;container_id&gt;);驗證重啟成功(docker ps)。其他方法:Docker Compose(docker-compose restart)或 Docker API(參考 Docker 文檔)。

docker怎麼啟動mysql docker怎麼啟動mysql Apr 15, 2025 pm 12:09 PM

在 Docker 中啟動 MySQL 的過程包含以下步驟:拉取 MySQL 鏡像創建並啟動容器,設置根用戶密碼並映射端口驗證連接創建數據庫和用戶授予對數據庫的所有權限

docker怎麼創建容器 docker怎麼創建容器 Apr 15, 2025 pm 12:18 PM

在 Docker 中創建容器: 1. 拉取鏡像: docker pull [鏡像名] 2. 創建容器: docker run [選項] [鏡像名] [命令] 3. 啟動容器: docker start [容器名]

docker怎麼查看日誌 docker怎麼查看日誌 Apr 15, 2025 pm 12:24 PM

查看 Docker 日誌的方法包括:使用 docker logs 命令,例如:docker logs CONTAINER_NAME使用 docker exec 命令運行 /bin/sh 並查看日誌文件,例如:docker exec -it CONTAINER_NAME /bin/sh ; cat /var/log/CONTAINER_NAME.log使用 Docker Compose 的 docker-compose logs 命令,例如:docker-compose -f docker-com

See all articles