K8S – 优化dns解析时间

本篇博客记录在实施K8S过程中遇到的dns解析慢和不稳定问题。

背景

服务上线K8S后,通过调用链trace发现接口95线响应时间恶化10倍以上,于是开始排查。

明确问题方向

从调用链trace系统,很容易看出接口的哪一个网络请求拖慢了响应时间。

但是发现无论是http调用、mysql、redis的响应时间都严重变慢,所以怀疑是基础层面的问题引起,因此有2个方向:

  • 虚拟化网络慢
  • DNS解析慢

为了确定到底是哪个原因,最好是通过工具客观分析,拿数据说话。

登录到container内,创建如下的一个文件:

然后利用curl请求目标域名,就可以得到处理各个阶段的耗时情况:

多执行几次,会出现响应时间糟糕的情况,数据如下:

curl的展示策略是累计时间,因此可以看出dns查询就占掉了0.124686秒,整个请求的总时间才0.146876,并且connect时间几乎为0。

下面是响应时间正常的情况:

因此可以判定就是dns解析慢引起的,而网络因素则可能性很低。

分析具体原因

K8S集群并没有什么压力,请求的域名IP是直接配置在coredns里的,理论上应该几毫秒就返回结果的,那么是什么导致了偶尔的100+毫秒解析时间呢?

所以我在container内开启了tcpdump抓包,监听/etc/resolve.conf中的nameserver地址(其实就是coredns的service ip)的流量,同时通过上述curl命令发起请求,观察耗时长的原因。

透过tcpdump抓包发现,每一次curl请求都发出了2个DNS query,一个是A记录,另外一个是AAAA记录,也就是同时请求了IPV4和IPV6地址。

IPV4很快就返回了结果,而IPV6则经常花费上百毫秒时间,且最终返回NXDomain无IP结果。

这里有2个问题:

  • 为什么curl会同时请求IPV4和IPV6呢?实际上我们只有IPV4地址。
  • 为什么coredns响应IPV6这么慢呢?

第2个问题很容易回答,因为在coredns里我们只配置了对应的IPV4解析,而IPV6请求会被forward到upstream的DNS(在我这里就是公网DNS),所以IPV6的响应时间就不稳定了。

关于第1个问题,经过谷歌搜索后明确了原因,主要是因为curl走的是glibc的gethostbyname调用来解析域名,而这个函数默认是同时发出ipv4和ipv6请求的:

它不像后来linux推出的getaddrinfo函数,可以指定具体IPV4还是IPV4:

这些就不具体说明了。

总之,现在问题明确了,就是因为glibc发起了IPV6的请求,而IPV6地址我们没有配置在coredns中所以请求被upstream到外网解析,从而导致了慢查询。

优化方法

一共有3个工作要做,下面依次列出。

下掉ipv6内核模块

glibc之所以发起ipv6查询,其原因是kernel开启了ipv6模块导致的。

因为docker共享的是宿主机的linux kernel,所以我们需要在宿主机上关闭ipv6内核模块,才能彻底禁用ipv6解析的行为。

做法如下:

在grud中配置ipv6.disable=1可以达到下线ipv6解析的效果,改后需要重启宿主机。

该效果已得到验证,还有一些其他选项应该不是必须的,大家可以酌情参考:https://blog.csdn.net/cjm712/article/details/87886614

你必须付费加入我的知识星球,为有效知识付费是对作者最好的回报。

二维码见下方 或者 右侧边栏。