引
生产环境的问题,按出现频率,大体可以分为两类:
- 高频问题
- 低频问题
- 周期性低频问题
- 无周期随机发生问题
- 单例发生
- 多例发生
一般,遇到 无周期随机发生问题
且 单例发生
的情况,是最难定位问题根源的。
前不久,我就遇到应用对外发起连接 无周期随机发生
且 单例发生
的情况 。你不可能在大流量的情况下,做 tcpdump 去分析连接情况的。而能透视内核状态的 eBPF/BPF 可能是个更合适的选择。
eBPF 可以在以下事件
发生时,抓取到 连接信息
:
- 应用发起
connect()
,连接进入SYN_SENT
状态时 - 在发送
SYN
一定时间后,无收到响应而重传SYN
时 - connect timeout 后,应用
close()
socket。连接从SYN_SENT
状态变为CLOSE
状态时
这里说的连接信息
,包括:
- 连接 4 元组: source_IP: local_port <-> destination_IP:dest_port
- 事发时的连接状态
而这些 事件
+ 连接信息
可以为进一步研判问题的方向,提供一个客观的事实证据。
应用场景例子
之前,我了一篇:特定条件下 Istio 发生 half-close 连接泄漏与出站连接失败 。其中模拟了一个场景:
应用程序出站outbound连接超时因为应用程序选择了一个与-15001outboundlistener-上的现有套接字冲突的临时端口
App invoke syscall
connect(sockfd, peer_addr)
, kernel allocation aephemeral port
(44410 in this case) , bind the new socket to thatephemeral port
and sentSYN
packet to peer.
SYN
packet reach conntrack and it create atrack entry
inconntrack table
:$ conntrack -L tcp 6 108 SYN_SENT src=172.29.73.7 dst=172.21.206.198 sport=44410 dport=7777 src=127.0.0.1 dst=172.29.73.7 sport=15001 dport=44410
SYN
packet DNAT to127.0.0.1:15001
SYN
packet reach the already existingFIN-WAIT-2 127.0.0.1:15001 172.29.73.7:44410
socket, then sidecar reply aTCP Challenge ACK
(TCP seq-no is from the oldFIN-WAIT-2
) packet to App
大体意思是,如果应用在发起出站连接时,内核自动选择的临时本地端口如果与 “Envoy 在 15001 上的 FIN-WAIT-2
状态的 socket 的对端端口” 相同时,就会有问题。为了提高问题发生机率,我当时用 nc -p $临时本地端口号
的方法指定 临时本地端口号
,而不是让内核自动选择, 模拟端口号冲突的。
但实际应用是让内核自动选择临时本地端口号
的。那么问题来了,如果要证明在问题发生时, 内核自动选择的临时本地端口号
就是我设想的情况?
实时跟踪 TCP 连接失败与重试
我们知道,TCP 连接超时的处理过程,大概会有发下几个事件(函数)点:
- 应用调用
connect()
。连接进入SYN_SENT
状态- 对应内核的
tcp_connect()
- 对应内核的
- 在发送
SYN
一定时间后,因无收到响应而间歇重传SYN
- 对应内核的
tcp_retransmit_skb()
- 对应内核的
- connect timeout 后,应用
close()
socket。连接从SYN_SENT
状态变为CLOSE
状态时- 对应内核的
inet_sk_state_store()
- 对应内核的
Talk is cheap, show you the code :
|
|
运行 bpftrace ,执行上面脚本:
|
|
向一个不会回复的 ip 发送连接请求。且指定 10s 超时时长:
|
|
这时,bpftrace 会出现跟踪信息:
$ bpftrace trace-poll-timeout.bt
Attaching 5 probes...
Tracing IP TCP connect latency and SYN retry with stacks. Ctrl-C to end.
21:47:55 0 192.168.1.14:42636 1.1.1.146:22 SYN_SENT
21:47:57 0 192.168.1.14:42636 1.1.1.146:22 SYN_SENT
21:48:01 0 192.168.1.14:42636 1.1.1.146:22 SYN_SENT
21:48:04 TCP_SYN_SENT to CLOSE: 23023 nc 10009ms(尝试连接用时) 4 192.168.1.14:42636(内核自动分配的本地临时端口号) --> 1.1.1.146:22
kstack:
inet_sk_state_store+1
__tcp_close+678
tcp_close+37
inet_release+69
__sock_release+63
sock_close+21
__fput+156
____fput+14
task_work_run+106
exit_to_user_mode_loop+343
exit_to_user_mode_prepare+160
syscall_exit_to_user_mode+39
do_syscall_64+105
entry_SYSCALL_64_after_hwframe+97
ustack:
0x7f1b1c6f8117
0x600000001
^C
就这样,应用对外发起连接 无周期随机发生
且 单例发生
的连接问题,就可以找到证据了。
谢谢阅读!