💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] 背景:在容器网络,docker容器一般都是一个与宿主机不同网段的IP,假设某个docker容器的IP为172.17.0.2,它所在的宿主机的IP为192.168.2.103,那么这个docker容器如何能够ping通公网比如114呢? 本文,我们将介绍连接跟踪是如何使得docker容器能ping通公网的。 首先,在主机103上,docker会在iptables的POSTROUTING链上生成如何的一条规则: ``` $ iptables -t nat -nL POSTROUTING Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 ``` 这条规则的意思是,容器的包从宿主机出去的时候,会进行MASQUERADE操作(类似SNAT),也就是说,容器的包从宿主机出去后,源IP变成了宿主机的IP,这样外网回复的包就能到达宿主机。 那么问题又来了,外网回复的包到达宿主机时,目的地址为宿主机的IP,宿主机如何知道把这个包转发给容器呢,而且如何把目的地址转换成容器的IP呢? 这个,就和linux的连接跟踪有关了。 我们从容器中ping一个不通的IP ``` $ ping -c 1 114.114.114.115 ``` 发送一个ping包后,我们查看连接跟踪表中的内容: ``` $ cat /proc/net/nf_conntrack ipv4 2 icmp 1 28 src=172.17.0.2 dst=114.114.114.115 type=8 code=0 id=33 [UNREPLIED] src=114.114.114.115 dst=192.168.2.103 type=0 code=0 id=33 mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2 ... ``` 一般这个文件中会有很多内容,很难找到这一行,所以我们一般用conntrack命令(需要安装conntrack-tools)来过滤: ``` $ conntrack -L --proto icmp --orig-src 172.17.0.2 icmp 1 27 src=172.17.0.2 dst=114.114.114.115 type=8 code=0 id=14 [UNREPLIED] src=114.114.114.115 dst=192.168.2.103 type=0 code=0 id=14 mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1 ``` 上面这条记录的意思是:如果宿主机收到了回复包`114.114.114.115 -> 192.168.2.103`,那么要把这个包的转换成`114.114.114.115 -> 172.17.0.2`。 我们来详细地分析一下上面的这一条连接跟踪记录,在这条记录中: * 第一列`icmp`表示协议,第二列`1`表示协议号 * 第三列表示这条连接跟踪记录的生存时间(TTL),这里只剩27秒了,它的默认值是在`/proc/sys/net/ipv4/netfilter/ip_ct_icmp_timeout`中 * 第四行和第五列表示原始包的源IP与目的IP,`[UNREPLIED]`后面的表示回复包的源IP与目的IP * 第六、七、八列`type=8 code=0 id=14`以及`[UNREPLIED]`后面几个对应字段是ICMP协议的一些信息。我们举个例子来说明几个字段的用途: 比如我们在容器内同时运行两个ping,那么如何判断回复包是哪个Ping的?请求包中`type=8`表示这是一个`echo request`(即ping发送的包)类型的包,`id=14`表示ping的id为`14`(每个ping命令都会有一个id);回复包中`type=0`表示包的类型为`echo reply`(即ping的回复包)。后半部分的意思是:只有当收到的包,它的协议为ICMP、类型为`echo reply`(`type=0`)、id为`14`,才会把目的地址转换为容器地址 到这里,我们就大概了解,容器为什么能连通外网了。