引言
學習docker網路,可以帶著下面兩個問題來探討
- 容器之間可以相互訪問的原理
- 容器暴露埠后,通過宿主機訪問到容器內應用,并且對于訪問端而言不用感知容器存在的原理
Docker 本身的技術依賴Linux的內核虛擬化技術,所以為了能夠更好的理解Docker的網路實作,必須要對牽扯到的主要技術做些了解
用到的主要Linux技術點
- 網路命名空間(Network Namespace)
- veth設備對
- 網橋 bridge(docker0)
- iptables netfilter
- 路由
網路命名空間
-
Linux中為了支持網路協議堆疊的多個實體,引入了網路命名空間,可以簡單理解成相互不干擾、互相隔離的平行網路空間,通過創建不同的網路命名空間,就可以在一臺機器上模擬多個不同的網路環境,每個網路命名空間中,都可以有自己獨立的路由表,及獨立的iptables來提供包轉發、NAT等功能,Docker正是利用了網路空間的這個特性,實作容器之間的網路隔離(埠號相互不會沖突),
-
網路命名空間的操作(以下命令需要root權限操作)
- 創建命名空間
ip netns add <name>例:創建命名空間 netns1
ip netns add netns1- 在命名空間中執行命令
ip netns exec <name> <command>例:配置netns1中的veth1的地址,并將設備啟動
ip netns exec netns1 ip addr add 30.0.0.1/24 dev veth1 ip netns exec netns1 ip link set dev veth1 up或者
ip netns exec netns1 ifconfig veth1 30.0.0.1/24 up- 進入命名空間
ip netns exec <name> bash- 查看命名空間
ip netns list- 洗掉命名空間
ip netns delete <name>
veth設備對
-
veth指虛擬以太網口對(virtual ethernet),只能成對出現,不能單個存在,在Linux術語中把其中一個稱為另一個的peer,在Linux中所有的網路設備都只能存在于一個命名空間,物理設備通常只關聯到root命名空間中,veth可以通過命令來任意創建和銷毀,并可以關聯到指定的某個命名空間中,還可以通過命令操作其在不同的命名空間中移動,
前面提到網路命名空間代表的是一個獨立的協議堆疊,相互之間是隔離的,彼此無法直接通信,那如果想在不同的命名空間之間通信,就要用到veth設備對了,他的作用就像是一個管道,一端連著自己的命名空間協議堆疊,另一端連著另一個命名空間的peer,當veth設備在一端發送資料時,會直接將資料發送給自己的peer,并觸發peer接收資料,然后peer把收到的資料再提交到自己的網路協議堆疊進行處理,從而實作不同協議堆疊之間的資料傳輸,Veth的示意圖如下:

-
veth設備對的常用操作
- 創建veth設備對
ip link add <name1> type veth peer name <name2>例:創建name分別時veth1和veth2的設備對
ip link add veth1 type veth peer name veth2- 查看創建的veth資訊
ip link show或者
ip addr- 將veth移動到特定的網路空間中
ip link set <vethName> netns <netnsName>例:將veth1移動到netns1命名空間下
ip link set veth1 netns netns1
實驗1
為了驗證對網路命名空間和veth設備的理解,我們可以設計下面的場景,來加以證明

