1.docker-proxy如何作業
在docker的原始碼中,docker-proxy代碼位于vendor目錄的proxy包內部,感興趣的讀者可以自行閱讀proxy實作代碼,本文繞開代碼實作只講實作原理,本文以tcp鏈接為例,udp效果等同,
在上一章例子中,看到docker-proxy 通過-host-ip指定了docker-proxy在主機上監聽的網路介面,通過-host-port指定了監聽的埠號;通過-container-ip和-container-port 指定了docker-proxy鏈接到容器內部的容器ip和埠號,在上例中docker-proxy監聽0.0.0.0:8080,那么當主機任何網路介面上有netfliter模塊處理后input鏈到達的目標埠為8080的tcp資料包時刻,docker-proxy會接受這個鏈接(accept,記為input鏈接),并主動在連接container-ip+container-port建立一個tcp鏈接(記為output鏈接),當此新建的與容器的鏈接建立后,docker-proxy會將所有來自input鏈接的包 發送給output鏈接,

2.docker 設定的iptables nat規則
其實在目前的docker-proxy實作中,并不是所有的資料包都由docker-proxy完成包轉發,docker配置的iptables nat也參與其中,
可以通過下面命令查看到docker配置的iptables nat規則,
ps -aux | grep -v grep | grep docker-proxy
[root@k8slys01 pgsmaster]# ps -aux | grep -v grep | grep docker-proxy
root 28308 0.0 0.0 225240 3204 ? Sl Sep18 0:08 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 13306 -container-ip 172.17.0.5 -container-port 3306
root 28323 0.0 0.0 290776 3212 ? Sl Sep18 0:07 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 13306 -container-ip 172.17.0.5 -container-port 3306
root 34497 0.0 0.0 364508 6864 ? Sl Dec14 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 5500 -container-ip 172.17.0.2 -container-port 5432
root 34504 0.0 0.0 364508 5288 ? Sl Dec14 0:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 5500 -container-ip 172.17.0.2 -container-port 5432
root 95238 0.0 0.0 438496 3236 ? Sl Dec14 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3100 -container-ip 172.17.0.8 -container-port 3100
root 95247 0.0 0.0 299228 3236 ? Sl Dec14 0:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 3100 -container-ip 172.17.0.8 -container-port 3100
root 103664 0.0 0.1 298972 12940 ? Sl Aug30 0:15 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 5501 -container-ip 172.17.0.3 -container-port 5432
root 103671 0.0 0.1 218452 9104 ? Sl Aug30 0:16 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 5501 -container-ip 172.17.0.3 -container-port 5432
root 114961 0.0 0.0 366172 3280 ? Sl Sep25 0:10 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 443 -container-ip 172.17.0.6 -container-port 443
root 114973 0.0 0.0 290776 3240 ? Sl Sep25 0:10 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 443 -container-ip 172.17.0.6 -container-port 443
root 115364 0.0 0.1 364508 9164 ? Sl Dec09 0:01 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 5502 -container-ip 172.17.0.4 -container-port 5432
root 115372 0.0 0.0 225240 3232 ? Sl Dec09 0:01 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 5502 -container-ip 172.17.0.4 -container-port 5432
root 119408 0.0 0.0 298972 3240 ? Sl Sep25 0:09 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 9411 -container-ip 172.17.0.7 -container-port 9411
root 119417 0.0 0.0 364508 3236 ? Sl Sep25 0:11 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 9411 -container-ip 172.17.0.7 -container-port 9411
root 199800 0.0 0.0 364508 3236 ? Sl Dec13 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3000 -container-ip 172.17.0.10 -container-port 3000
root 199807 0.0 0.0 308576 3232 ? Sl Dec13 0:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 3000 -container-ip 172.17.0.10 -container-port 3000
iptables-save -t nat
[root@k8slys01 pgsmaster]# iptables-save -t nat
# Generated by iptables-save v1.4.21 on Fri Dec 17 16:19:54 2021
*nat
:PREROUTING ACCEPT [1863890:214521050]
:INPUT ACCEPT [393:115683]
:OUTPUT ACCEPT [15872:1204760]
:POSTROUTING ACCEPT [16547:1246012]
: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.3/32 -d 172.17.0.3/32 -p tcp -m tcp --dport 5432 -j MASQUERADE
-A POSTROUTING -s 172.17.0.5/32 -d 172.17.0.5/32 -p tcp -m tcp --dport 3306 -j MASQUERADE
-A POSTROUTING -s 172.17.0.6/32 -d 172.17.0.6/32 -p tcp -m tcp --dport 443 -j MASQUERADE
-A POSTROUTING -s 172.17.0.7/32 -d 172.17.0.7/32 -p tcp -m tcp --dport 9411 -j MASQUERADE
-A POSTROUTING -s 172.17.0.4/32 -d 172.17.0.4/32 -p tcp -m tcp --dport 5432 -j MASQUERADE
-A POSTROUTING -s 172.17.0.10/32 -d 172.17.0.10/32 -p tcp -m tcp --dport 3000 -j MASQUERADE
-A POSTROUTING -s 172.17.0.8/32 -d 172.17.0.8/32 -p tcp -m tcp --dport 3100 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5432 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5501 -j DNAT --to-destination 172.17.0.3:5432
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 13306 -j DNAT --to-destination 172.17.0.5:3306
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.17.0.6:443
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 9411 -j DNAT --to-destination 172.17.0.7:9411
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5502 -j DNAT --to-destination 172.17.0.4:5432
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 3000 -j DNAT --to-destination 172.17.0.10:3000
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 3100 -j DNAT --to-destination 172.17.0.8:3100
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5500 -j DNAT --to-destination 172.17.0.2:5432
COMMIT
docker-proxy是否有必要存在
在網上大量的容器最佳實踐中都建議關閉docker-proxy,“原因是docker會為每個容器每個暴露的埠都啟動一個docker-proxy行程,這個docker-proxy會消耗大概2M的RSS記憶體,當宿主機環境上有幾百上千個容器的時刻,那么可能有幾百上千個docker-proxy,其對物理記憶體消耗的是非常可觀的,而docker-proxy的功能完全可以被docker配置的iptables nat規則替代,所以沒有docker-proxy就沒有必要開啟,” 上述論斷是否是正確的呢?答案是,在大多數場景下此答案正確,只有如下場景docker-proxy才是剛需:
1、ipv6場景
docker啟動時刻可以通過ipv6引數開啟docker ipv6支持功能,開啟后所有docker容器都在ipv6下作業,但是此時docker在ipv6上的作業并為完善,docker并未在ipv6table上為容器添加相應的DNAT規則,如果此時關閉docker-proxy,那么容器外部無法訪問到容器內部網路,在不借助任何外部手段的情況下(可以使用一個叫ipv6nat工具實作ip6table nat規則的自動添加),所以此場景下docker-proxy需要開啟,
2、在老內核下(2.6.x)
容器內部通過hairpin 方式訪問自己暴露的服務 在第一章的例子中,如果需要在容器內部訪問自己暴露的服務,那么就出現了hairpin DNAT訪問方式:
#docker exec -it zxy-nginx /bin/bash
root@8173f601424 #curl http://192.168.126.222:8080
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body bgcolor=“white“ text=“black”>
<center><h1>Welcome to nginx!</h1></center>
</body>
</html>
可以看到在容器zxy-nginx內通過主機ip+容器映射主機埠方式一樣可以訪問到zxy-nginx容器自己暴露的nginx服務, 這就是hairpin DNAT模式,但是關閉docker-proxy時刻,資料包進過docker0上的prerouting鏈時被表3-2的DNAT命中,資料包dst-ip被轉換為172.17.0.4,dst-port被轉換為80,在docker0的forwarding動作中,判定此包需要送回zxy-nginx在docker0上的網路介面veth17f3d1上,默認情況下,內核bridge不允許將包發送回到源介面的;只有在內核配置了hairpin mode enable時刻,才允許此類操作,在docker處理流程中,如果用戶關閉了docker-proxy,那么docker會開啟內核的hairpin mode(在centos 7x上通過echo “1”>/sys/class/net/docker0/brif/vethxxx/hairpin_mode開啟hairpin模式),但是在老內核2.6.x上,沒有辦法啟用hairpin mode,所以此時無法借助iptables nat實作容器內部網路可達,此刻就必須使用docker proxy了,關于hairpin模式的解釋請參考:https://wiki.mikrotik.com/wiki/Hairpin_NAT
3、總結
如果不使用ipv6或者通過ipv6nat工具實作了docker容器網路ipv6 DNAT規則,那么完全可以關閉docker-proxy,
機器上docker-proxy行程rss是1592KB,如果有100個容器,關閉了docker-proxy是否真實節省了100*1592KB 約等于1.5GB物理記憶體?答案是否定的!docker-proxy其實邏輯很簡單,它的rss占用約1.5MB是因為docker-proxy是golang語言撰寫,golang默認采用靜態鏈接方式將所有的庫都靜態鏈接到可執行程式中,所以docker-proxy RSS看起來比較大,但是這里1.5MB中有很大的部分都是docker-proxy可執行程式的代碼段,這部分在linux上以map方式映射到docker-proxy可執行檔案上的,當多個docker-proxy行程存在時刻,這部分maps實際上是通過檔案快取在整個系統共享的,所以在真實系統上多個docker-proxy消耗的真實物理記憶體,其實只有docker-proxy的堆和堆疊,這部分大概只有幾百KB(約200KB),所以關閉docker-proxy的收益并沒有想象的那么大(可以通過cat/proc/$docker-proxy-pid/pmaps 中每個map段的pss,隨著docker-proxy行程數目增加反而下降證明),
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/387127.html
標籤:其他
上一篇:Python下實作Tesseract OCR訓練字符庫(OpenCV-python邊緣檢測代替jTessBoxEditor手動矯正)
