转: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