創建兩個命名空間,創建一個veth設備對,將設備對分別放到兩個命名空間中,給兩個veth添加網路地址,從兩個命名空間中分別ping對端,彼此能ping通,則說明veth對的確能打破不同命名空間的壁壘,實作通信
-
創建命名空間
root@chengf:~# ip netns add netns1 root@chengf:~# ip netns add netns2 root@chengf:~# ip netns list netns2 netns1 -
創建veth設備對
root@chengf:~# ip link add veth1 type veth peer name veth2 root@chengf:~# ip link show ... 58: veth2@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether fe:62:b8:f5:4a:68 brd ff:ff:ff:ff:ff:ff 59: veth1@veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 46:69:c0:3f:e2:27 brd ff:ff:ff:ff:ff:ff -
將veth設備對分別移到不同的命名空間下,執行移動后,可以看到在root空間下已經沒有了剛才創建的veth設備了
root@chengf:~# ip link set veth1 netns netns1 root@chengf:~# ip link set veth2 netns netns2 -
在對應的命名空間中查看,可以看到veth的確移動到對應的命名空間中
root@chengf:~# ip netns exec netns1 ip link show 1: 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 59: veth1@if58: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 46:69:c0:3f:e2:27 brd ff:ff:ff:ff:ff:ff link-netnsid 1
root@chengf:~# ip netns exec netns2 ip link show
1: lo:
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
58: veth2@if59: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fe:62:b8:f5:4a:68 brd ff:ff:ff:ff:ff:ff link-netnsid 0
```
-
給對應的veth設定網路地址,并且啟動veth設備
root@chengf:~# ip netns exec netns1 ip addr add 30.0.0.1/24 dev veth1 root@chengf:~# ip netns exec netns1 ip link set dev veth1 up root@chengf:~# ip netns exec netns2 ip addr add 30.0.0.2/24 dev veth2 root@chengf:~# ip netns exec netns2 ip link set dev veth2 up -
通過ip addr 命令產看啟動后的設備情況
root@chengf:~# ip netns exec netns1 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 59: veth1@if58: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 46:69:c0:3f:e2:27 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet 30.0.0.1/24 scope global veth1 valid_lft forever preferred_lft forever inet6 fe80::4469:c0ff:fe3f:e227/64 scope link valid_lft forever preferred_lft forever root@chengf:~# ip netns exec netns2 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 58: veth2@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether fe:62:b8:f5:4a:68 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 30.0.0.2/24 scope global veth2 valid_lft forever preferred_lft forever inet6 fe80::fc62:b8ff:fef5:4a68/64 scope link valid_lft forever preferred_lft forever -
在兩個命名空間中相互ping對方
root@chengf:~# ip netns exec netns1 ping -c 1 30.0.0.2 PING 30.0.0.2 (30.0.0.2) 56(84) bytes of data. 64 bytes from 30.0.0.2: icmp_seq=1 ttl=64 time=0.125 ms --- 30.0.0.2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.125/0.125/0.125/0.000 ms root@chengf:~# ip netns exec netns2 ping -c 1 30.0.0.1 PING 30.0.0.1 (30.0.0.1) 56(84) bytes of data. 64 bytes from 30.0.0.1: icmp_seq=1 ttl=64 time=0.084 ms --- 30.0.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.084/0.084/0.084/0.000 ms
可以看到通過veth設備對,的確能夠在不同的命名空間之間相互訪問,由于veth只能是一對一的,那我們有多個命名空間時(兩個以上docker容器),怎么相互訪問呢,這就是網橋(bridge)的功能了,結下來再看bridge的基本介紹,
網橋
-
Linux網橋的作用類似于物理網路設備中的交換機,是一個作業在二層的虛擬網路設備,有多個埠,資料從多個介面進,也可以從多個介面出,主要作用是把若干個網路介面打通,以使網路介面之間的報文能夠相互轉發,網橋能夠通過MAC地址自動學習,獲取到MAC地址和網路介面的關聯關系,當受到報文時,首先會決議收到的報文,讀取目標MAC地址,通過和自己的MAC記錄表對比,來決定轉發的目標網路介面,如果能找到MAC地址匹配的介面,就向特定的網路介面轉發,否則就將報文廣播到所有介面,
-
網橋是建立在從設備的基礎上作業的,從設備可以是物理設備,也可以是虛擬設備,當一個設備attach到一個網橋時,就類似于物理交換機上插入了一根網線從而和一個終端設備相連,從而實作連接在bridge上的各個彼此隔離的網路空間相互訪問,另外,因為bridge轉發是基于目的MAC地址和介面的映射完成的,bridge只要知道attach到自己的設備連接的對端設備的MAC地址就可以了,所以attach到bridge上的設備對應的IP和MAC就沒有作用了,實際上這些設備都被bridge一視同仁的當作介面對待,這些設備會被設定成接收任何資料包,最終資料包的處理:接收、轉發、丟棄、廣播等操作都由bridge來決定,
-
相比較于傳統的交換機,對于接收到的報文要么轉發,要么丟棄,運行于Linux內部網路堆疊里面的網橋,所在的機器本身就是一臺主機,有可能就是網路報文的目的地,其收到的報文出了轉發和丟棄外,有可能被送到網路協議堆疊的上層(網路層),從而被自己主機處理,如果給bridge分配一個IP,其他設備在訪問bridge時,就可以通過bridge連接的宿主機的網路協議堆疊來對資料進行處理,所以從這個層面來講,網橋也可以看作是一個三層設備,其作業示意圖如下(圖片來自網路):

-
網橋的常用操作
- 新增一個網橋
brctl addbr <name>- 洗掉一個網橋(洗掉前需要先停掉對應的設備)
brctl delbr <name>- 為網橋新加埠
brctl addif <brname> <ifname>例: 將eth0網卡追加到br001
brctl addif br001 eth0- 給網橋配置IP地址
ifconfig <name> <IP>- 啟動網橋設備
ifconfig <name> up- 停止網橋設備
ifconfig <name> down- 查看主機上的網橋
brctl show
實驗2
了解過了bridge的基本知識后,我們可以嘗試用下面的簡單實驗來測驗bridge的功能

創建三個命名空間,創建三對veth,三對veth中的其中一端分別放在三個命名空間中,另一端都放到網橋br001中,分配好地址空間后,相互能ping通,則證明網橋的確可以實作將資料根據目的發送到不同網路,
-
創建命名空間
root@chengf:~# ip netns add netns1 root@chengf:~# ip netns add netns2 root@chengf:~# ip netns add netns3 root@chengf:~# ip netns list netns3 netns2 netns1 -
創建veth對
root@chengf:~# ip link add veth001 type veth peer name peer-veth001 root@chengf:~# ip link add veth002 type veth peer name peer-veth002 root@chengf:~# ip link add veth003 type veth peer name peer-veth003 root@chengf:~# ip link show ... 51: peer-veth001@veth001: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 2e:8b:9e:92:76:28 brd ff:ff:ff:ff:ff:ff 52: veth001@peer-veth001: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 6a:9e:04:44:30:a9 brd ff:ff:ff:ff:ff:ff 53: peer-veth002@veth002: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 56:1f:8e:f5:8b:8c brd ff:ff:ff:ff:ff:ff 54: veth002@peer-veth002: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether f2:1a:b4:54:94:3d brd ff:ff:ff:ff:ff:ff 55: peer-veth003@veth003: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 1a:b9:5f:ab:6a:d2 brd ff:ff:ff:ff:ff:ff 56: veth003@peer-veth003: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 0a:84:78:42:8f:77 brd ff:ff:ff:ff:ff:ff -
將veth對中的一端移到對應命名空間中
root@chengf:~# ip link set veth001 netns netns1 root@chengf:~# ip link set veth002 netns netns2 root@chengf:~# ip link set veth003 netns netns3 -
創建bridge
root@chengf:~# brctl addbr br001 root@chengf:~# ip link show ... 57: br001: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 56:0b:ad:d5:fe:73 brd ff:ff:ff:ff:ff:ff -
veth對的另一端attach到bridge
root@chengf:~# brctl addif br001 peer-veth001 root@chengf:~# brctl addif br001 peer-veth002 root@chengf:~# brctl addif br001 peer-veth003 root@chengf:~# ip link show ... 51: peer-veth001@if52: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master br001 state DOWN mode DEFAULT group default qlen 1000 link/ether 2e:8b:9e:92:76:28 brd ff:ff:ff:ff:ff:ff link-netnsid 2 53: peer-veth002@if54: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master br001 state DOWN mode DEFAULT group default qlen 1000 link/ether 56:1f:8e:f5:8b:8c brd ff:ff:ff:ff:ff:ff link-netnsid 3 55: peer-veth003@if56: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master br001 state DOWN mode DEFAULT group default qlen 1000 link/ether 1a:b9:5f:ab:6a:d2 brd ff:ff:ff:ff:ff:ff link-netnsid 4 57: br001: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 1a:b9:5f:ab:6a:d2 brd ff:ff:ff:ff:ff:ff- 可以看到 br001 對應的mac地址變成了peer-veth003的地址(peer-veth001、peer-veth002、peer-veth003三個中最小的一個)
-
分別設定netns中的veth設備的IP地址,并啟動對應的設備
root@chengf:~# ip netns exec netns1 ifconfig veth001 30.0.1.1/16 up root@chengf:~# ip netns exec netns2 ifconfig veth002 30.0.1.2/16 up root@chengf:~# ip netns exec netns3 ifconfig veth003 30.0.1.3/16 up- 此時br001還沒有分配地址,所以相互是ping不通的
root@chengf:~# ip netns exec netns1 ping 30.0.1.2 PING 30.0.1.2 (30.0.1.2) 56(84) bytes of data. ^C --- 30.0.1.2 ping statistics --- 13 packets transmitted, 0 received, 100% packet loss, time 12285ms -
設定bridge的IP地址,并啟動bridge
root@chengf:~# ifconfig br001 30.0.0.1/16 up- 此時再去相互ping,發現還是不能訪問,用tcpdump -n -i br001抓包也沒有反應,因為我們只是把peer-veth001、peer-veth002、peer-veth003這幾個設備attach到br001,但是還沒有啟動,所以veth設備對還不能正常作業
root@chengf:~# ip netns exec netns1 ping -c 1 30.0.1.2
PING 30.0.1.2 (30.0.1.2) 56(84) bytes of data.
--- 30.0.1.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
```
-
啟動peer設備
root@chengf:~# ip link set dev peer-veth001 up root@chengf:~# ip link set dev peer-veth002 up root@chengf:~# ip link set dev peer-veth003 up -
相互訪問
root@chengf:~# ip netns exec netns1 ping -c 1 30.0.1.2 PING 30.0.1.2 (30.0.1.2) 56(84) bytes of data. 64 bytes from 30.0.1.2: icmp_seq=2 ttl=64 time=0.249 ms --- 30.0.1.2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 1027ms rtt min/avg/max/mdev = 0.249/0.325/0.401/0.076 ms root@chengf:~# ip netns exec netns1 ping -c 1 30.0.1.3 PING 30.0.1.3 (30.0.1.3) 56(84) bytes of data. 64 bytes from 30.0.1.3: icmp_seq=1 ttl=64 time=0.472 ms --- 30.0.1.3 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.472/0.472/0.472/0.000 ms root@chengf:~# ip netns exec netns2 ping -c 1 30.0.1.1 PING 30.0.1.1 (30.0.1.1) 56(84) bytes of data. 64 bytes from 30.0.1.1: icmp_seq=1 ttl=64 time=0.219 ms --- 30.0.1.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.219/0.219/0.219/0.000 ms root@chengf:~# ip netns exec netns2 ping -c 1 30.0.1.3 PING 30.0.1.3 (30.0.1.3) 56(84) bytes of data. 64 bytes from 30.0.1.3: icmp_seq=1 ttl=64 time=0.389 ms --- 30.0.1.3 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.389/0.389/0.389/0.000 ms root@chengf:~# ip netns exec netns3 ping -c 1 30.0.1.1 PING 30.0.1.1 (30.0.1.1) 56(84) bytes of data. 64 bytes from 30.0.1.1: icmp_seq=1 ttl=64 time=0.194 ms --- 30.0.1.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.194/0.194/0.194/0.000 ms root@chengf:~# ip netns exec netns3 ping -c 1 30.0.1.2 PING 30.0.1.2 (30.0.1.2) 56(84) bytes of data. 64 bytes from 30.0.1.2: icmp_seq=1 ttl=64 time=0.216 ms --- 30.0.1.2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.216/0.216/0.216/0.000 ms -
br001上對應的tcpdump資訊
21:49:59.741205 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.2 tell 30.0.1.1, length 28 21:49:59.741313 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.2 is-at f2:1a:b4:54:94:3d, length 28 21:49:59.741345 IP (tos 0x0, ttl 64, id 14004, offset 0, flags [DF], proto ICMP (1), length 84) 30.0.1.1 > 30.0.1.2: ICMP echo request, id 10633, seq 1, length 64
21:49:59.741526 IP (tos 0x0, ttl 64, id 3913, offset 0, flags [none], proto ICMP (1), length 84)
30.0.1.2 > 30.0.1.1: ICMP echo reply, id 10633, seq 1, length 64
21:50:00.768500 IP (tos 0x0, ttl 64, id 14060, offset 0, flags [DF], proto ICMP (1), length 84)
30.0.1.1 > 30.0.1.2: ICMP echo request, id 10633, seq 2, length 64
21:50:00.768646 IP (tos 0x0, ttl 64, id 3968, offset 0, flags [none], proto ICMP (1), length 84)
30.0.1.2 > 30.0.1.1: ICMP echo reply, id 10633, seq 2, length 64
```
* 可以看到br001在回應icmp請求前,會通過ARP請求,找到目的IP對應的Mac,之后才會把對應的icmp請求轉發到具體地址
* 通過持續在netns3中對30.0.1.1進行ping操作,利用tcpcump持續對br001進行抓包,發現差不多30s,就會發同一個IP發一次ARP包
```
root@chengf:~# tcpdump -n -vv -i br001 | grep ARP
tcpdump: listening on br001, link-type EN10MB (Ethernet), capture size 262144 bytes
22:36:54.364519 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.2 tell 30.0.1.3, length 28
22:36:54.364646 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.2 is-at f2:1a:b4:54:94:3d, length 28
22:37:04.604563 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.3 tell 30.0.1.2, length 28
22:37:04.604783 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.3 is-at 0a:84:78:42:8f:77, length 28
22:37:28.156545 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.2 tell 30.0.1.3, length 28
22:37:28.156703 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.2 is-at f2:1a:b4:54:94:3d, length 28
22:37:33.280248 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.3 tell 30.0.1.2, length 28
22:37:33.280383 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.3 is-at 0a:84:78:42:8f:77, length 28
22:37:54.780257 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.3 tell 30.0.1.2, length 28
22:37:54.780418 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.3 is-at 0a:84:78:42:8f:77, length 28
22:38:01.948258 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 30.0.1.2 tell 30.0.1.3, length 28
22:38:01.948414 ARP, Ethernet (len 6), IPv4 (len 4), Reply 30.0.1.2 is-at f2:1a:b4:54:94:3d, length 28
```
iptables 和 Netfilter
-
在Linux網路協議堆疊中,在資料包的處理程序中用來對資料包進行過濾、修改、丟棄的技術叫做netfilter和iptables,其中netfilter作業在系統內核中,是利用各種規則真正對資料進行處理的邏輯;而iptables作業在用戶空間,提供了豐富的命令列引數,負責協助用戶維護、修改netfilter要使用的各種規則表,二者相互配合共同完成Linux對于資料包的各種處理,
在Linux網路協議堆疊中Netfilter可以參與進去執行動作的點稱為掛接點,每個掛接點可以有多個規則,形成鏈,成為規則鏈,其中掛接點共有5個(prerouting、input、output、forward、postrouting),相應的規則鏈也有5個,但是也允許用戶建立自己的規則鏈,并通過在默認的規則鏈中執行跳轉到自定義的規則鏈中對資料進行處理,
五種掛接點在資料處理程序中的位置如下圖所示:

這五種規則鏈,按照作用分成了四類,存盤在Linux的四張表中分別是
- raw 用來決定是否對一條記錄追蹤
- mangle 為資料包設定標記(很少用到)
- nat 主要做網路地址轉換
- filter 對一條記錄進行具體的過濾操作
這四種表里面規則的優先級從高到低依次是 raw --> mangle --> nat --> filter
并且這四種表并非都對5個掛接點進行存盤,其對應關系如下

-
處理規則
規則鏈中的每一條記錄,稱為一條規則,每個規則都由如下幾部分組成- 表型別 :代表干什么事情
- 什么掛接點 :什么時候起作用
- 匹配的引數是什么 :針對什么樣的資料包
- 匹配后的動作 :匹配后具體的操作是什么
-
其中表型別和掛接點已說過,匹配引數主要有以下分類
- 流入、流出的網路介面 (-o -i 引數)
- 來源、目的地址 (-d -s 引數)
- 協議型別 (-p : udp、tcp 引數)
- 來源、目的埠 (--sport、--dport 引數)
-
匹配后的動作
-
ACCEPT(接收)
-
DROP(丟棄)
-
REJECT(丟棄,必要時會回傳呼叫端一個資訊)
-
DNAT(目的地址轉換,在prerouting階段,進入路由決策之前,修改目標地址,在路由決策時參與判斷的目的地址已經是我們改變后的了,此程序中,源地址不變,并在本機建立NAT表項,當資料回傳時,根據NAT表將源地址修改為資料發送過來時的目標地址,并發給遠程主機)
-
SNAT(源地址轉換,在postrouting階段,經過路由決策之后,在通過本地網路協議堆疊發送出去之前,重新改寫源地址,目標地址不變,并在本機建立NAT表項,當資料回傳時,根據NAT表將目的地址資料改寫為資料發送出去時候的源地址,并發送給主機)
-
MSQUERADE(SNAT的一種,但是地址是動態獲取發送資料的網卡地址)
例:
下面的例子演示了如下場景,主機192.168.0.102和192.168.0.108在同一個局域網中,其中108機器上運行了一個docker容器,容器中是一個web應用,埠是80,并通過埠映射到108機器的1180機器上,當102機器訪問這個web應用時的處理程序如下:
-
第一步:192.168.0.102訪問192.168.0.108:1180
-
第二步:192.168.0.108根據路由規則
-A PREROUTING ! -i docker0 -p tcp -m tcp --dport 1180 -j DNAT --to-destination 172.17.0.2:80在prerouting階段,對于不是從docker0進入的、協議型別是tcp、訪問埠是1180的資料包,將目的地址轉換成訪問172.17.0.2:80,即訪問本機1180的請求轉換成訪問172.17.0.2:80
-
第三步:訪問真實的后端web服務
-
第四步:后端web服務回應請求
-
第五步:192.168.0.108根據路由規則
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE在postrouting階段,對于原地址是172.17.0.0/16的地址段,不經過docker0發出的資料包進行SNAT,把src換成發送資料的網卡地址,即192.168.0.108:1180
-
-
-
常用命令
用戶配置iptables規則主要用iptables,并且docker或者kubernets在安裝和發布容器的程序中會自動幫我們創建很多規則,Linux提供了豐富的引數來控制iptables命令,從而追加不同的規則,可以通過iptables -h 來具體查看,
這里列舉幾條查看已創建的規則命令iptables-save iptables -n -v -L iptables -n -v -L -t nat
路由
-
基本概念
在上一章節中,我們知道Linux在收到資料包,執行prerouting后會利用路由決策機制來決定資料包具體是發送網本機或是forward,這時就需要使用到路由功能,
路由功能有IP層維護的路由表來實作,當收到網路側接收到資料報文時,IP層首先會檢查報文的IP地址是不是本機地址,如果時,那么報文會進入傳輸層相關協議中,如果不是本機的地址,主機將根據路由規則,將資料轉發,如果沒有匹配的路由規則,資料將會丟棄,
路由表中的資料是以條目形式存在的,一個典型的路由條目通常包含以下幾個主要專案:- 目的IP地址:表示目標的IP地址,可以是一個主機的IP地址(標志為H),也可以是網路地址(標志為N)
- 下一個路由器的IP地址
- 標志 H(主機)、G(網關)等
- 網路介面規范:一些資料報文的網路介面規范,這個規范跟隨報文一起傳播
-
具體的路由程序如下
-
首先,路由表會在條目中查找第一個欄位(IP地址)與資料報文中目的IP地址完全相同的條目,如果找到,則資料包被發送到條目相應設備或中間路由器,
-
如果沒有找到一個完全的匹配IP,那么就接著搜索相匹配的網路ID,掩碼從長到短匹配,如果找到,那么該資料報文會被轉發到指定的路由器,這樣可以實作這個網路上的所有主機都通過這個路由表中的單個(這個)條目來管理,從而減少路由表的條目數,
-
如果上述兩個條件都不匹配,那么該資料報文將轉發到一個“默認路由器”,Genmask(掩碼對應的是0.0.0.0),
-
如果上述步驟失敗,即沒有默認路由器,那么該資料報文最終無法被轉發,任何無法投遞的資料報文都將產生一個ICMP主機不可達或ICMP網路不可達的錯誤,并將此錯誤回傳給生成此資料報文的應用程式
-
-
基本命令
- 查看路由串列
route -n -v查看ip v6
route -n -v -6ip route listip route list table all- 追加到特定主機的route
route add -host 192.168.0.114 dev eth0表示到 192.168.0.114主機的資料都經由eth0介面發送
- 追加到某個網路的路由
route add -nat 192.168.0.0 netmask 255.255.255.0 dev eth0
Docker 網路
-
基本概念
標準的docker支持以下4類網路模型
- host模式
- container模式
- none模式
- bridge模式
其中bridge是默認模式,我們創建容器時,默認不指定時就采用這種模式,所以我們也主要探討bridge下的docker網路,
在bridge模式下,Docker Daemon第一次啟動時會創建一個虛擬的網橋docker0,并從幾個備選地址段里給他選擇一個地址,一般是172開頭,這個地址和主機地址不重疊,之后針對docker創建的每一個容器,都會利用網路命名空間技術,給容器創建一個獨立的命名空間,接著會創建一對虛擬的以太設備(veth設備對),其中一端關聯到網橋docker0上,另一端再使用Linux的網路命名技術,移動到容器內的網路空間,并修改名稱為eth0,然后,從docker0網橋相同的地址段內給eth0介面分配一個IP地址,bridge模式下的網路模型示意圖如下

由上圖可以看出,容器內的IP默認情況下是不對外部宿主機暴露的,在不通過宿主機影射出去的話,外部機器也無法訪問到容器內的應用,
結下來我們通過三種場景下Linux的網路協議堆疊資訊變化來進一步分析docker網路
docker 安裝前后
-
docker 安裝前
root@slave:~# 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: enp8s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 link/ether 48:0f:cf:6a:23:fc brd ff:ff:ff:ff:ff:ff 3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether c4:8e:8f:d0:33:89 brd ff:ff:ff:ff:ff:ff inet 192.168.0.114/24 brd 192.168.0.255 scope global dynamic noprefixroute wlo1 valid_lft 6415sec preferred_lft 6415sec inet6 fe80::c25:3752:ea14:78dc/64 scope link noprefixroute valid_lft forever preferred_lft forever root@slave:~# iptables-save root@slave:~# route -v -n 內核 IP 路由表 目標 網關 子網掩碼 標志 躍點 參考 使用 介面 0.0.0.0 192.168.0.1 0.0.0.0 UG 600 0 0 wlo1 169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 wlo1 192.168.0.0 0.0.0.0 255.255.255.0 U 600 0 0 wlo1- wlo1 是宿主機的物理網卡
- 默認情況下iptables中的規則是空的
-
docker 安裝啟動后
root@slave:~# 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: enp8s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 link/ether 48:0f:cf:6a:23:fc brd ff:ff:ff:ff:ff:ff 3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether c4:8e:8f:d0:33:89 brd ff:ff:ff:ff:ff:ff inet 192.168.0.114/24 brd 192.168.0.255 scope global dynamic noprefixroute wlo1 valid_lft 5916sec preferred_lft 5916sec inet6 fe80::c25:3752:ea14:78dc/64 scope link noprefixroute valid_lft forever preferred_lft forever 4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:8c:7a:a3:9d 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 root@slave:~# iptables-save # Generated by iptables-save v1.6.1 on Sun Dec 22 12:18:01 2019 *filter :INPUT ACCEPT [124:8899] :FORWARD DROP [0:0] :OUTPUT ACCEPT [70:7745] :DOCKER - [0:0] :DOCKER-ISOLATION-STAGE-1 - [0:0] :DOCKER-ISOLATION-STAGE-2 - [0:0] :DOCKER-USER - [0:0] -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION-STAGE-1 -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -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-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 COMMIT # Completed on Sun Dec 22 12:18:01 2019 # Generated by iptables-save v1.6.1 on Sun Dec 22 12:18:01 2019 *nat :PREROUTING ACCEPT [11:508] :INPUT ACCEPT [9:428] :OUTPUT ACCEPT [7:491] :POSTROUTING ACCEPT [7:491] :DOCKER - [0:0] -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE -A DOCKER -i docker0 -j RETURN COMMIT # Completed on Sun Dec 22 12:18:01 2019 root@slave:~# route -v -n 內核 IP 路由表 目標 網關 子網掩碼 標志 躍點 參考 使用 介面 0.0.0.0 192.168.0.1 0.0.0.0 UG 600 0 0 wlo1 169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 wlo1 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.0.0 0.0.0.0 255.255.255.0 U 600 0 0 wlo1-
docker 安裝啟動后默認給我們創建了一個docker0的網橋,并賦予了一個172.17.0.1的IP地址,
-
在iptables的filter表中中給我們追加了DOCKER-USER、DOCKER等幾個規則鏈,并將這些規則鏈放入到默認的FORWARD中,并把FORWARD的默認動作設定成DROP
-
在iptables的nat表中也追加了DOCKER鏈,并加入到了PREROUTING和OUTPUT鏈中
-
在iptables的nat表中的POSTROUTING中追加了如下一條記錄
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE表示如果源地址是172.17.0.0/16網段的資料,經過非docker0介面發出去的資料,要把資料包中的源地址修改成發送資料的網卡地址,即從容器中訪問外部網路時應該做一次SNAT,
-
路由表中追加了一項
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0當訪問目標地址是172.17.0.0/16網段的請求時,使用docker0介面,根據之前講的網橋的作用,這樣,容器之間就可以相互訪問了,
-
啟動一個容器(容器無埠映射)
-
以一個簡單的nginx容器為例
slave@slave:~$ docker run -d -it --name nginxfornet nginx:1.9.1 a3b1d91e31ea72420233a8a3bcd1ce0946cee60ce47f2f4004e74103a96098e5 slave@slave:~$ 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: enp8s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 link/ether 48:0f:cf:6a:23:fc brd ff:ff:ff:ff:ff:ff 3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether c4:8e:8f:d0:33:89 brd ff:ff:ff:ff:ff:ff inet 192.168.0.114/24 brd 192.168.0.255 scope global dynamic noprefixroute wlo1 valid_lft 6637sec preferred_lft 6637sec inet6 fe80::c25:3752:ea14:78dc/64 scope link noprefixroute valid_lft forever preferred_lft forever 4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:8c:7a:a3:9d 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:8cff:fe7a:a39d/64 scope link valid_lft forever preferred_lft forever 6: vethf064714@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 16:66:c2:9d:e5:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::1466:c2ff:fe9d:e503/64 scope link valid_lft forever preferred_lft forever slave@slave:~$ docker exec -it nginxfornet 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 5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever slave@slave:~$ sudo iptables-save # Generated by iptables-save v1.6.1 on Sun Dec 22 21:39:14 2019 *filter :INPUT ACCEPT [40148:56446687] :FORWARD DROP [0:0] :OUTPUT ACCEPT [18377:800696] :DOCKER - [0:0] :DOCKER-ISOLATION-STAGE-1 - [0:0] :DOCKER-ISOLATION-STAGE-2 - [0:0] :DOCKER-USER - [0:0] -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION-STAGE-1 -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -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-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 COMMIT # Completed on Sun Dec 22 21:39:14 2019 # Generated by iptables-save v1.6.1 on Sun Dec 22 21:39:14 2019 *nat :PREROUTING ACCEPT [38:2301] :INPUT ACCEPT [32:1417] :OUTPUT ACCEPT [62:4827] :POSTROUTING ACCEPT [62:4827] :DOCKER - [0:0] -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE -A DOCKER -i docker0 -j RETURN COMMIT # Completed on Sun Dec 22 21:39:14 2019 slave@slave:~$ route -n -v 內核 IP 路由表 目標 網關 子網掩碼 標志 躍點 參考 使用 介面 0.0.0.0 192.168.0.1 0.0.0.0 UG 600 0 0 wlo1 169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 wlo1 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.0.0 0.0.0.0 255.255.255.0 U 600 0 0 wlo1- 通過對比可以看到,啟動前后iptables和路由都沒有發生變化
- 在設備里面追加了vethf064714,這個veth和nginx中的eth0是一對的,并且vethf064714 attach到了docker0這個網橋上
啟動一個容器(有埠映射)
-
還是以nginx為例,把容器內部的80埠,暴露到宿主句的1180上
slave@slave:~$ docker rm -f nginxfornet
nginxfornet
slave@slave:~$ docker run -d -it --name nginxfornet -p 1180:80 nginx:1.9.1
93b58ae974017b63e6e344ec0f1c4f5e45bbf119146f97422ce04d8b60b68010
slave@slave:~$ 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: enp8s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
link/ether 48:0f:cf:6a:23:fc brd ff:ff:ff:ff:ff:ff
3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether c4:8e:8f:d0:33:89 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.114/24 brd 192.168.0.255 scope global dynamic noprefixroute wlo1
valid_lft 6488sec preferred_lft 6488sec
inet6 fe80::c25:3752:ea14:78dc/64 scope link noprefixroute
valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:8c:7a:a3:9d 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:8cff:fe7a:a39d/64 scope link
valid_lft forever preferred_lft forever
8: veth9e0cff3@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 12:9c:31:7b:29:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::109c:31ff:fe7b:290c/64 scope link
valid_lft forever preferred_lft forever
slave@slave:~$ sudo iptables-save
Generated by iptables-save v1.6.1 on Sun Dec 22 21:41:14 2019
*filter
:INPUT ACCEPT [49:3352]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [38:5168]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -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 80 -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
COMMIT
Completed on Sun Dec 22 21:41:14 2019
Generated by iptables-save v1.6.1 on Sun Dec 22 21:41:14 2019
*nat
:PREROUTING ACCEPT [6:216]
:INPUT ACCEPT [6:216]
:OUTPUT ACCEPT [3:219]
:POSTROUTING ACCEPT [3:219]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -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 80 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 1180 -j DNAT --to-destination 172.17.0.2:80
COMMIT
Completed on Sun Dec 22 21:41:14 2019
slave@slave:~$ route -n -v
內核 IP 路由表
目標 網關 子網掩碼 標志 躍點 參考 使用 介面
0.0.0.0 192.168.0.1 0.0.0.0 UG 600 0 0 wlo1
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 wlo1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.0.0 0.0.0.0 255.255.255.0 U 600 0 0 wlo1
```
* 在ip addr和route的輸出中和沒有暴露埠的結果是一致的
* 在iptables中追加了如下內容
```
*filter
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
*nat
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j MASQUERADE
...
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 1180 -j DNAT --to-destination 172.17.0.2:80
```
* 第一條是說,從外部訪問IP是172.17.0.2:80的請求是允許的
* 第二條表示從172.17.0.2訪問172.17.0.2:80的請求,在發出時會做SNAT,把源地址轉化成docker0的地址(為啥還有轉一次呢?)
* 第三條表示,訪問宿主機1180埠的請求,會把目標地址轉換成172.17.0.2:80,這樣根據route里面的規則,此請求就會通過docker0發出,訪問到具體的容器了
通過抓包觀察具體的請求
通過在和宿主機(192.168.0.114)在一個局域網里面的另一臺機器(192.168.0.109)對宿主機的1180進行訪問,并使用tcpdumap工具對wlo1和docker0這兩個設備進行抓包,看下請求程序
-
宿主機物理網卡wlo1
root@slave:~# tcpdump -n -vv -i wlo1 ... 21:46:02.990084 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.0.1 tell 192.168.0.114, length 28 21:46:02.990605 ARP, Ethernet (len 6), IPv4 (len 4), Reply 192.168.0.1 is-at 9c:21:6a:d7:91:28, length 28 ... 21:46:13.521957 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.0.114 (c4:8e:8f:d0:33:89) tell 192.168.0.109, length 28 21:46:13.521983 ARP, Ethernet (len 6), IPv4 (len 4), Reply 192.168.0.114 is-at c4:8e:8f:d0:33:89, length 28 ... 21:46:27.345491 IP (tos 0x0, ttl 128, id 4599, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.109.26999 > 192.168.0.114.1180: Flags [S], cksum 0xb9eb (correct), seq 4049181629, win 8192, options [mss 1460,nop,wscale 8,sackOK,TS val 2543868184 ecr 0], length 0 21:46:27.345597 IP (tos 0x0, ttl 128, id 4600, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.109.27000 > 192.168.0.114.1180: Flags [S], cksum 0x23d2 (correct), seq 3764470477, win 8192, options [mss 1460,nop,wscale 8,sackOK,TS val 2543868185 ecr 0], length 0 21:46:27.345789 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.114.1180 > 192.168.0.109.26999: Flags [S.], cksum 0x6121 (correct), seq 1260248266, ack 4049181630, win 65160, options [mss 1460,sackOK,TS val 4197603350 ecr 2543868184,nop,wscale 7], length 0 21:46:27.345863 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.114.1180 > 192.168.0.109.27000: Flags [S.], cksum 0x2595 (correct), seq 996708850, ack 3764470478, win 65160, options [mss 1460,sackOK,TS val 4197603350 ecr 2543868185,nop,wscale 7], length 0 ...- 192.168.0.114主機會發ARP包查找默認網關的介面位置
- 192.168.0.109通過ARP包查找192.168.0.114主機的介面位置
- 192.168.0.109和192.168.0.114建立通信連接,并發送請求
- 192.168.0.114向192.168.0.109回傳請求內容
-
docker0網橋
root@slave:~# tcpdump -n -vv -i docker0 tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes ... 21:46:27.345560 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.2 tell 172.17.0.1, length 28 21:46:27.345651 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.2 is-at 02:42:ac:11:00:02, length 28 ... 21:46:32.430063 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 172.17.0.1 tell 172.17.0.2, length 28 21:46:32.430161 ARP, Ethernet (len 6), IPv4 (len 4), Reply 172.17.0.1 is-at 02:42:8c:7a:a3:9d, length 28 21:46:27.345676 IP (tos 0x0, ttl 127, id 4599, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.109.26999 > 172.17.0.2.80: Flags [S], cksum 0xd33e (correct), seq 4049181629, win 8192, options [mss 1460,nop,wscale 8,sackOK,TS val 2543868184 ecr 0], length 0 21:46:27.345684 IP (tos 0x0, ttl 127, id 4600, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.109.27000 > 172.17.0.2.80: Flags [S], cksum 0x3d25 (correct), seq 3764470477, win 8192, options [mss 1460,nop,wscale 8,sackOK,TS val 2543868185 ecr 0], length 0 21:46:27.345731 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60) 172.17.0.2.80 > 192.168.0.109.26999: Flags [S.], cksum 0x6d57 (incorrect -> 0x7a74), seq 1260248266, ack 4049181630, win 65160, options [mss 1460,sackOK,TS val 4197603350 ecr 2543868184,nop,wscale 7], length 0 21:46:27.345748 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60) 172.17.0.2.80 > 192.168.0.109.27000: Flags [S.], cksum 0x6d57 (incorrect -> 0x3ee8), seq 996708850, ack 3764470478, win 65160, options [mss 1460,sackOK,TS val 4197603350 ecr 2543868185,nop,wscale 7], length 0
思考點
- 為什么docker創建的網路命名空間使用ip netns list不能查看到
- 為什么物理網卡沒有attach到docker0這個bridge上,容器內部還是能訪問宿主機所在的網路
總結
本文通過Linux基本組件veth設備對、bridge、route等幾個組件開始,介紹了docker底層實作容器之間訪問的實作原理,當然docker容器網路技術堆疊不僅僅只有bridge,并且在生產環境下也不贊成用bridge默認網橋docker0,通過docker自建網橋還具有服務發現的功能,另外docker swarm模式下還具有overlay、和macvlan等解決方案,功能更強大,當然也更復雜,等后續再進一步研究,文中有不對或理解不當的地方還望各位網友指正,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/41204.html
標籤:其他
