性能文章>一次 APISIX 网关 503 的问题排查(DNS篇)>

一次 APISIX 网关 503 的问题排查(DNS篇)原创

8989510

最近我们内网的 k8s 集群做了一次升级,发现经过 APISIX 网关服务都 503 异常了,于是做了一次分析。我们在内网和线上都采用了 APISIX 来做流量网关,对 APISIX 也贡献了 6 个 PR,所以对它的源码还算比较了解。下面排查过程比较曲折,情感上多次起伏,各位看官耐心看完。

现象

经由 APISIX 接管的所有接口请求都 503 了,如下所示。

网络的拓扑结构也非常的简单,就是 APISIX 将流量转发到后端的 java 服务。

APISIX 错误日志如下。

发现是因为域名解析失败,但是非常奇怪的是,在容器内我们通过 curl 请求直接是可以请求成功的

curl "http://school-performance-http.easicare-test-2:8080/school-performance
/student-archive/schools/30375dee54dc47ef8410b6508cd7aa6a
/archive-bags/0054616e455f4ccc91f64e9cf11e5571/students/
335cb51e8c0343918969e939b1461e8f"
 \
     -H 'accesstoken: masaike'

遇事不决,先抓包

通过 APISIX 直接请求的包如下,IpV4 返回结果正常,IpV6 返回结果为 No such name。我粗浅的以为是因为 lua 那一层认为没有拿到 IP,所以没有后续三次握手发送请求的逻辑,请求在 APISIX 这一层直接终止 503 了。

这个问题可以同步用 nslookup 来确定

$ nslookup -type=A school-performance-http.easicare-test-2
Server:         169.254.20.10
Address:        169.254.20.10#53

Name:   school-performance-http.easicare-test-2.svc.kubernetes.local
Address: 10.96.136.142

$ nslookup -type=AAAA school-performance-http.easicare-test-2
Server:         169.254.20.10
Address:        169.254.20.10#53

** server can't find school-performance-http.easicare-test-2: NXDOMAIN

可以看到 A 记录(IPv4)地址有正确的返回,但是 AAAA(IPv6)的查询返回了 NXDOMAIN,NXDOMAIN 是 DNS 响应码(Rcode=3)表示不存在记录,也就是域名解析结果不存在。

验证是否是因为 IPv6 返回 NXDOMAIN 导致的问题

带着这个疑问,我看了一下最新版的 APISIX 的代码,发现在今年(22 年)的 1 月份,已经增加了这部分的逻辑,允许用户通过 apisix.enable_ipv6 关掉 ipv6 解析,具体的 PR 在这里 https://github.com/apache/apisix/pull/6023 ,这个 PR 改动了两个地方,一个 nginx 的 resolver 配置以及 core/dns/client.lua ,增加对 enable_ipv6 参数的处理。

nginx 配置文件部分的修改

lua 代码的修改

于是我们就重新用最新版的 APISIX 重新打包镜像上传,果然问题解决了。

到这里,我以为找到了根本的原因,于是放下了这个问题。

高兴得太早了

后面我想,一个大版本的升级,带来的改动是非常多的,你怎么能确定就是那个带来的呢?于是我来魔改 2.10.1 版本的 APISIX 的代码,将 IPv6 的解析去掉,如下所示。

diff --git a/apisix/core/dns/client.lua b/apisix/core/dns/client.lua
index a6dbfb37..c5c1b8c3 100644
--- a/apisix/core/dns/client.lua
+++ b/apisix/core/dns/client.lua
@@ -137,7 +137,14 @@ function _M.new(opts)
     -- make sure each client has its separate room
     package_loaded["resty.dns.client"] = nil
     local dns_client_mod = require("resty.dns.client")
+    local table_remove = table.remove
 
+    for i, v in ipairs(opts.order) do
+        if v == "AAAA" then
+            table_remove(opts.order, i)
+            break
+        end
+    end

我以为这样改动,就可以解决问题了,结果发现居然服务还是 503,问题压根就没有解决,而且通过抓包确实没有再次发起 AAAA 记录的查询了,说明我的改动生效了,这样就说明并不是因为 AAAA 记录返回 NXDOMAIN 导致的问题。

开始怀疑人生,抓包显示 A 记录的解析已经成功了,为什么 APISIX 会认为域名还是失败的呢。

既然最新版 2.13.0 版本可以,那就来对比代码,看看 DNS 部分的逻辑到底有什么不一样的。

APISIX 的 dns 解析是通过 lua-resty-dns-client 库来实现的,这个库在 APISIX 的友商 kong 项目下:https://github.com/Kong/lua-resty-dns-client, 2.10.1 版本和2.13.0 依赖对比差异如下。

可以看到 2.10.1 版本的 APISIX 用的 lua-resty-dns-client 版本的是 5.2.0,2.13.0 版本的 APISIX 用的版本是 6.0.2,这下就好办了,来一个移花接木,把最新版本的 lua-resty-dns-client 的代码覆盖到旧版的 APISIX 中

