问题发现

暂时修复:

  1. 通知用户及时重启恢复作业
  2. 关注作业积压的报警, 及时发现问题

排查机器和DNS服务:

  1. DNS 服务无明显的问题异常, 凌晨机器负载较高,但进一步的原因无法确认

由于作业已经重启, 无有效手段进行下一步分析, 因此计划开发 Flink Canray 程序,定期扫描全集群的作业,结合 metric 和 jstack 能过及时发现问题

问题排查

  1. 查看 jstack 查看对应的执行栈, 发现最终是在一个 native 方法上
pool-21-thread-1": running
        at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
        at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:929)
        at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1324)
        at java.net.InetAddress.getAllByName0(InetAddress.java:1277)
        at java.net.InetAddress.getAllByName(InetAddress.java:1193)
        at java.net.InetAddress.getAllByName(InetAddress.java:1127)
        at org.apache.http.impl.conn.SystemDefaultDnsResolver.resolve(SystemDefaultDnsResolver.java:45)
        at org.apache.http.impl.conn.DefaultClientConnectionOperator.resolveHostname(DefaultClientConnectionOperator.java:263)
        at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:162)
        at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:326)
        at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:610)
        at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:445)
        at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:118)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
  1. 查看 Native 方法的异常栈 登录物理机, 使用 GDB 查看对应的 JNI 层的方法调用
(gdb) info threads
(gdb)
(gdb)
(gdb) thread 20
[Switching to thread 20 (Thread 0x7f17a1e7f700 (LWP 133851))]
#0  0x00007f1811f7e49d in read () from /lib64/libpthread.so.0
(gdb) backtrace
#0  0x00007f1811f7e49d in read () from /lib64/libpthread.so.0
#1  0x00007f17e1dea027 in send_vc () from /lib64/libresolv.so.2
#2  0x00007f17e1deb3f0 in __libc_res_nsend () from /lib64/libresolv.so.2
#3  0x00007f17e1de8c5e in __libc_res_nquery () from /lib64/libresolv.so.2
#4  0x00007f17e1de9840 in __libc_res_nsearch () from /lib64/libresolv.so.2
#5  0x00007f17e1ffc77d in _nss_dns_gethostbyname3_r () from /lib64/libnss_dns.so.2
#6  0x00007f1811871d82 in gaih_inet () from /lib64/libc.so.6
#7  0x00007f1811874a3d in getaddrinfo () from /lib64/libc.so.6
#8  0x00007f17e60bc9cb in Java_java_net_Inet4AddressImpl_lookupAllHostAddr () from /opt/soft/openjdk-8u265-b01/jre/lib/amd64/libnet.so

分析 从线程栈分析, 卡在 read 方法上, 说明和 DNS 服务的 TCP 链接已经建立, read()方法一直阻塞等待 DNS 服务返回. 通过查看卡 DNS 进程所在的机器, 使用 netstat 查看和 nameserver 的链接为 establish 状态,说明和 DNS 服务建立链接是没有问题的

初步怀疑和 glibc 的这个 bug 有关,导致解析 DNS 的线程一直卡住, 但该 jira 至目前为止,还未修复 https://bugzilla.redhat.com/show_bug.cgi?id=1825248

暂无进一步结论

2022.07.06 发现出现问题的机器都是使用了旧版本的 DNS 地址

目前公司内部存在新旧两个版本的 DNS, 发现出现问题的机器配置的 DNS 都是旧的地址, 因此计划尝试将集群的额机器的 DNS 机器都替换成新的地址

2022.07.13 DNS 卡住可能和机器网络丢包相关

出现 DNS 卡住的时间点,对应的机器的丢包率比较高,同时机器的网络流量也比较高,因此猜测也可能和机器的网卡丢包有关

2022.07.14 完成所有 DNS 的配置的升级

完成所有的机器的新版 DNS 地址配置, 依旧出现了卡 DNS 的问题, 但通过 netstat 命令查看对应 DNS 的链接,实际使用的还是旧版本的 DNS 地址, 由于 Flink 作业是常驻作业, 机器上的 /etc/resolved.conf 修改之后, 作业需要重启才能生效

2022.07.20 出现新版 DNS Server 也卡住情况

经过一段时间观察,发现也出现了使用了新版本 DNS 地址的机器,也出现了卡 DNS 的问题

2022.07.25 尝试配置 DNS cache

