【51CTO活动】8.26 带你与清华大学、搜狗、京东大咖们一同讨论基于算法的IT运维实际
最近在任务中遇到一个 docker 容器下 UDP 协议网络不通的成绩,困扰了很久,也比较有意思,所以想写上去和大家分享。
我们有个运用是 UDP 协议的,部署上去发现无法任务,但是换成 TCP 协议是可以的(运用同时支持 UDP、TCP 协议,切换成 TCP 形式发现一切正常)。虽然换成 TCP 能处置成绩,但是我们还是想知道究竟 UDP 协议在网络形式下为什么会出现这个成绩,以避免前面其他 UDP 运用会有异常。
这个成绩笼统出来是这样的:假设有 UDP 效劳运转在主机上(或许运转在网络模型为 Host 的容器里),并且监听在 0.0.0.0 地址(也就是一切的 ip 地址),从运转在 docker bridge 网络的容器运转客户端拜访效劳,两者通讯有成绩。
留意以上的的限制条件,经过测试,我们发现上去几种状况都是正常的:
运用 TCP 协议没有这个成绩,这个曾经说过了
假设 UDP 效劳器监听在 eth0 IP 地址上也不会出现这个成绩
并不是一切的运用都有这个成绩,我们的 DNS(dnsmasq + kubeDNS) 也是异样的部署方式,但是功用都正常
这个成绩在 docker 上也有 issue 记载:https://github.com/moby/moby/issues/15127,但是目前并没有合理的处置方案。
这篇文章就剖析一下出现这个成绩的缘由,希望给异样遇到这个成绩的读者提供些协助。
成绩重现
这个成绩很容易重现,我的实验是在 ubuntu16.04 下用 netcat 命令完成的,其他系统应该相似。在主机上经过 nc 监听 56789 端口,然后在容器里运用 nc 发数据。第一个报文是能发送出去的,但是以后的报文虽然在网络上能看到,但是对方无法接纳。
在主机上运转 nc UDP 效劳器( -u 表示 UDP 协议, -l 表示监听的端口)
$ nc -ul 56789
然后启动一个容器,运转客户端:
$ docker run -it apline sh
/ # nc -u 172.16.13.13 56789
nc 的通讯是双方的,不管对方输入什么字符,回车后对方就能立刻收到。但是在这个形式下,客户端第一次输入对方可以收到,后续的报文对方都收不到。
在这个实验中,容器运用的是 docker 的默许网络,容器的 ip 是 172.17.0.3,经过 veth pair(图中没有显示)衔接到虚拟网桥 docker0(ip 地址为 172.17.0.1),主机本身的网络为 eth0,其 ip 地址为 172.16.13.13。
172.17.0.3
+----------+
| eth0 |
+----+-----+
|
|
|
|
+----+-----+ +----------+
| docker0 | | eth0 |
+----------+ +----------+
172.17.0.1 172.16.13.13
tcpdump 抓包
遇到这种疑问杂症,第一个想到的抓包,我们需求在 docker0 上抓包,由于这是报文必经过的中央。经过过滤容器的 ip 地址,很容器找到感兴味的报文:
$ tcpdump -i docker0 -nn host 172.17.0.3
为了模拟少数运用一问一答的通讯方式,我们一共发送三个报文,并用 tcpdump 抓取 docker0 接口上的报文:
客户端先向效劳器端发送 hello 字符串
效劳器端回复 world
客户端继续发送 hi 音讯
抓包的结果如下,可以发现第一个报文发送出去没有任何成绩(由于 UDP 是没有 ACK 报文的,所以客户端无法知道对方有没有收到,这里说的没有成绩是值没有对应的 ICMP 报文),但是第二个报文从效劳端发送的报文,对方会前往一个 ICMP 通知端口 38908 不可达;第三个报文从客户端发送的报文也是如此。以后的报文状况相似,双方再也无法停止通讯了。
11:20:43.973286 IP 172.17.0.3.38908 > 172.16.13.13.56789: UDP, length 6
11:20:50.102018 IP 172.17.0.1.56789 > 172.17.0.3.38908: UDP, length 6
11:20:50.102129 IP 172.17.0.3 > 172.17.0.1: ICMP 172.17.0.3 udp port 38908 unreachable, length 42
11:20:54.503198 IP 172.17.0.3.38908 > 172.16.13.13.56789: UDP, length 3
11:20:54.503242 IP 172.16.13.13 > 172.17.0.3: ICMP 172.16.13.13 udp port 56789 unreachable, length 39
而此时主机上 UDP nc 效劳器并没有参加,运用 lsof -i :56789 能够看到它依然在监听着该端口。
成绩缘由
从网络报文的剖析中可以看到效劳端前往的报文源地址不是我们预想的 eth0 地址,而是 docker0 的地址,而客户端直接以为该报文是合法的,前往了 ICMP 的报文给对方。
那么成绩的缘由也可以分为两个部分:
为什么应对报文源地址是 错误的 ?
既然 UDP 是有形状的,内核怎样判别源地址不正确呢?
主机多网络接口 UDP 源地址选择成绩
第一个成绩的关键词是:UDP 和多网络接口。由于假设主机上只要一个网络接口,收回去的报文源地址一定不会有错;而我们也测试过 TCP 协议是可以处置这个成绩的。
经过搜索,发现这确实是个已知的成绩。在 UNP() 这本书中,曾经描画过这个成绩,下面是对应的内容:
(责任编辑:admin)