问题总结
在 Kubernetes 环境下,服务间访问遇到多个 DNS 和网络相关问题:
问题 1:Alpine 镜像 DNS 解析失败
服务使用 node:xxx-alpine 镜像,服务间访问报错:getaddrinfo EAI_AGAIN
问题 2:ClusterIP 访问超时
非 Alpine 镜像,使用 ClusterIP 访问频繁出现超时问题:connect ECONNRESET、read ECONNRESET 以及 axios 的 timeout
问题 3:DNS 访问报错
非 Alpine 镜像,使用 DNS 访问报错:getaddrinfo ENOTFOUND
问题 4:CoreDNS I/O 超时
CoreDNS 报错:[ERROR] plugin/errors: 2 . NS: read udp 10.42.2.5:38764->183.60.82.98:53: i/o timeout
问题排查过程
问题 1:Alpine 镜像 DNS 解析问题
问题现象:
- 问题发生在流量高峰阶段
- 压测 TPS,200 线程仅 30 多每秒,吞吐量极差
- 是偶尔性的,200 线程 50 次仅发现十几条这样的报错日志
原因分析:
备注: 该原因分析,仅针对
node:alpine镜像在 DNS 解析报错EAI_AGAIN的问题。在后文我发现换了 Debian 也有同样的错误,所以这个问题也不排除是 CoreDNS 出了问题
参考链接:https://github.com/nodejs/docker-node/issues/1030#issuecomment-956122581
我们所有的程序相互都是以 HTTP 协议进行接口交互,而 Kubernetes 提供的 Service 为每一组 Pod 提供了 DNS。
当 A 调用 B 时,调用方式如下:
1 | http://B.default.svc.cluster.local:<port> |
问题根源:
- 当 A 尝试与
B.default.svc.cluster.local建立 HTTP 连接时,必须查找 IP - Node 的
lib/dns.js包装了 Node 的引擎函数,通过调用getaddrinfo()进行实际查找 - 后者取决于动态链接的 libc(Alpine 使用的并不是传统的 libc)
- 关键问题:Alpine 的 musl libc 不完全支持 DNS 解析的所有功能,例如当使用多个 DNS 服务器或搜索命令时
解决方案:
参考链接中提到两种可能的解决方案:
- 方案 A:不要使用不完全支持 libc 功能(例如 Alpine)的最小基础镜像,而是切换到 Debian、Ubuntu、Ubi 等 "slim" 变体
- 方案 B:在构建 Node.js 引擎二进制文件时,将 libc 的
getaddrinfo()静态链接到二进制文件中。这不会使用动态链接的 musl libc 代码
问题 2、3、4 排查与解决过程
0. 确认 DNS 策略
首先确认 Pod 的 dnsPolicy,为 ClusterFirst(默认)
1. 查看系统日志
发现有 error 提示,但是通过该 issue 来看,似乎问题不在这里:
1 | Aug 15 21:51:12 k3s-prod-master3 k3s: W0815 21:51:12.632215 2021 watcher.go:220] watch chan error: etcdserver: mvcc: required revision has been compacted |
2. 查看 CoreDNS 日志
通过日志,检查了 10.42.2.5 所在节点,服务器带宽较低:
1 | kubectl logs coredns-7448499f4d-pswcf -n kube-system |
3. 手动测试连接
进入容器测试:
- curl 集群内部接口 - 正常
- curl 集群外部第三方服务接口 - 响应较慢
4. 查看 CoreDNS 监控
我们现在 CoreDNS 的资源及副本数如下,猜测是由于 CoreDNS 副本数不足、业务请求量高等情况导致的 CoreDNS 负载高才发生的报错(但 Prometheus 查并没啥异样...高峰期 0.38 左右的 CPU 和 20 多 MB 内存,并没到限制值):
1 | spec: |

5. 进行 DNS 调试
参考:https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/
部署调试 Pod:
1 | apiVersion: v1 |
测试 DNS 解析:
1 | # 使用 dig 命令,status 为 NOERROR,循环看了 1000 次 |
1 | # 使用 nslookup 命令 |
解决方案
补充 1:临时切换到 ClusterIP
暂时将所有的 DNS 地址换成了 Service 的 ClusterIP,并且发现吞吐量是使用 DNS 时候的 5 倍之多,感觉就是 DNS 解析这里出了问题。
补充 2:资源调整无效
在将内存最小最大都调至 210 MB,再进行压测还是会有 getaddrinfo ENOTFOUND 这种问题,且内存占用资源还不到限制额度的七分之一。而且使用 ClusterIP 访问方式压测第 2 轮之后,发现流量过大会有如下问题:
服务间接性 timeout:
connect ECONNRESET、read ECONNRESET以及服务本身 axios 报的 timeout
以下问题是因为重启了 CoreDNS 导致,忽略
发现了 CoreDNS 的 error 日志
1 | .:53 |
补充 3:网络问题排查
按照官方的建议将版本升至 1.24.3,并加 egress-selector-mode: false 参数,再次进行压测,还是会有超时问题,所以想着是不是节点之间的网络有问题。
这里将所有的 agent 节点 Pod 迁移到 master,并停止调度,再次进行压测:
1 | # 配置不可调度 |
测试结果:
- 使用 ClusterIP 的方式压测,timeout 问题消失,大胆断定之前服务间接性 timeout 问题是因为 node 节点和 master 节点之间 connect 存在互通问题(目前是 node 和 master 之间使用 nginx 进行负载,而 nginx 所在服务器资源占用比较严重)
- 使用 DNS 的方式进行压测,服务出现间接性的
getaddrinfo ENOTFOUND错误
补充 4:NodeLocalCache 解决 DNS 解析问题
问题: 如果使用 DNS 访问,服务将偶现 getaddrinfo ENOTFOUND
肯定还是 DNS 解析问题,然后顺着 这个 StackOverflow 问题 去了解 NodeLocalCache,安装后并进行压测,此问题消失。
关于 NodeLocalCache,站内查看 NodeLocalCache 篇。
补充 5:开启 CoreDNS 日志调试
可以使用 log 插件将请求详情记录下日志,来分析异常的原因。但需要注意的是开启 log 插件调试可能会消耗 10% 左右的 CPU 资源,建议调试完之后关闭。

开启日志功能:
1 | # 通过修改 kubernetes 的 configmap 中的 coredns 配置来开启日志功能,无需重启 coredns |
配置示例:
1 | # 使用 log 插件,默认记录所有类型的日志,指定只记录错误相关的信息 |
查看上游 DNS 服务器:
1 | cat /etc/resolv.conf |
问题总结
问题 1:上游 DNS 服务器性能问题
通过错误日志 [ERROR] plugin/errors: 2 . NS: read udp 10.42.2.5:38764->183.60.82.98:53: i/o timeout,检查 10.42.2.5 所在节点,发现出网带宽较低。SSH 进入当前节点,进行 curl 外部第三方服务接口时发现响应也较慢。
问题 2:DNS 无意义请求
发现些外部域名末尾加了本不该加的 svc.cluster.local,如下:
1 | [INFO] 10.42.3.0:41390 - 37462 "AAAA IN cmq-gz.public.tencenttdmq.com.cluster.local. tcp 61 false 65535" NXDOMAIN qr,aa,rd 154 0.000066155s "0" |
原因:DNS 的无意义请求(其实这个本质上不是问题,但是产生了资源的浪费)
具体原因和解决方案见本站 关于 DNS 解析的一些认识。