一、docker是什么
- Docker本質是一個進程,
- 宿主機通過namespace隔離機制提供進程需要運行基礎環境,并且通過Cgroup限制進程調用資源。
- Docker的隔離機制包括
- network隔離,此次主要探討網絡隔離
- mount隔離
- hostname隔離
- user隔離
- pid隔離
- 進程通信隔離
二、docker 網絡模式
host
: 主機模式- 不會創建網絡隔離機制,直接宿主機的網絡。
bridge
: 默認橋接, 新建虛擬橋接網卡用于docker
容器之間的通信。- 橋接網卡通過
iptables
對分組進行轉發到主網卡,通常使用主機外網IP與外部網絡通信。 - 橋接網卡會關聯多張
interface
網絡接口。 實現內網通信。 interface
主要是通過veth pair
虛擬設備對,一端用于容器,一端用于綁定網橋。
- 橋接網卡通過
macvlan
: 為容器穿件一張虛擬網卡,該網卡用于獨立的MAC地址和相同的網卡操作邏輯。- 相比
bridge
和hosts
模式。 即滿足了網絡隔離需求,也滿足了和宿主機網絡相同地位。
- 相比
ipvlan
: 需要要求內核版本 4.2+, 暫不復現。 可以通過ip link vlan
相關命令了解用法overlay
: 未復現,主要用于swarm
集群none
: 容器不具備網絡通信能力,更多作為類似于腳本任務。
三、docker 網絡通信
docker
主要通過iptables
規則進行報文轉和過濾。
目標: 嘗試理解docker
創建的iptables
行為。
1. 清空iptables
規則, 重啟docker
并啟動nginx容器
# 清空iptables規則,重啟docker后,docker-proxy會自動更新iptables策略
[root@mking /]# iptables -F && iptables -t nat -F && systemctl restart docker# 啟動docker容器,并暴露網卡
[root@mking /]# docker network create demo# 查看網卡信息,demo創建橋接的網卡名為: br-a397efbb3fb4
[root@mking /]# docker network ls
NETWORK ID NAME DRIVER SCOPE
be4c6d689139 bridge bridge local
a397efbb3fb4 demo bridge local
d843459f25fd host host local
7a3b4a1a7c00 none null local# 啟動docker容器,
[root@mking /]# docker run -itd --rm --name nginx --network demo -p 80:80 nginx
demo創建橋接的網卡名為br-a397efbb3fb4
, 后續對該網卡規則進行理解。
2. 查看nat
轉發表信息
nat主要修改報文目標IP,目標端口和源P地址,源端口
[root@mking /]# iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 4 packets, 600 bytes)pkts bytes target prot opt in out source destination 0 0 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCALChain INPUT (policy ACCEPT 4 packets, 600 bytes)pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)pkts bytes target prot opt in out source destination 0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCALChain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * !docker0 10.0.254.0/24 0.0.0.0/0 0 0 MASQUERADE all -- * !br-a397efbb3fb4 10.2.254.0/24 0.0.0.0/0 0 0 MASQUERADE tcp -- * * 10.2.254.2 10.2.254.2 tcp dpt:80Chain 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 0 0 RETURN all -- br-a397efbb3fb4 * 0.0.0.0/0 0.0.0.0/0 0 0 DNAT tcp -- !br-a397efbb3fb4 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:10.2.254.2:80
網橋 br-a397efbb3fb4
在nat
中處理邏輯。
- PREROUTING: 外部流量到達主機時處理,該鏈路主要動作是修改報文目的IP地址,通過DNAT動作實現請求轉發
- 鏈路展示所有報文會通過
DOCKER CHAIN
鏈, 該鏈也恰好有處理DNAT
動作
- 鏈路展示所有報文會通過
- OUTPUT: 流量出去的時候,也會經過
DOCKER CHAIN
。 - DOCKER: 關于網橋
br-a397efbb3fb4
有兩處處理。- RETURN處: 流量從網卡
br-a397efbb3fb4
進入的, 就不再繼續向下匹配,也就不修改目標IP - DNAT: 流量從其他網卡進入且訪問80端口的,通過修改了目的地址指向nginx容器的IP:
10.2.254.2
。
- RETURN處: 流量從網卡
- POSTROUTING: 內部流量從網卡出去時候處理, 該鏈路主要用于修改源IP地址,用于響應回源。
br-a397efbb3fb4
有2處處理- 第二行
- 命令:
iptables -t nat -A POSTROUTING ! -o br-a397efbb3fb4 -s 10.2.254.0/24 -j MASQUERADE
- 理解: 來自源IP網段
10.2.254.0/24
且流量不經過網卡br-a397efbb3fb4
出去的報文,修改動態源IP地址。MASQUERADE
動態獲取IP地址,通常是網卡的IP地址。- 該規則也用于第一行的
docker0
。 說明這是docker創建網卡時的標準規則。
- 命令:
- 第三行:
- 命令:
iptables -t nat -A POSTROUTING -p tcp --dport 80 -s 10.2.254.2 -d 10.2.254.2 -j MASQUERADE
- 理解: 對源IP和目標IP都是
10.2.254.2
且端口是80的TCP發出的報文動態修改源IP地址。
- 命令:
- 第二行
場景1: DNAT轉換后報文是如何到達nginx的
- 假設外部通過
eth0
訪問80端口,先通過iptable
規則進行報文分析處理- 報文的入口網卡是
eth0
- 報文的入口網卡是
- 在
PREROUTING
進行了目的端口轉發,那么報文就要發送到10.2.254.2
, 見docker
鏈的DNAT
規則- 要求不是入口網卡
br-a397efbb3fb4
的報文進行DNAT轉換 - 網橋
br-a397efbb3fb4
作為一個網關角色,是保持內部通信的基礎,所以其IP不能進行改動
- 要求不是入口網卡
- 通過路由尋址。
route -n
可以看到目標網段在10.2.254.0
是通過br-a397efbb3fb4
出去。- 此時報文的入口是
eth0
, 報文的出口br-a397efbb3fb4
br-a397efbb3fb4
和容器IP10.2.254.2
在同一個網段下,且滿足通信。
- 此時報文的入口是
場景2: 容器是如何通過SNAT實現對外通信的
-
假設容器請求其他主機:
192.168.35.253
,刪除a397efbb3fb4
的SNAT規則后。進行ping測試 -
通過簡單的分析拿到容器在主機的設備對網卡是
vetha276645
- 在容器查看命令
ip link show
可以看容器網卡的ID@外部的網卡ID。eth0@if125
- 外部查看
ip link show
查到125網卡vetha276645
- 在容器查看命令
-
ping
請求通過vetha276645
到達主機后,通過路由會從網卡eth0
發出,查看eth0
抓包信息。可以看到是有報文出去
抓包容器網卡vetha276645
向192.168.35.253
發起請求
[root@mking net]# tcpdump -vvn -i vetha276645 icmp
tcpdump: listening on vetha276645, link-type EN10MB (Ethernet), capture size 262144 bytes
11:15:39.983214 IP (tos 0x0, ttl 64, id 19344, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 782, length 64
11:15:40.983457 IP (tos 0x0, ttl 64, id 19815, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 783, length 64
11:15:41.983658 IP (tos 0x0, ttl 64, id 20419, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 784, length 64
11:15:42.983858 IP (tos 0x0, ttl 64, id 21070, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 785, length 64
抓包192.168.33.253
出口網卡也發出去請求
[root@mking ~]# tcpdump -i enp3s0 -p icmp -vvnn
tcpdump: listening on enp3s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:04:49.856208 IP (tos 0x0, ttl 63, id 10747, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 132, length 64
11:04:50.856421 IP (tos 0x0, ttl 63, id 11569, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 133, length 64
11:04:51.856633 IP (tos 0x0, ttl 63, id 11661, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 134, length 64
11:04:52.856829 IP (tos 0x0, ttl 63, id 12190, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 135, length 64
11:04:53.857026 IP (tos 0x0, ttl 63, id 12795, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 136, length 64
抓包192.168.35.253
的確收到來自10.2.254.3
的請求,也嘗試直接回復10.2.254.3
它的請求,但失敗。
[root@host-253 ~]# tcpdump -i ens192 icmp -vvnn
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
11:05:53.852475 IP (tos 0x0, ttl 62, id 43080, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 196, length 64
11:05:53.852530 IP (tos 0x0, ttl 64, id 13542, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 196, length 64
11:05:54.852667 IP (tos 0x0, ttl 62, id 43886, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 197, length 64
11:05:54.852719 IP (tos 0x0, ttl 64, id 13570, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 197, length 64
11:05:55.312720 IP (tos 0x0, ttl 64, id 39942, offset 0, flags [DF], proto ICMP (1), length 60)192.168.35.253 > 125.64.129.223: ICMP echo request, id 3328, seq 5836, length 40
11:05:55.319196 IP (tos 0x0, ttl 56, id 39942, offset 0, flags [DF], proto ICMP (1), length 60)125.64.129.223 > 192.168.35.253: ICMP echo reply, id 3328, seq 5836, length 40
11:05:55.852795 IP (tos 0x0, ttl 62, id 44326, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 198, length 64
11:05:55.852817 IP (tos 0x0, ttl 64, id 13723, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 198, length 64
11:05:56.853000 IP (tos 0x0, ttl 62, id 45059, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 199, length 64
11:05:56.853028 IP (tos 0x0, ttl 64, id 13921, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 10.2.254.3: ICMP echo reply, id 32, seq 199, length 64
11:05:57.853198 IP (tos 0x0, ttl 62, id 45512, offset 0, flags [DF], proto ICMP (1), length 84)10.2.254.3 > 192.168.35.253: ICMP echo request, id 32, seq 200, length 64
-
通過抓包可以簡單分
- 容器
10.2.254.3
向192.168.35.253
發起了ICMP
請求。 - 主機
192.168.33.253
通過路由指定網卡進行外部通信 - 目標
192.168.35.253
也收到來自IP10.2.254.3
的請求 - 目標
192.168.35.253
向10.2.254.3
進行回應, - 目標
192.168.35.253
找不到10.2.254.3
, 因為他們本身不具備直接通信的條件。 - 容器
10.2.254.3
不能收到192.168.35.253
響應。 導致網絡不能互通
- 容器
-
通過
SNAT
修改源IP地址,讓192.168.35.253
可以和主機通信- 對
10.2.254.3
修改源IP,所以docker取網段范圍-s 10.2.254.0/24
滿足更多容器 - 其中
br-a397efbb3fb4
是一個網橋,是內部通信的基礎。所以不需要修改其源IP。假設修改了,那么會造成主機內網不能互通的情況 - 綜合規則:
iptables -t nat -A POSTROUTING ! -o br-a397efbb3fb4 -s 10.2.254.0/24 -j MASQUERADE
- 對
主機抓包后可以看到icmp
的報文原始IP地址變更主機網卡IP地址
[root@mking ~]# tcpdump -i enp3s0 -p icmp -vvnn
tcpdump: listening on enp3s0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:23:42.355262 IP (tos 0x0, ttl 63, id 62150, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 9, length 64
11:23:42.355422 IP (tos 0x0, ttl 63, id 16529, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 9, length 64
11:23:43.355476 IP (tos 0x0, ttl 63, id 62489, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 10, length 64
11:23:43.355662 IP (tos 0x0, ttl 63, id 16562, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 10, length 64
目標主機抓包信息的請求源地址也是滿足可以通信的主機IP地址。并且能正常響應回復
[root@runner253 ~]# tcpdump -i ens192 icmp -vvnn
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
11:26:11.368853 IP (tos 0x0, ttl 62, id 12798, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 158, length 64
11:26:11.368893 IP (tos 0x0, ttl 64, id 26732, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 158, length 64
11:26:12.369095 IP (tos 0x0, ttl 62, id 13330, offset 0, flags [DF], proto ICMP (1), length 84)192.168.33.233 > 192.168.35.253: ICMP echo request, id 33, seq 159, length 64
11:26:12.369136 IP (tos 0x0, ttl 64, id 27102, offset 0, flags [none], proto ICMP (1), length 84)192.168.35.253 > 192.168.33.233: ICMP echo reply, id 33, seq 159, length 64
目前docker主機
可以收到來自目標主機的回復響應,主機又如何把回復響應轉交給docker容器
根據網上查詢,自己沒有驗證。 當創建SNAT后,iptables也會自動在
nat
創建DNAT規則,用于回源到最原始的IP
簡單總結nat
規則目的
- 外部對內請求,會在PREROUTING鏈DNAT轉發請求到容器端口。
- 外部對內過程沒有修改源IP地址。所以容器是可以拿到客戶端IP地址。
- 內部對外訪問,會在POSTROUTING鏈SNAT轉發請求到外部網絡。
- 內部對外修改了源IP地址。所以目的端收到的IP服務器出網的網卡IP。
3. 查看filter
過濾表信息
filter主要對流量進行攔截, 查看主機規則。 很多都是docker
創建, 還是以br-a397efbb3fb4
為例。
[root@mking /]# iptables -t filter -nvL
Chain INPUT (policy ACCEPT 9502 packets, 752K bytes)pkts bytes target prot opt in out source destination Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)pkts bytes target prot opt in out source destination 34 5744 DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0 34 5744 DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0 16 2984 ACCEPT all -- * br-a397efbb3fb4 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED2 120 DOCKER all -- * br-a397efbb3fb4 0.0.0.0/0 0.0.0.0/0 16 2640 ACCEPT all -- br-a397efbb3fb4 !br-a397efbb3fb4 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- br-a397efbb3fb4 br-a397efbb3fb4 0.0.0.0/0 0.0.0.0/0 Chain DOCKER (2 references)pkts bytes target prot opt in out source destination 2 120 ACCEPT tcp -- !br-a397efbb3fb4 br-a397efbb3fb4 0.0.0.0/0 10.2.254.2 tcp dpt:80Chain DOCKER-ISOLATION-STAGE-1 (1 references)pkts bytes target prot opt in out source destination 0 0 DOCKER-ISOLATION-STAGE-2 all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0 16 2640 DOCKER-ISOLATION-STAGE-2 all -- br-a397efbb3fb4 !br-a397efbb3fb4 0.0.0.0/0 0.0.0.0/0 34 5744 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 Chain DOCKER-ISOLATION-STAGE-2 (2 references)pkts bytes target prot opt in out source destination 0 0 DROP all -- * docker0 0.0.0.0/0 0.0.0.0/0 0 0 DROP all -- * br-a397efbb3fb4 0.0.0.0/0 0.0.0.0/0 16 2640 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 Chain DOCKER-USER (1 references)pkts bytes target prot opt in out source destination 34 5744 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
通過鏈路分析關于網橋 br-a397efbb3fb4
處理攔截。
-
PREROUTING 鏈路: 進入主機后規則匹配成功,修改目標IP地址。
-
INPUT 鏈: 對所有目的IP存在本機的流量進行處理
- 因為容器對網絡環境進行了隔離,所以當前主機 (
ip address
和ifconfig
) 沒有容器IP地址。
- 因為容器對網絡環境進行了隔離,所以當前主機 (
-
FORWARD 鏈:對所有目的IP不在本機的流量進行處理
- 容器IP不在主機網絡的,會經過當前鏈進行攔截處理。
- 網橋
br-a397efbb3fb4
處理動作有3個- 第三行規則
- 命令:
iptables -I FOREWARD -o br-a397efbb3fb4 -m stat RELATED,ESTABLISHED -j ACCEPT
- 理解: 經過
br-a397efbb3fb4
出去的網絡,允許回應報文通過。
- 命令:
- 第四行規則
- 命令:
iptables -I FOREWARD -o br-a397efbb3fb4 -j DOCKER
- 理解: 經過
br-a397efbb3fb4
出去的網絡,由Docker
進一步篩選控制。
- 命令:
- 第五行規則:
iptables -I FOREWARD -i br-a397efbb3fb4 ! -o br-a397efbb3fb4 -j ACCEPT
- 規則翻譯: 允許經過
br-a397efbb3fb4
進的網絡, 從其他網卡出去。 - 本質是:允許容器內網與外網通信。
- 規則翻譯: 允許經過
- 第六行規則:
iptables -I FOREWARD -i br-a397efbb3fb4 -o br-a397efbb3fb4 -j ACCEPT
- 規則翻譯: 允許經過
br-a397efbb3fb4
進的網絡并從該網卡出去
- 規則翻譯: 允許經過
- 第三行規則
-
OUTPUT 鏈: 通過網卡出去的網絡進行攔截。
- 此處不涉及
-
Docker 鏈: 對于
br-a397efbb3fb4
有一項處理- 第一行規則:
- 命令:
iptables -I DOCKER -p tcp --dport 80 -d 10.2.254.2 ! -i br-a397efbb3fb4 -o br-a397efbb3fb4 -j ACCEPT
- 理解: 允許非
br-a397efbb3fb4
網絡到br-a397efbb3fb4
網絡對目標IP10.2.254.2
進行80端口訪問
- 命令:
- 第一行規則:
-
DOCKER-ISOLATION-STAGE-1規則上最終交給DOCKER-ISOLATION-STAGE-2: 有一個丟包機制
- 第二行規則:
- 命令:
iptables -I DOCKER-ISOLATION-STAGE-2 -o br-a397efbb3fb4 -j DROP
- 理解: 拒絕從網橋
br-a397efbb3fb4
出去的流量
- 命令:
- 第二行規則:
場景1: 外部請求容器服務需要經歷哪些關卡
- 從
192.168.35.253
訪問 容器HTTP 服務,請求經過eth0
進入主機。 - 在
PREROUTING
鏈路修改目的IP地址,通過目的端口轉向到10.2.254.2
- 路由選擇: 匹配到
10.2.254.2
需要經過過br-a397efbb3fb4
出去- 報文入口網卡是
eth0
- 分組出口網卡是
br-a397efbb3fb4
- 報文入口網卡是
- 目標IP不在主機網絡,經過
FORWARD
鏈處理,這里主要有2個處理- 嘗試建立鏈接: 允許 TCP 3次握手,所以滿足
RELATED,ESTABLISHED
進行放行。 行三規則 - 已經建立鏈接: 允許通過來自外部對
10.2.254.2
的80端口訪問。 行四規則
- 嘗試建立鏈接: 允許 TCP 3次握手,所以滿足
場景2: 容器對外請求/響應需要經歷哪些關卡
- 容器 回復
192.168.35.253
的響應報文,經過br-a397efbb3fb4
到達主機 - 在
POSTROUTING
鏈路修改了源IP地址。 - 路由選擇: 匹配到
192.168.35.253
需要經過eth0
出去- 流量入口網卡是
br-a397efbb3fb4
。 - 流量出口網卡是
eth0
- 流量入口網卡是
- 目標IP不在主機網絡,經過
FORWARD
鏈處理。- 允許從其他網卡出去。行五規則
場景3: 不同網絡的容器是如何隔離的
10.1.254.2
嘗試建立10.2.254.2
請求10.1.254.2
從docker0
進入主機,- 路由選擇: 匹配到
10.2.254.2
需要經過過br-a397efbb3fb4
出去- 流量入口網卡是
docker0
- 流量出口網卡是
br-a397efbb3fb4
- 流量入口網卡是
- 目標IP不在主機網絡,經過
FORWARD
,DOCKER-ISOLATION-STAGE-1
,DOCKER-ISOLATION-STAGE-2
鏈處理。- 拒絕從
br-a397efbb3fb4
出去的網卡 第二行規則 - 拒絕之后,到達
RETURN
規則,不再進行匹配。 - 驗證取消容器網絡之間的隔離:
iptables -F DOCKER-ISOLATION-STAGE-2
- 拒絕從
四、 模擬docker網絡通信
1. 通過shell
命令實現網絡隔離
, 并實現內部通信和外網通信
1. 準備工作
- 會用到centos網絡設備管理命令
ip help # 支持子命令。 所有的命令: 增(add)刪(delete/rm)改(set/put)查(show)
ip link # 查看網絡連接, 網卡,支持虛擬技術的。硬件虛擬,vmware就是硬件虛擬機。
ip link type bridge 虛擬網卡 / veth: 一對虛擬網卡(常見用于docker命名空間隔離)
ip address # 查看IP地址
ip netns # 網絡命名空間,簡單理解虛擬了一個環境,與外部獨立。
ip route # 查看路由。 看IP怎么走的
-
網橋: 網絡連接的一種形式,主要連接多個局域網(lan), 進行流量接收,存儲和轉發。
-
虛擬以太網設備對(veth-pair): 網絡連接的另一種形式, 會產生一對網卡。常用于
網絡隔離
通信。
2. 設備對通信
- 創建一個網絡隔離的namespace: demo
[root@mking /]# ip netns add demo
# 查看網絡NS信息
[root@mking /]# ip netns ls
demo
- 創建一組網卡設備對: veth pair。
設備對網卡可以通過關鍵字@
快速辨別。 51,51 表示網卡的編號ID , peer1
和peer2
表示網卡別名
[root@mking /]# ip link add name peer0 type veth peer name peer1
[root@mking /]# ip link show
51: peer1@peer0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 6a:a0:1b:3a:58:e5 brd ff:ff:ff:ff:ff:ff
52: peer0@peer1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 6e:90:2d:40:59:1e brd ff:ff:ff:ff:ff:ff
- 把peer0的網卡對設置到另一個命名空間
demo
[root@mking /]# ip link set peer0 netns demo
- 查不到編號52:
peer0@peer1
的網卡,同時編號51網卡顯示:peer1@if52
, 說明編號52
網卡存在,但已被設置到其他的ns
, 當前的ns
查看不了
[root@mking /]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether d4:5d:64:7f:92:66 brd ff:ff:ff:ff:ff:ff
51: peer1@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000link/ether 6a:a0:1b:3a:58:e5 brd ff:ff:ff:ff:ff:ff link-netnsid
- 分別為兩張網卡設置IP,實現通信。
veth pari
創建的設備對,默認鏈路是通的。
# 操作宿主機默認的NS。 操作中需要滿足IP在一個網段內。
[root@mking /]# ip address add 10.100.0.15/16 dev peer1
[root@mking /]# ip link set dev peer1 up# 操作隔離的NS: 設置IP地址并啟動
[root@mking /]# ip netns exec demo ip address add 10.100.15.34/16 dev peer0
[root@mking /]# ip netns exec demo ip link set dev peer0 up
- 相互ping測
[root@mking /]# ip netns exec demo ping 10.100.0.15
PING 10.100.0.15 (10.100.0.15) 56(84) bytes of data.
64 bytes from 10.100.0.15: icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from 10.100.0.15: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 10.100.0.15: icmp_seq=3 ttl=64 time=0.046 ms# 從宿主機進行探測ping通10.100.15.34
[root@mking /]# ping 10.100.15.34
PING 10.100.15.34 (10.100.15.34) 56(84) bytes of data.
64 bytes from 10.100.15.34: icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from 10.100.15.34: icmp_seq=2 ttl=64 time=0.046 ms
64 bytes from 10.100.15.34: icmp_seq=3 ttl=64 time=0.046 ms
宿主機創建隔離網絡空間demo
實現通信, 主要步驟如下
- 創建一個
ns
獨立的網絡空間 - 創建一組虛擬設備對
veth pair
產生兩張網絡接口滿足通信鏈路條件 - 其中一張網卡設置在
ns
- 兩張網卡IP在同一個網段
- 啟動兩張網卡
3.多個隔離網絡之間的通信
我們實現了一個ns與主機的通信,現在模擬docker多個容器之間的通信: 即可實現多個ns內部相互通信。
- IP段規劃
設備 | 命名空間 | IP | 廣播地址 | 備注 |
---|---|---|---|---|
br0 | default | 10.105.0.1 | 10.105.255.255 | 擬gateway |
vir10 | default | 10.105.1.110 | 10.105.255.255 | veth-peer中的一個網卡 |
vir11 | ns1 | 10.105.3.50 | 10.105.255.255 | veth-peer中的一個網卡 |
vir20 | default | 10.105.2.110 | 10.105.255.255 | veth-peer中的一個網卡 |
vir21 | ns2 | 10.105.8.110 | 10.105.255.255 | veth-peer中的一個網卡 |
- IP二進制換算: 128 64 32 16 8 4 2 1
- 10.105.0.1 == 00001010.01101001.00000000.00000001
- 子網換算
- 10.105.0.1/24 == 00001010.01101001.00000000.xxxxxxxx == 10.105.0.0-10.105.0.255
- 10.105.0.1/20 == 00001010.01101001.0000xxxx.xxxxxxxx == 10.105.0.0-10.105.31.255
- 10.105.0.1/16 == 00001010.01101001.xxxxxxxx.xxxxxxxx == 10.105.0.0-10.105.255.255
- 需求
- 創建橋接網卡模擬gateway的功能,實現arp
- ns1網卡滿足通過gateway找到ns2的網卡實現通信
- 要求
- 隔離在ns1的網卡需要訪問到gateway
- 隔離在ns2的網卡也需要訪問到gateway
- 已知
- ns1的vir11可以與vir10進行通信
- vir10可以與gateway直接通信
- ns2的vir21可以與vir20進行通信
- vir11可以與gateway直接通信
- 可得
- vir11可以通過vir10把數據請求到gateway進行通信
- vir21可以通過vir20把數據請求到gateway進行通信
- 創建兩個ns, 模擬兩個docker網絡環境
[root@mking /]# ip netns add ns1
[root@mking /]# ip netns add ns2
-
同上兩組網卡設備對
vir10(主機)/vir11(ns1)
和vir20(主機)/vir21(ns2)
。同一主機內的
vir10
和vir20
是相互獨立的, 需要創建一張網卡
作為其網關
,將流量轉發到網關
, 用于arp
IP尋址此處設置默認網關地址
10.105.0.1/16
, 廣播地址得出10.105.255.255
。
# 安裝相關命令
[root@mking /]# yum install bridge-utils -y# 創建一張橋接網卡
[root@mking /]# brctl addbr gateway1# 為gateway1設置網卡IP并啟動
[root@mking /]# ip address add 10.105.0.1/16 broadcast 10.105.255.255 dev gateway1
[root@mking /]# ip link set dev gateway1 up
- 同上兩組網卡設備對
veth pair
分別用于ns1
和ns2
。 并對兩組ns
設置IP,要求在同一個網段內,不需要設置vir10
和vir20
網卡
# 創建vir1x和vir2x的veth peer網卡
[root@mking /]# ip link add name vir10 type veth peer name vir11
[root@mking /]# ip link add name vir20 type veth peer name vir21# 把vir11和vir21網卡分別放在ns1和ns2中
[root@mking /]# ip link set vir11 netns ns1
[root@mking /]# ip link set vir21 netns ns2# 分別為vri11和vir21設置IP,并啟動環路lo網卡
[root@mking /]# ip netns exec ns1 ip address add 10.105.3.50/16 broadcast 10.105.255.255 dev vir11
[root@mking /]# ip netns exec ns1 ip link set dev vir11 up
[root@mking /]# ip netns exec ns1 ip link set dev lo up[root@mking /]# ip netns exec ns2 ip address add 10.105.8.110/16 broadcast 10.105.255.255 dev vir21
[root@mking /]# ip netns exec ns2 ip link set dev vir21 up
[root@mking /]# ip netns exec ns2 ip link set dev lo up
- 設置同主機內的
vir10
和vir20
, 我們將網卡指向到gateway1
# 處理vir10和vir20, 把vir10和vir20 指向到gateway1
[root@mking /]# brctl addif gateway1 vir10
[root@mking /]# brctl addif gateway1 vir20# 啟動同一主機下的網卡
[root@mking /]# ip link set dev vir10
[root@mking /]# ip link set dev vir20# 查看網橋信息
[root@mking /]# brctl show
bridge name bridge id STP enabled interfaces
gateway1 8000.7e6c5709cc5e no vir10vir20
- 至此
ns1
和ns2
可以相互ping通, 理論上不同的網絡隔離空間
下的設備對網卡指向到gateway1
,都可以訪問
# 從ns1 訪問 ns2
[root@mking /]# ip netns exec ns1 ping 10.105.8.110
PING 10.105.8.110 (10.105.8.110) 56(84) bytes of data.
64 bytes from 10.105.8.110: icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 10.105.8.110: icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from 10.105.8.110: icmp_seq=3 ttl=64 time=0.122 ms# 從ns2 訪問 ns1
[root@mking /]# ip netns exec ns2 ping 10.105.3.50
PING 10.105.3.50 (10.105.3.50) 56(84) bytes of data.
64 bytes from 10.105.3.50: icmp_seq=1 ttl=64 time=0.068 ms
64 bytes from 10.105.3.50: icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from 10.105.3.50: icmp_seq=3 ttl=64 time=0.105 ms
多個網絡空間ns
實現通信, 主要步驟如下
- 創建多個
ns
和多組veth pair
虛擬設備對的網卡 - 設備對的一端網卡分別設置在多個
ns1
- 宿主機創建一張
bridge
網卡 - 設備對的另一端網卡添加到
bridge
網卡。 ns
里面的網卡和bridge
網卡的IP設置在一個網段內
4.多個NS對外通信
模擬了多個docker的網絡隔離之間通信,現在模擬docker對外的通信。
- 創建兩個ns, 模擬兩個docker網絡環境。 此處繼續使用之間建好的
ns1
和ns2
。
# 從ns1 訪問外網,尋址失敗
[root@mking /]# ip netns exec ns2 ping 61.139.2.69
connect: Network is unreachable# 從ns2 訪問外網, 尋址失敗
[root@mking /]# ip netns exec ns1 ping 61.139.2.69
connect: Network is unreachable
- 創建默認路由,讓請求外網的分組請求到
gateway1
# 添加默認路由
[root@mking /]# ip netns exec ns1 route add default gw 10.105.0.1
[root@mking /]# ip netns exec ns2 route add default gw 10.105.0.1# 訪問外網,但無響應。
[root@mking /]# ip netns exec ns1 ping 61.139.2.69
PING 61.139.2.69 (61.139.2.69) 56(84) bytes of data.
2 packets transmitted, 0 received, 100% packet loss, time 1000ms[root@mking /]# ip netns exec ns2 ping 61.139.2.69
PING 61.139.2.69 (61.139.2.69) 56(84) bytes of data.
2 packets transmitted, 0 received, 100% packet loss, time 1001ms# 查看gateway1有分組經過。RX packets 接收流量, TX packets 13 傳輸流量
[root@mking /]# ifconfig gateway1
gateway1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 10.105.0.1 netmask 255.255.0.0 broadcast 10.105.255.255inet6 fe80::dcb0:d8ff:fe6b:856f prefixlen 64 scopeid 0x20<link>ether 22:4e:62:c8:f3:80 txqueuelen 1000 (Ethernet)RX packets 33 bytes 2124 (2.0 KiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 13 bytes 978 (978.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
此時ns1
和 ns2
可以把流量發送到網卡gateway1
。
- 分析
ns1
流量走勢
iptables處理分組時間軸
timelinetitle iptables時間線PREROUTING : natINPUT : filter: natFORWARD : natOUTPUT : filter: natPOSTROUTING: nat
分組從 ns1
的網卡 vir11
經過vir10
到達網卡gateway
, 會經過如下判斷
- PREROUTING
- 事件: 接收分組時候
- 備注: 對分組進行修改, 此處不修改
- FORWARD:
- 事件: 接收分組后
- 執行動作: 放行
- 觸發條件: 目標IP不在主機. 所以進行
forward
轉發
- POSTROUTING:
- 事件: 通過轉發后觸發
- 執行動作: 修改報文
source ip
, 本質是將分組交給source ipaddress
發出去
# 放行FORWARD, 流入gateway1網卡分組通過。 主要是通過來自ns1,ns2請求流量
[root@mking /]# iptables -t filter -I FORWARD -i gateway1 -j ACCEPT# 放行FORWARD, 出口gateway1網卡分組通過。 主要是通過返回ns1,ns2響應流量
[root@mking /]# iptables -t filter -I FORWARD -o gateway1 -j ACCEPT# 地址轉換, 把gateway1網段的分組, 修改Source IP或指定網卡IP。實現外部請求
[root@mking /]# iptables -t nat -A POSTROUTING -s 10.105.0.0/16 -o ens33 -j MASQUERADE
- 自建的
ns1
可以訪問外部網絡。
[root@mking /]# ip netns exec ns2 ping 61.139.2.69
PING 61.139.2.69 (61.139.2.69) 56(84) bytes of data.
64 bytes from 61.139.2.69: icmp_seq=1 ttl=57 time=3.09 ms
64 bytes from 61.139.2.69: icmp_seq=2 ttl=57 time=3.03 ms
64 bytes from 61.139.2.69: icmp_seq=3 ttl=57 time=3.44 ms
--- 61.139.2.69 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
5.外部訪問NS網絡
實現ns
可以訪問外網,模擬外網如何訪問ns
內部服務(訪問docker服務)
-
前置條件: 創建一個
ns
, 并要求ns
網絡可以對外訪問。 此處繼續使用之間建好的ns1
和ns2
。 -
終端1在
ns1
開啟一個端口
[root@mking /]# ip netns exec ns1 nc -l -p 8000
- 終端2檢查
ns1
端口情況
[root@mking /]# ip netns exec ns1 netstat -anpt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 26130/nc
tcp6 0 0 :::8000 :::* LISTEN 26130/nc
- 通過
iptables
的DNAT轉發,對外提供訪問
enp3s0
網卡收到外部請求端口: 8000。 可通過iptables如下鏈路
- PREROUTING
- 事件: 接收分組時候。
- 執行動作: 修改目的IP和端口。
- 備注: 因為當前主機是沒有提供端口服務,而主機與
ns1
可以直接通信。 所以在入口時通過修改dst address
和dst port
轉發請求到目的網絡
- FORWARD:
- 事件: 接收分組后
- 執行動作: 放行
- 觸發條件: 目標IP已改,目的不在主機. 所以進行
forward
轉發
# 放行FORWARD, 流入gateway1網卡分組通過。 主要是通過來自ns1,ns2請求流量
[root@mking /]# iptables -t filter -I FORWARD -i gateway1 -j ACCEPT# 放行FORWARD, 出口gateway1網卡分組通過。 主要是通過返回ns1,ns2響應流量
[root@mking /]# iptables -t filter -I FORWARD -o gateway1 -j ACCEPT# 地址轉換, 把gateway1網段的分組, 修改Source IP或指定網卡IP。實現外部請求
[root@mking /]# iptables -t nat -I PREROUTING -p tcp --dport 8000 -j DNAT --to-destination 10.105.3.50
- 驗證自建的
ns1
對外提供端口訪問。
# 從其他設備訪問8000
[root@others /]# telnet 192.168.33.253 8000# 檢查ns1的網絡連接情況
[root@mking /]# ip netns exec ns1 netstat -anpt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 10.105.3.50:8000 192.168.35.253:48246 ESTABLISHED 26332/nc
可以看到8000端口有來自192.168.35.253
的訪問,基礎目標完成
2. 給docker做適配: 為容器接入自定義ns
集群
目標: 把自建隔離的網絡放入到docker中
- 創建好一組虛擬網卡設備對, 規劃和
gateway1
相同的IP段.
# 創建ns3
[root@mking /]# ip netns add ns3# 創建新的虛擬網卡設備對
[root@mking /]# ip link add name vir30 type veth peer name vir31
- 啟動一個nginx docker,不暴露端口。
# 啟動一個nginx服務,并使用自定義網絡, 但不暴露端口。
[root@mking /]# docker run --name nginx --network demo -itd --rm nginx
指定網絡是docker
啟動會創建新的隔離網絡, 不暴露端口是咱不使用docker-proxy
代理。
ns
的信息主要存放在路徑:/var/run/netns/
, 通過創建/proc/${pid}/ns/net
軟連接便可以管理進程的ns
。
# 查詢docker容器ID
[root@mking /]# docker inspect nginx --format="{{ .State.Pid }}"
6331# 創建PID下的ns軟連接到/var/run/netns/ngx, 便可以ngx去管理docker啟動nginx容器網絡
[root@mking /]# ln -s /proc/6331/ns/net /var/run/netns/ngx# 可以查看目前ngx有一張網卡
[root@mking net]# ip netns exec ngx ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 10.2.254.2 netmask 255.255.255.0 broadcast 10.2.254.255ether 02:42:0a:02:fe:02 txqueuelen 0 (Ethernet)RX packets 8 bytes 656 (656.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
- 將
vir31
網卡設置到ngx
內。把vir30
網卡添加到gateway1
的網橋
# 把vir31網卡設置到ngx內
[root@mking net]# ip link set vir31 netns ngx # 查看目前ngx新加入的eth1
[root@mking net]# ip netns exec ngx ifconfig vir31# 設置IP段
[root@mking /]# ip netns exec ngx ip address add 10.105.1.250/16 broadcast 10.105.255.255 dev vir31# 查看網卡信息
[root@mking net]# ip netns exec ngx ifconfig vir31
vir31: flags=4098<BROADCAST,MULTICAST> mtu 1500inet 10.105.1.250 netmask 255.255.0.0 broadcast 10.105.255.255ether 76:f4:11:39:f9:48 txqueuelen 1000 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0# 啟動容器網卡
[root@mking net]# ip netns exec ngx ip link set vir31 up
# 啟動主機網卡
[root@mking net]# ip link set vir30 up# 把外部的vir30網卡添加到gateway1.
[root@mking net]# brctl addif gateway1 vir30
- 內部
ns
訪問容器網絡
# 在ns1訪問ngx的IP。
[root@mking net]# ip netns exec ns1 curl http://10.105.1.250 #查看nginx的日志
[root@mking /]# docker logs -f nginx
10.105.3.50 - - [12/Jul/2024:07:24:36 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.5.0" "-"
- 通過
iptables
方式暴露對外訪問
# 先放行FORWARD(以上已做),
[root@mking net]# iptables -I FORWARD -i gateway1 -j ACCEPT
[root@mking net]# iptables -I FORWARD -o gateway1 -j ACCEPT#添加 nat 地址映射。更改目標IP
[root@mking net]# iptables -t nat -I PREROUTING -p tcp --dport 8002 -j DNAT --to-destination 10.105.1.250:80#查看nginx的日志
[root@mking /]# docker logs -f nginx
192.168.35.253 - - [12/Jul/2024:07:55:21 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"
小結
- docker利用橋接和虛擬設備對實現網絡隔離以及通信。
- docker網絡通過iptables對分組進行攔截過濾和轉發。