轉:https://blog.liu-kevin.com/2020/05/13/cha-kan-dockerrong-qi-de-tcplian-jie/
查看容器內的tcp連接
當需要查看tcp連接時,通常使用netstat或ss命令查看,但是查看docker容器的tcp連接存在兩個問題
- docker容器中無netstat或ss命令
- node節點上無法查看容器中的連接
查看docker容器中網絡連接
通過容器中的proc文件查看
- 查看proc文件內容
cat /proc/net/tcp
- 內容分析,主要關注的點是local_address、rem_address
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 47626 1 ffff8ad7fae25f00 100 0 0 10 0
1: 00000000:01BB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 47627 1 ffff8ad7fae25740 100 0 0 10 0
2: 020011AC:01BB 8297417C:CE2C 01 00000000:00000000 00:00000000 00000000 101 0 12071491 1 ffff8ad7fa78d740 20 4 23 10 -1
3: 020011AC:01BB 8297417C:CE2B 01 00000000:00000000 00:00000000 00000000 101 0 12071492 1 ffff8ad7fa78e6c0 20 4 30 10 -1
通過namespace查看
- 查找docker容器進程號
docker inspect -f {{.State.Pid}} nginx
- 進入某個進程的network namespace
nsenter -n -t 2593
- 執行netstat
[root@kevin ns]# netstat -t
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 kevin:35086 172.17.0.4:opentable TIME_WAIT
tcp 0 0 kevin:https 124.64.18.179:41661 ESTABLISHED
tcp 0 0 kevin:https 124.65.151.130:61026 ESTABLISHED
tcp 0 0 kevin:https 124.65.151.130:61025 ESTABLISHED
tcp 0 0 kevin:https 124.65.151.130:61024 ESTABLISHED
tcp 0 0 kevin:https 124.65.151.130:59837 ESTABLISHED
tcp 0 0 kevin:https 124.65.151.130:60000 ESTABLISHED
故 netstat 默認情況下只能查看當前namespace下的連接,如果查看其它namespace的連接,需要先進入其它namespace
查看docker nginx進程對應的tcp連接
在節點上執行下述命令
cat /proc/2593/net/tcp
docker-proxy
查看節點上http或https對應的端口監聽程序
當在節點上執行netstat -ltp
時,可以發現存在進程docker-proxy-current
監聽着8080、443端口
[root@centos75_100-58 ~]# netstat -ltp|grep http
tcp6 0 0 [::]:http [::]:* LISTEN 42138/docker-proxy-
tcp6 0 0 [::]:https [::]:* LISTEN 42124/docker-proxy-
查看進程詳細信息
[root@centos75_100-58 ~]# ps -ef|grep 42138 |grep -v grep
root 42138 41940 0 2019 ? 00:00:05 /usr/libexec/docker/docker-proxy-current -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.66.3 -container-port 80
[root@centos75_100-58 ~]# ps -ef|grep 42124|grep -v grep
root 42124 41940 0 2019 ? 00:00:05 /usr/libexec/docker/docker-proxy-current -proto tcp -host-ip 0.0.0.0 -host-port 443 -container-ip 172.17.66.3 -container-port 443
docker-proxy 通過-host-ip指定了docker-proxy在主機上監聽的網絡接口,通過-host-port指定了監聽的端口號;通過-container-ip和-container-port 指定了docker-proxy鏈接到容器內部的容器ip和端口號
為什么查詢不到外部請求到 docker-proxy的tcp連接
Docker 1.7 版本起,Docker 提供了一個配置項: -userland-proxy,以讓 Docker 用戶決定是否啟用 docker-proxy,默認為 true,即啟用 docker-proxy。
現在的 Docker 環境默認的是: -userland-proxy=true。iptables 和 docker-proxy 都會起作用。
當-userland-proxy=true時,每設置一對端口映射就會啟動一個 docker-proxy 進程。
但是,當我們通過iptables-save -t nat
查看iptables nat列表時,會發現所有到docker容器映射端口的請求都會被nat到docker0
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
所以請求不會到達docker-proxy進程,故不會出現到該進程的tcp連接,所以在一定情況下,可以將docker-proxy關閉。
關閉docker-proxy的問題:
1、ipv6場景
docker啟動時刻可以通過ipv6參數開啟docker ipv6支持功能。開啟后所有docker容器都在ipv6下工作。但是此時docker在ipv6上的工作並為完善,docker並未在ipv6table上為容器添加相應的DNAT規則。如果此時關閉docker-proxy,那么容器外部無法訪問到容器內部網絡。在不借助任何外部手段的情況下(可以使用一個叫ipv6nat工具實現ip6table nat規則的自動添加,但即便如此ipv6場景下,docker-proxy依然有存在意義詳見后文場景3),所以此場景下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、在內核無法開啟route_localnet的情況下
正常情況下,內核不會對地址為localnet(127.0.0.0/8)的地址做forwarding,因為這部分地址被認為為martian 。但是在內核中可以通過配置
echo “1”>/proc/sys/net/ipv4/conf/$BridgeName/route_localnet
開啟。docker在啟動階段會配置此配置項。但是對於低版本內核無此參數或對於ipv6地址場景(ipv6內核無此配置項,無此功能)內核依然不會對localhost(ipv6下地址為[::1])進行forwarding;所以在此部分場景下,如果需要在主機上,使用localhost:Port 與容器通訊依然需要依賴於docker-proxy
shell腳本:遍歷容器涉及的網絡NAMESPACE,查看網絡連接
for x in `docker ps | awk 'NR > 1 {print $1}'`
do
echo "#$x"
a=`docker inspect -f {{.State.Pid}} $x`
#echo $a
echo "nsenter -n -t $a"
echo "netstat -antp | grep 130.6"
done