Docker-Bridge Network 02 容器與外部通信
本小節介紹bridge network模式下,容器與外部的通信。
1.前言2.容器通路外部2.1 通路外網2.2 原理2.3 一張圖總結2.4 抓包3.外部通路容器3.1 建立nginx容器并從外部通路3.2 原理3.3 一張圖總結3.4 抓包3.5 docker-proxy4.小結
1.前言
上一節介紹了容器之間的通信過程。容器中部署了服務,我們肯定要從外部去通路這個服務,那麼,容器如何與外部進行通信呢?
2.容器通路外部
這一節,我們仍然使用bbox1容器進行實驗。首先要確定虛拟機Host1能通路外網。在“準備Docker環境”那一節,介紹了虛拟機如何直接ping通百度。
要說明一點,外部網絡不一定指網際網路,外部與内部是相對的概念。隻要是容器網絡以外的網絡,都可以稱作外部網絡,比如Host1所在的網絡。
2.1 通路外網
[root@docker1 ~]# docker exec -it bbox1 sh
/ # ping www.baidu.com
PING www.baidu.com (180.101.49.11): 56 data bytes
64 bytes from 180.101.49.11: seq=0 ttl=51 time=21.308 ms
64 bytes from 180.101.49.11: seq=1 ttl=51 time=24.613 ms
2.2 原理
回顧一下目前容器的網絡連接配接情況:
我們試着分析一下,bbox1發出ping包,根據bbox1的路由表,封包被送到了docker0。那docker0和enp0s3是怎麼連接配接的呢?
Linux本身就有路由轉發功能,是以其實linux系統本身可以作為一個路由器。執行指令cat /proc/sys/net/ipv4/ip_forward,能看到ip_forward=1,說明系統開啟了路由轉發。
那麼docker0發出資料包後,應該是根據Host的路由表進行轉發。而Host的預設路由是指向enp0s3的,是以enp0s3收到了資料包。
我個人了解,此時enp0s3上資料包的源IP是bbox1的,目的IP是baidu,應該是能夠到達baidu的;但是在baidu發回應答包的時候,它不知道這個私網IP在哪,通過任何一級路由都找不到,是以enp0s3這裡發出封包前就得進行SNAT。
具體回包過程我不太明白,有明白的同學麻煩具體解釋一下,感謝。
說到SNAT,就必須得提到Linux核心的大殺器——IPtables了。
在虛拟機中執行iptables -t nat -vnL,檢視nat表。
Chain POSTROUTING (policy ACCEPT 21 packets, 1479 bytes)
pkts bytes target prot opt in out source destination
3 202 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
docker0發出封包後會經過linux核心的路由轉發,到達enp0s3。而到達enp0s3之前,會經過iptables的POSTROUTING鍊進行SNAT,就是上面這條規則。大意是指把源IP為172.17.0.0/16、目的IP任意的封包,進行MASQUERADE(僞裝),也就是SNAT,即把源IP和MAC替換,替換成路由選擇之後的enp0s3的IP和MAC。
2.3 一張圖總結
bbox1 ping 百度,封包發往docker0;
docker0發出封包,經過核心的路由轉發和SNAT處理,将封包的源IP及MAC換成了enp0s3的IP和MAC,到達enp0s3;
enp0s3發出封包,通路百度。
2.4 抓包
目前網絡裝置及位址如下:
裝置 IP MAC
容器bbox1 172.17.0.2 02:42:ac:11:00:02
網橋docker0 172.17.0.1 02:42:a8:64:6c:32
虛拟機網卡enp0s3 192.168.0.11 08:00:27:70:b6:ef
在bbox1内部執行ping www.baidu.com
在docker0上抓包:
[root@docker1 ~]# tcpdump -nei docker0 icmp
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:49:11.669684 02:42:ac:11:00:02 > 02:42:a8:64:6c:32, ethertype IPv4 (0x0800), length 98: 172.17.0.2 > 180.101.49.11: ICMP echo request, id 9728, seq 0, length 64
12:49:11.697374 02:42:a8:64:6c:32 > 02:42:ac:11:00:02, ethertype IPv4 (0x0800), length 98: 180.101.49.11 > 172.17.0.2: ICMP echo reply, id 9728, seq 0, length 64
在enp0s3上抓包:
[root@docker1 ~]# tcpdump -nei enp0s3 icmp
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
12:49:11.669700 08:00:27:70:b6:ef > 48:0e:ec:3b:b3:41, ethertype IPv4 (0x0800), length 98: 192.168.0.11 > 180.101.49.11: ICMP echo request, id 9728, seq 0, length 64
12:49:11.697340 48:0e:ec:3b:b3:41 > 08:00:27:70:b6:ef, ethertype IPv4 (0x0800), length 98: 180.101.49.11 > 192.168.0.11: ICMP echo reply, id 9728, seq 0, length 64
從enp0s3的封包可以看出,源IP和MAC已經是enp0s3的了。
3.外部通路容器
3.1 建立nginx容器并從外部通路
建立一個nginx容器,執行docker run -it -d --name=nginx01 -p 8081:80 nginx該指令将容器的80端口映射到主機的8081端口。
從本虛拟機上curl 127.0.0.1:8081,能傳回nginx歡迎資訊。從另一台虛拟機上curl 192.168.0.11:8081,也能傳回nginx歡迎資訊。說明外部網絡能通路nginx容器。
3.2 原理
使用了IPtables的DNAT功能。
執行iptables -t nat -vnL檢視IPtables規則,可以發現目的端口8081被替換為172.17.0.4:80。
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8081 to:172.17.0.4:80
3.3 一張圖總結
在Host2裡面curl 192.168.0.11:8081,封包到達Host1的enp0s3;
Host1的enp0s3發出封包後,經由核心的轉發及DNAT處理,将目的IP替換成nginx01的IP和端口;
docker0收到封包後,根據目的mac找到對應端口,送出封包到nginx01。
3.4 抓包
使用tcpdump,在Host1的網卡及docker0上抓包驗證。
Host1的enp0s3
[root@docker1 ~]# tcpdump -nei enp0s3 tcp port 8081
13:25:14.725366 08:00:27:d4:6f:d1 > 08:00:27:70:b6:ef, ethertype IPv4 (0x0800), length 74: 192.168.0.12.48392 > 192.168.0.11.tproxy: Flags [S], seq 1433025043, win 29200, options [mss 1460,sackOK,TS val 4294829465 ecr 0,nop,wscale 7], length 0
13:25:14.725559 08:00:27:70:b6:ef > 08:00:27:d4:6f:d1, ethertype IPv4 (0x0800), length 74: 192.168.0.11.tproxy > 192.168.0.12.48392: Flags [S.], seq 404119170, ack 1433025044, win 28960, options [mss 1460,sackOK,TS val 59419808 ecr 4294829465,nop,wscale 7], length 0
Host1的docker0
[root@docker1 ~]# tcpdump -nei docker0 tcp
13:26:07.014766 02:42:a8:64:6c:32 > 02:42:ac:11:00:04, ethertype IPv4 (0x0800), length 74: 192.168.0.12.48396 > 172.17.0.4.http: Flags [S], seq 1839700092, win 29200, options [mss 1460,sackOK,TS val 4294881751 ecr 0,nop,wscale 7], length 0
13:26:07.015357 02:42:ac:11:00:04 > 02:42:a8:64:6c:32, ethertype IPv4 (0x0800), length 74: 172.17.0.4.http > 192.168.0.12.48396: Flags [S.], seq 3312305882, ack 1839700093, win 28960, options [mss 1460,sackOK,TS val 59472098 ecr 4294881751,nop,wscale 7], length 0
可以看出,docker0上收到的封包,目的IP及MAC已經是nginx01的了。
3.5 docker-proxy
注意:對于外部通路容器,除了iptables DNAT處理外,還有一種方式docker-proxy。網上很多文章寫得并不全面,隻說了docker-proxy。其實通過上面的分析,隻通過DNAT就可以完成外部通路容器了,那docker-proxy什麼時候起作用呢?
[root@docker1 ~]# ps -ef|grep proxy
root 4073 927 0 13:07 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8081 -container-ip 172.17.0.4 -container-port 80
root 4143 1521 0 13:32 pts/0 00:00:00 grep --color=auto proxy
檢視程序可以發現,我們的host上也開啟了docker-proxy,将host的0.0.0.0:8081轉發到容器172.17.0.4:80。
關于docker-proxy和DNAT何時起作用,有一篇文章分析的很好,分享給大家《docker-proxy存在合理性分析》。
4.小結
容器通路外部,由iptables SNAT實作
外部通路容器,由iptables DNAT實作,另外在一些場景下,通過docker-proxy進行轉發
原文位址
https://www.cnblogs.com/sunqingliang/p/12731601.html