cp -rf lua-resty-dns-client-5.2.3/src/resty/dns/* /usr/local/apisix/deps/share/lua/5.1/resty/dns/

重新启动 APISIX,发现问题解决了,跟 IPv4、IPv6 没有任何关系。其实想想也是这样,如果 IPv4 域名解析成功、IPv6 失败的情况下,造成 APISIX 域名解析失败,这个错误也太低级了,不应该发生才对。

到这里问题就已经局限到这个库到底有啥问题了,采用二分的方式,好在这个库的版本不多,从 6.0.25.2.0 版本二分覆盖测试。

很快就发现,5.2.2 版本是 OK 的,再低的版本就会出问题,于是对比 5.2.2 和 5.2.1 到底改了什么。

这里的逻辑就是处理了域名末尾带点号的问题。容器内的 /etc/resolv.conf 的配置如下:

cat /etc/resolv.conf 
nameserver 169.254.20.10
search imdach-dev-dev.svc.kubernetes.local. svc.kubernetes.local. kubernetes.local. gz.cvte.cn

比如我们查询的 app-537b340e61adc0ec7ccd840bdcd8a59989cb6598-3.imdach-dev-dev 域名的时候,就会依次查询 search,如下所示。

可以看到,DNS 的查询和返回,都没有带上最后的点号,比如查询

app-537b340e61adc0ec7ccd840bdcd8a59989cb6598-3
.imdach-dev-dev.svc.kubernetes.local.

请求和响应的域名都是

app-537b340e61adc0ec7ccd840bdcd8a59989cb6598-3
.imdach-dev-dev.svc.kubernetes.local

DNS 查询和响应都没有最后的点号。

但是 lua 中需要进行字符串的匹配,qname 是带有点号的,DNS 返回结果虽然查询到了 IP 但是域名没有点号,这样 lua 中就匹配不上,表现出来就是域名解析失败未找到对应 IP。

到这里原因基本上清楚了,那为什么最近才出问题呢?于是问了一下 k8s 运维的同学,得到了肯定的答复。

为了 100% 验证这个问题,我自己手动改了一下 /etc/resolv.conf,将 search 中的点号去掉,然后 APISIX 回滚到最初出问题的版本,问题同样也解决了,访问正常了。

cat /etc/resolv.conf 
nameserver 169.254.20.10
search imdach-dev-dev.svc.kubernetes.local svc.kubernetes.local kubernetes.local gz.cvte.cn

域名到底要不要以点号结尾

其实标准的 DNS 域名就是需要以点号结尾的,但是大家在用域名的过程中往往省略了最后的点,. 是根域名,访问所有域名本质都是要从根域名开始解析,比如 care.seewo.com. 理论要先问根域名服务器 .com 在哪。

这里专门有一篇文章讲这个问题,感兴趣的同学可以深入研究

http://www.dns-sd.org/trailingdotsindomainnames.html

小结

因为内网 K8S 的升级,导致 /etc/resolv.conf 中的 search 末尾多了一个点号,导致低版本的 APISIX(APISIX 2.12 版本以下)的域名解析会失败,与 IPV6 返回 NXDOMAIN 无关。

后记

分析问题一定得静下心,仔细去探究问题的根源,不要急于求成。

今天看到一句话,觉得挺好的,分享给大家,「经验用来对待特殊场景,方**用来处理通用场景,没有经验可能会慢一些,没有方**可能寸步难行」

如果上面的分析过程能给你带来一些启发,那就很好了。

 

请先登录,查看5条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

大数据中台之Kafka,到底好在哪里?
Hello,大家好,今天给大家分享一个大数据里面很火的技术——Kafka,Kafka 是一个分布式的消息系统,其高性能在圈内很出名。本人阅读过多个大数据生态的开源技术的源码,个人感觉 Kafka 的源
什么?搞不定Kafka重复消费?
今天我们聊一个话题,如何保证 Kafka 消息不重复消费?在使用 Kafka 的时候一般都会设置重试的次数,但是因为网络的一些原因,设置了重试就有可能导致有些消息重复发送了(当然导致消息重复也有可能是其他原因),那么怎么解决消息重复这个问题呢?
Kafka的生产者优秀架构设计
前言 Kafka 是一个高吞吐量的分布式的发布订阅消息系统,在全世界都很流行,在大数据项目里面使用尤其频繁。笔者看过多个大数据开源产品的源码,感觉 Kafka 的源码是其中质量比较上乘的一个,这得益于
一次 Docker 容器内大量僵尸进程排查分析
前段时间线上的一个使用 Google Puppeteer 生成图片的服务炸了,每个 docker 容器内都有几千个孤儿僵死进程没有回收,如下图所示。这篇文章比较长,主要就讲了下面这几个问题。- 什么情
What?一个 Dubbo 服务启动要两个小时!
前言前几天在测试环境碰到一个非常奇怪的与 ```dubbo``` 相关的问题,事后我在网上搜索了一圈并没有发现类似的帖子或文章,于是便有了这篇。希望对还未碰到或正在碰到的朋友有所帮助。 现象现象是这样
Jackson修改字段名和自定义命名策略
国庆期间写了一教程:[《轻松学习Jackson》程序员口袋里的开发手册](https://996geek.com/articles/164),这是其中的一篇。Jackson支持在处理数据的时候,使用不
一次 APISIX 网关 503 的问题排查(DNS篇)
一次 DNS 造成的服务故障排查,抽丝剥茧,逐步定位问题
踩了个DNS解析的坑,但我还是没想通
hello大家好,我是小楼。最近踩了个DNS解析的小坑,虽然问题解决了,但排查过程比较曲折,最后还是有一点没有想通,整个过程分享给大家。背景最近负责的服务要置换机器。置换机器可能很多小伙伴不知道是干啥,因为大家平时接触不到,我简单解释一下什么是机器置换以及为什么需要机器置换。机器置换通俗
10
5
关于作者

机械工业出版社《深入理解 JVM 字节码》作者,掘金小册作者《JVM 字节码从入门到精通》、《深入理解TCP 协议》作者,Vim 死忠粉、Kotlin&Go 爱好者、能抓一手好包、喜欢底层技术和分享。微信公众号:张师傅的博客(shifuzhang01)