最近我们边缘集群服务遇到了一个 DNS 访问故障问题,现象是在边缘服务器上无法访问 DNS 服务器(10.7.0.1), 发出去的 DNS 请求包没有收到任何回应。
由于这是第一次遇到这种问题,因此我记录了详细的故障排查过程,让我们一起来看看是如何一步步逼近真相,找到问题根源的。
首先简单介绍一下我们的部署架构,这个 DNS 服务器(10.7.0.1)主要用来为我们的存储节点提供负载均衡服务。我们有 4 台存储服务器, 总容量达 2PB。边缘计算服务器会根据 DNS 服务器返回的 IP 地址挂载对应的存储服务器。
arp-dns
经过初步的排查,我们发现:
起初我们怀疑是 DNS 服务本身出了问题。但检查发现 10.7.0.1 上 53 端口处于正常监听状态:
本机执行 DNS 查询也一切正常。这样就可以排除 DNS 服务异常的可能性了。
接下来我们分析是否 10.7.0.1 的防火墙限制了 53 端口的访问。但经过仔细检查 iptables 规则后, 我们同样没有发现异常。
这就有点奇怪了,防火墙没有拦截,DNS 服务也正常,但请求却没有响应。接下来想到要抓包分析一下。
然而这台 DNS 服务器无法访问外网,没有办法通过简单的 apt-get 包管理器安装 tcpdump,这也不是啥大问题,我们可以在其他机器上编译一个静态链接的 tcpdump, 然后拷贝过来使用。
由于 DNS 服务器是 arm64 架构, 静态编译 tcpdump 花了我一点时间。我们可以选择
幸运的是公司内网有 arm64 的机器可以使用,我直接登录上去进行编译。首先从 tcpdump 的官网下载对应的源码,然后执行以下命令完成静态编译:
cd libpcap-1.10.4
CFLAGS=-static ./configure --with-pcap=linux
make
cd tcpdump-4.99.4
CFLAGS=-static ./configure --without-crypto
make
最终我们得到了一个静态链接的 tcpdump 可执行文件:
$ file tcpdump
tcpdump: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=e1cbb8879d17fd80aa0153da4e4e42ffe4593d96, for GNU/Linux 3.7.0, not stripped
此时运行 tcpdump 就可以执行抓包了。
将这个文件拷贝到 DNS 服务器上, 就可以执行抓包了。
我们在 DNS 服务器(10.7.0.1)上运行 tcpdump 抓包, 然后从边缘业务服务器(10.7.0.46)向其发送 ping 和 DNS 请求。
tcpdump -i any host 10.7.0.46 -nn
但令人意外的是, tcpdump 抓不到任何来自 10.7.0.46 的网络包,这说明请求压根就没有到达 DNS 服务器。
arp-dns1
为了再次确认, 我们回到业务服务器(10.7.0.46)上, 查看它的 ARP 表:
ip n
发现业务机上 10.7.0.1 对应的 mac 地址居然是错误的,而且状态还是 REACHABLE ,这意味着 10.7.0.46 确实是与局域网中的某一台 10.7.0.1 的主机在通信。
正确的 mac 地址是 bc:99:30:89:e0:f0
。
为了验证我们的判断,先在业务机(10.7.0.46)上删除错误的 ARP 表项,然后增加一条静态 permanent 的 arp 表项,指向正确的 MAC 地址:
ip n del 10.7.0.1 dev eth0
ip n add 10.7.0.1 lladdr bc:99:30:89:e0:f0 dev eth0 nud permanent
修改后再次从 10.7.0.46 访问 DNS 服务, 一切恢复正常:
所有与 10.7.0.46 的通信都恢复正常了,tcpdump 的抓包结果如下:
至此问题已经解决,但我们还是想找出 IP 冲突的元凶, 避免类似问题再次发生。
通过之前 ARP 记录的 REACHABLE 状态,可以判断 IP 冲突的主机目前仍在线。
我们尝试扫描了这个 IP 的常用端口, 发现 22 和 80 端口是开放的, 但仍无法判断具体是什么服务。
这时我无意中翻到存储服务器的监控看板上的一个告警, 提示在「2024-05-09 18:32」发现 10.7.0.1 IP 冲突。
这个时候开始社会工程,让老铁们帮忙回忆这个时间点启动了什么服务,或者做了什么变更。
然后真的有铁子回忆到这个时间点启动了一台运行 jumpserver 的虚拟机:
在「嫌疑人」的配合下, 我们确认了这台虚拟机的 IP 的确错误地配置成了 10.7.0.1, 导致了 IP 冲突。
最后, 我们下线了这台虚拟机, 彻底修复了故障,至此破案。
这次故障排查过程还是比较顺利的, 虽然中间编译 tcpdump 工具花了点时间。整个过程是逐步排除各种不可能的选项, 最终锁定问题根源的。期间用到的一些小技巧包括:
希望通过分享这个案例, 能给你一些故障排查思路上的启发。