有两种方案:

  1. 在 Java JVM 层设置较长的 cache ttl,JVM 默认为 60s
  • Java 层配置全局的 DNS TTL 需要编辑 /opt/soft/jdk1.8.265/jre/lib/security/java.security 文件, 无法在 java 的 jvm 参数中配置, 但还有另外一种方式,通过指定一个 extra security 文件 (-Djava.security.properties=./extra-java.security)
  • Talos 的自寻址失败之后, 会通过域名进行消费, 如果配置较长的 TTL 可能无法拿到正确的机器列表 (走域名会有小概率链接不上, 因为是从 DNS 的 cache 中任意选一台机器来进行连接?)
  1. 在 Yarn 的机器上安装 local DNS resolver

避免产生较大影响, 采用方案一, 在 jvm 层配置 DNS 的 cache TTL

2022.08.10 目前发现出现 DNS 问题均为 7.27 之前的作业

7.27 开始灰度 DNS cache 的配置, 期间出现的卡 DNS 的作业,都是 07.27 之前作业, 同一台机器上,也只有 7.27 之前启动的作业, 因此可以初步确认,配置 DNS cache 是有效的。

2022.08.15 出现调整 TTL 的作业依旧出现卡 DNS 问题

原因分析: TTL虽然能减缓出现概率,但无法完全避免这种问题出现,如果流量突增期间 TTL 过期, 则依旧会出现卡 DNS 问题, 理论上 TTL 越大, 出现卡 DNS 概率越低. 后续可能的解决思路:

  1. 排查流量突增,导致打满网卡的进程.
  2. 在网卡打满情况下, 尽量减少网络丢包.
  3. 安装单机的 DNS 服务,避免 DNS 层卡住 (理论上会把卡 DNS 问题转移到本机的 DNS 服务), 该问题可能是 linux 内核层的问题,即使更换为单机的 DNS 服务,DNS 服务本身也可能会卡住.

2022.09.23 找 SRE 同学协助排查网卡丢包问题, 修改网卡绑定参数,可以有效减少机器的丢包率

# 设置网卡中断绑定中断的,这个一直就有,只不过之前是绑到一颗物理CPU上了,此次分散绑到了2颗物理CPU上了【针对intel网卡目前线上配置均为此配置】
sh /home/work/opshell/set_irq_affinity -x all eth0
ethtool -G  eth0 rx 4000
ethtool -G  eth0 tx 4000

2022.10.28 开始全量升级机器的网卡配置

dns

问题修复

设置 DNS TTL

  1. 调整一个较长的 TTL 可以缓解这个问题,但不能根治

期间调整 TTL 配置,确实能够缓解部分问题, 出现问题的频度有降低,但不能根本解决

根治机器层面丢包问题

  1. 通过设置网卡绑定参数, 修复机器层的丢包问题, 解决了丢包问题, 则不会触发卡 DNS 的问题

修复 libc 的 DNS 解析bug

  1. 涉及 linux 内核层的修改, 改动太大, 有一定风险
  2. 需要全集群进行内核升级, 代价有点高

原因分析

卡 DNS 的原因分析:

  1. 通过 netstat 查看,对应的连接处于 establish 状态, 说明连接是正常的,但并没有正常关闭
  2. 内部的 DNS 服务地址由 LVS 进行转发, 因此可能是最后的关闭请求出现丢包,但 LVS 并没有进行重发,导致问题.
  3. DNS 的 client 端并没有设置 socket 的 read 超时, 因此 JNI 的栈实际一直在 read 方法上

机器丢包的原因分析:

  1. 机器的网卡绑定设置问题, 只绑定了一张网卡,导致流量高峰时出现丢包

只有消费消息队列的作业频繁出现卡 DNS 原因:

  1. 消息队列未使用 lvs 进行负载均衡,所有的 RS 的地址都配置到域名解析,一个域名配置配置了几百个 ip 地址
  2. 当域名解析的记录数超过一定大小, 会使用 TCP 的协议进行 DNS 解析.

使用 nslook 命令时,会出现如下的信息,则说明实际会使用 TCP 协议进行域名解析:

;; Truncated, retrying in TCP mode.

公司内部的大部分系统都走了 LVS,因此基本都是 UDP 的域名解析, 不会出现卡 read() 方法上的问题.

参考文档: https://juejin.cn/post/7100388171661180941 https://bugzilla.redhat.com/show_bug.cgi?id=1825248