Featured image of post 容器环境overlay网络下http通信问题

容器环境overlay网络下http通信问题

docker

问题现象

我发现这么一个问题,我的容器在同一个overlay网络中,但是发现跨节点访问有问题,具体表现为:

  • imcp通信正常,可以直接ping通不通节点的hostname

  • 4层通信正常,我使用nc -vz $host $port,能够正常返回

  • 但是当我使用curl请求的时候就会超时

排查过程

没有思路,只好抓包来看,我监听了vxlan的4789端口(overlay是基于vxlan技术实现的),这是我请求的时候完整的通信过程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
1	0.000000	10.0.1.3	10.0.1.6	TCP	124	37148 → 8080 [SYN] Seq=0 Win=64860 Len=0 MSS=1410 SACK_PERM TSval=1705177287 TSecr=0 WS=128
2	0.000739	10.0.1.6	10.0.1.3	TCP	124	8080 → 37148 [SYN, ACK] Seq=0 Ack=1 Win=64308 Len=0 MSS=1410 SACK_PERM TSval=4021339686 TSecr=1705177287 WS=128
3	0.000804	10.0.1.3	10.0.1.6	TCP	116	37148 → 8080 [ACK] Seq=1 Ack=1 Win=64896 Len=0 TSval=1705177288 TSecr=4021339686
4	0.000869	10.0.1.3	10.0.1.6	HTTP	197	GET / HTTP/1.1
5	0.001165	10.0.1.6	10.0.1.3	TCP	116	8080 → 37148 [ACK] Seq=1 Ack=82 Win=64256 Len=0 TSval=4021339687 TSecr=1705177288
6	0.008789	10.0.1.6	10.0.1.3	HTTP	1318	[TCP Previous segment not captured] Continuation
7	0.008853	10.0.1.3	10.0.1.6	TCP	128	[TCP Dup ACK 3#1] 37148 → 8080 [ACK] Seq=82 Ack=1 Win=64896 Len=0 TSval=1705177296 TSecr=4021339687 SLE=6991 SRE=8193
8	20.031896	10.0.1.6	10.0.1.3	TCP	116	[TCP Previous segment not captured] 8080 → 37148 [FIN, ACK] Seq=11278 Ack=82 Win=64256 Len=0 TSval=4021359718 TSecr=1705177296
9	20.031944	10.0.1.3	10.0.1.6	TCP	136	[TCP Dup ACK 3#2] 37148 → 8080 [ACK] Seq=82 Ack=1 Win=64896 Len=0 TSval=1705197319 TSecr=4021339687 SLE=11278 SRE=11279 SLE=6991 SRE=8193
10	80.504036	10.0.1.3	10.0.1.6	TCP	136	[TCP Keep-Alive] 37148 → 8080 [ACK] Seq=81 Ack=1 Win=64896 Len=0 TSval=1705257791 TSecr=4021339687 SLE=11278 SRE=11279 SLE=6991 SRE=8193
11	80.504836	10.0.1.6	10.0.1.3	TCP	116	[TCP Keep-Alive ACK] 8080 → 37148 [ACK] Seq=11279 Ack=82 Win=64256 Len=0 TSval=4021420191 TSecr=1705197319
12	142.968063	10.0.1.3	10.0.1.6	TCP	136	[TCP Keep-Alive] 37148 → 8080 [ACK] Seq=81 Ack=1 Win=64896 Len=0 TSval=1705320255 TSecr=4021339687 SLE=11278 SRE=11279 SLE=6991 SRE=8193
13	142.968782	10.0.1.6	10.0.1.3	TCP	104	8080 → 37148 [RST] Seq=1 Win=0 Len=0

由上面可以看出:

  1. TCP三次握手连接建立正常
  2. 客户端发送HTTP请求,服务端ACK确认
  3. 然后服务端发送了第一个HTTP响应段,但是客户端没有收到
  4. 客户端重传ACK,触发快速重传机制
  5. 但是服务端并没有收到客户端的重传ACK,导致其自己超时,发送RST结束连接

当然我也测了纯四层的nc探测,一切正常:

1
2
3
4
5
6
7
1	0.000000	10.0.1.3	10.0.1.6	TCP	124	52898 → 8080 [SYN] Seq=0 Win=64860 Len=0 MSS=1410 SACK_PERM TSval=1707997198 TSecr=0 WS=128
2	0.000235	10.0.1.6	10.0.1.3	TCP	124	8080 → 52898 [SYN, ACK] Seq=0 Ack=1 Win=64308 Len=0 MSS=1410 SACK_PERM TSval=4024159597 TSecr=1707997198 WS=128
3	0.000564	10.0.1.3	10.0.1.6	TCP	116	52898 → 8080 [ACK] Seq=1 Ack=1 Win=64896 Len=0 TSval=1707997198 TSecr=4024159597
4	0.000655	10.0.1.3	10.0.1.6	TCP	116	52898 → 8080 [FIN, ACK] Seq=1 Ack=1 Win=64896 Len=0 TSval=1707997198 TSecr=4024159597
5	0.001178	10.0.1.6	10.0.1.3	TCP	116	8080 → 52898 [ACK] Seq=1 Ack=2 Win=64384 Len=0 TSval=4024159598 TSecr=1707997198
6	0.004187	10.0.1.6	10.0.1.3	TCP	116	8080 → 52898 [FIN, ACK] Seq=1 Ack=2 Win=64384 Len=0 TSval=4024159601 TSecr=1707997198
7	0.004496	10.0.1.3	10.0.1.6	TCP	116	52898 → 8080 [ACK] Seq=2 Ack=2 Win=64896 Len=0 TSval=1707997202 TSecr=4024159601

这个问题应该是出在内核了,我首先查了下本地的操作系统内核版本:

1
2
$ uname -r
5.10.0-136.17.0.93.oe2203sp1.x86_64

然后进容器看了容器的发行版版本(不要问为啥不看容器内核版本)

1
2
$ cat /etc/redhat-release
CentOS Linux release 8.3.2011

CentOS 8是基于linux kernel 4的,这里可以猜想是不是内核版本不同,导致两者在网络协议实现上有一定的差异,尤其是在TCP连接状态跟踪和重传机制上会有差异,这会导致连接建立后,数据传输过程中出现异常,触发了这个问题。

然后我直接搞了很多个版本的base镜像,发现linux kernel在5以上(含5)都是正常的。

那问题已经很明确了,这个就是内核版本不同的兼容性问题。

解决方案

升级容器的base,使得发行版使用内核在5,最好是宿主机跟容器使用一样的操作系统。