Una investigación tortuosa de APISIX gateway 503 (DNS)

Recientemente, se actualizó el clúster k8s de nuestra intranet y se descubrió que el servicio de puerta de enlace APISIX era anormal con 503, por lo que hicimos un análisis. Hemos utilizado APISIX como puerta de enlace de tráfico en la intranet y en línea, y hemos contribuido con 6 PR a APISIX, por lo que tenemos una buena comprensión de su código fuente. El siguiente proceso de investigación es más tortuoso, con muchos altibajos emocionales, y los jueces lo leen con paciencia.

Fenómeno

Todas las solicitudes de interfaz asumidas por APISIX son 503, como se muestra a continuación.

La topología de la red también es muy simple, es decir, APISIX reenvía el tráfico al servicio java de back-end.

El registro de errores de APISIX es el siguiente.

Se encontró que la resolución del nombre de dominio falló, pero es muy extraño que en el contenedor directamente podamos solicitar una solicitud exitosa a través de curl

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

En caso de indecisión, toma el paquete primero

Los paquetes solicitados directamente a través de APISIX son los siguientes, el resultado de retorno de IpV4 es normal y el resultado de retorno de IpV6 es No such name. Aproximadamente pensé que era porque la capa lua pensó que no obtuvo la IP, por lo que no había lógica para enviar la solicitud en el protocolo de enlace de tres vías posterior, y la solicitud finalizó 503 directamente en la capa APISIX.

Este problema se puede determinar sincrónicamente con 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
复制代码

Se puede ver que la dirección del registro A (IPv4) se devuelve correctamente, pero la consulta AAAA (IPv6) devuelve NXDOMAIN, NXDOMAIN es el código de respuesta DNS (Rcode=3) que indica que no hay ningún registro, es decir, el nombre de dominio. el resultado de la resolución no existe.

Verifique si el problema se debe a que IPv6 devuelve NXDOMAIN

Con esta pregunta en mente, miré el código de la última versión de APISIX y descubrí que en enero de este año (22 años), esta parte de la lógica se agregó para permitir a los usuarios apisix.enable_ipv6desactivar el análisis de ipv6. está aquí en github.com/apache/apis… , este PR ha cambiado dos lugares, una configuración de resolución de nginx y core/dns/client.luaun aumento en el procesamiento del parámetro enable_ipv6.

Modificación de la sección del archivo de configuración de nginx

Modificación del código 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 项目下: github.com/Kong/lua-re… 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 在哪。

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

www.dns-sd.org/trailingdot…

小结

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

后记

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

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

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

Supongo que te gusta

Origin juejin.im/post/7085140006729154567
Recomendado
Clasificación