主頁 >  其他 > Docker 網路原理

Docker 網路原理

2020-09-15 03:00:54 其他

引言

學習docker網路,可以帶著下面兩個問題來探討

  1. 容器之間可以相互訪問的原理
  2. 容器暴露埠后,通過宿主機訪問到容器內應用,并且對于訪問端而言不用感知容器存在的原理

Docker 本身的技術依賴Linux的內核虛擬化技術,所以為了能夠更好的理解Docker的網路實作,必須要對牽扯到的主要技術做些了解

用到的主要Linux技術點

  1. 網路命名空間(Network Namespace)
  2. veth設備對
  3. 網橋 bridge(docker0)
  4. iptables netfilter
  5. 路由

網路命名空間

  1. Linux中為了支持網路協議堆疊的多個實體,引入了網路命名空間,可以簡單理解成相互不干擾、互相隔離的平行網路空間,通過創建不同的網路命名空間,就可以在一臺機器上模擬多個不同的網路環境,每個網路命名空間中,都可以有自己獨立的路由表,及獨立的iptables來提供包轉發、NAT等功能,Docker正是利用了網路空間的這個特性,實作容器之間的網路隔離(埠號相互不會沖突),

  2. 網路命名空間的操作(以下命令需要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設備對

  1. veth指虛擬以太網口對(virtual ethernet),只能成對出現,不能單個存在,在Linux術語中把其中一個稱為另一個的peer,在Linux中所有的網路設備都只能存在于一個命名空間,物理設備通常只關聯到root命名空間中,veth可以通過命令來任意創建和銷毀,并可以關聯到指定的某個命名空間中,還可以通過命令操作其在不同的命名空間中移動,
    前面提到網路命名空間代表的是一個獨立的協議堆疊,相互之間是隔離的,彼此無法直接通信,那如果想在不同的命名空間之間通信,就要用到veth設備對了,他的作用就像是一個管道,一端連著自己的命名空間協議堆疊,另一端連著另一個命名空間的peer,當veth設備在一端發送資料時,會直接將資料發送給自己的peer,并觸發peer接收資料,然后peer把收到的資料再提交到自己的網路協議堆疊進行處理,從而實作不同協議堆疊之間的資料傳輸,Veth的示意圖如下:

  2. 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對的確能打破不同命名空間的壁壘,實作通信

  1. 創建命名空間

    root@chengf:~# ip netns add netns1
    root@chengf:~# ip netns add netns2
    root@chengf:~# ip netns list
    netns2
    netns1
    
  2. 創建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
    
    
  3. 將veth設備對分別移到不同的命名空間下,執行移動后,可以看到在root空間下已經沒有了剛才創建的veth設備了

    root@chengf:~# ip link set veth1 netns netns1
    root@chengf:~# ip link set veth2 netns netns2
    
  4. 在對應的命名空間中查看,可以看到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: 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
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
```

  1. 給對應的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
    
  2. 通過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
    
  3. 在兩個命名空間中相互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的基本介紹,

網橋

  1. Linux網橋的作用類似于物理網路設備中的交換機,是一個作業在二層的虛擬網路設備,有多個埠,資料從多個介面進,也可以從多個介面出,主要作用是把若干個網路介面打通,以使網路介面之間的報文能夠相互轉發,網橋能夠通過MAC地址自動學習,獲取到MAC地址和網路介面的關聯關系,當受到報文時,首先會決議收到的報文,讀取目標MAC地址,通過和自己的MAC記錄表對比,來決定轉發的目標網路介面,如果能找到MAC地址匹配的介面,就向特定的網路介面轉發,否則就將報文廣播到所有介面,

  2. 網橋是建立在從設備的基礎上作業的,從設備可以是物理設備,也可以是虛擬設備,當一個設備attach到一個網橋時,就類似于物理交換機上插入了一根網線從而和一個終端設備相連,從而實作連接在bridge上的各個彼此隔離的網路空間相互訪問,另外,因為bridge轉發是基于目的MAC地址和介面的映射完成的,bridge只要知道attach到自己的設備連接的對端設備的MAC地址就可以了,所以attach到bridge上的設備對應的IP和MAC就沒有作用了,實際上這些設備都被bridge一視同仁的當作介面對待,這些設備會被設定成接收任何資料包,最終資料包的處理:接收、轉發、丟棄、廣播等操作都由bridge來決定,

  3. 相比較于傳統的交換機,對于接收到的報文要么轉發,要么丟棄,運行于Linux內部網路堆疊里面的網橋,所在的機器本身就是一臺主機,有可能就是網路報文的目的地,其收到的報文出了轉發和丟棄外,有可能被送到網路協議堆疊的上層(網路層),從而被自己主機處理,如果給bridge分配一個IP,其他設備在訪問bridge時,就可以通過bridge連接的宿主機的網路協議堆疊來對資料進行處理,所以從這個層面來講,網橋也可以看作是一個三層設備,其作業示意圖如下(圖片來自網路):

  4. 網橋的常用操作

    • 新增一個網橋
    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通,則證明網橋的確可以實作將資料根據目的發送到不同網路,

  1. 創建命名空間

    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
    
  2. 創建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
    
  3. 將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
    
  4. 創建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
    
    
  5. 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三個中最小的一個
  6. 分別設定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
    
  7. 設定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
```
  1. 啟動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
    
  2. 相互訪問

    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
    
  3. 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

  1. 在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個掛接點進行存盤,其對應關系如下

  2. 處理規則
    規則鏈中的每一條記錄,稱為一條規則,每個規則都由如下幾部分組成

    • 表型別 :代表干什么事情
    • 什么掛接點 :什么時候起作用
    • 匹配的引數是什么 :針對什么樣的資料包
    • 匹配后的動作 :匹配后具體的操作是什么
  3. 其中表型別和掛接點已說過,匹配引數主要有以下分類

    • 流入、流出的網路介面 (-o -i 引數)
    • 來源、目的地址 (-d -s 引數)
    • 協議型別 (-p : udp、tcp 引數)
    • 來源、目的埠 (--sport、--dport 引數)
  4. 匹配后的動作

    • 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

  5. 常用命令

    用戶配置iptables規則主要用iptables,并且docker或者kubernets在安裝和發布容器的程序中會自動幫我們創建很多規則,Linux提供了豐富的引數來控制iptables命令,從而追加不同的規則,可以通過iptables -h 來具體查看,
    這里列舉幾條查看已創建的規則命令

    iptables-save
    iptables -n -v -L
    iptables -n -v -L -t nat
    

路由

  1. 基本概念
    在上一章節中,我們知道Linux在收到資料包,執行prerouting后會利用路由決策機制來決定資料包具體是發送網本機或是forward,這時就需要使用到路由功能,
    路由功能有IP層維護的路由表來實作,當收到網路側接收到資料報文時,IP層首先會檢查報文的IP地址是不是本機地址,如果時,那么報文會進入傳輸層相關協議中,如果不是本機的地址,主機將根據路由規則,將資料轉發,如果沒有匹配的路由規則,資料將會丟棄,
    路由表中的資料是以條目形式存在的,一個典型的路由條目通常包含以下幾個主要專案:

    • 目的IP地址:表示目標的IP地址,可以是一個主機的IP地址(標志為H),也可以是網路地址(標志為N)
    • 下一個路由器的IP地址
    • 標志 H(主機)、G(網關)等
    • 網路介面規范:一些資料報文的網路介面規范,這個規范跟隨報文一起傳播
  2. 具體的路由程序如下

    • 首先,路由表會在條目中查找第一個欄位(IP地址)與資料報文中目的IP地址完全相同的條目,如果找到,則資料包被發送到條目相應設備或中間路由器,

    • 如果沒有找到一個完全的匹配IP,那么就接著搜索相匹配的網路ID,掩碼從長到短匹配,如果找到,那么該資料報文會被轉發到指定的路由器,這樣可以實作這個網路上的所有主機都通過這個路由表中的單個(這個)條目來管理,從而減少路由表的條目數,

    • 如果上述兩個條件都不匹配,那么該資料報文將轉發到一個“默認路由器”,Genmask(掩碼對應的是0.0.0.0),

    • 如果上述步驟失敗,即沒有默認路由器,那么該資料報文最終無法被轉發,任何無法投遞的資料報文都將產生一個ICMP主機不可達或ICMP網路不可達的錯誤,并將此錯誤回傳給生成此資料報文的應用程式

  3. 基本命令

    • 查看路由串列
    route -n -v 
    

    查看ip v6

    route -n -v -6
    
    ip route list 
    
    ip 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 網路

  1. 基本概念

    標準的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 安裝前后

  1. 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中的規則是空的
  2. 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

標籤:其他

上一篇:kubernetes(十二) 部署 harbor 私有倉庫

下一篇:實體演示:如何在Kubernetes上大規模運行CI/CD

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more