DNS问题排查思路
参考文章
这篇博客主要是在推特中无意翻到了这篇博客,尝试以翻译的形式做一套笔记,分享的同时加强自己的记忆。
系统背后做的事情
当我们发起一个DNS请求的时候,基本上发生的就是下面两件事
- 电脑向一个被标记为
resolver的服务器发送一个DNS请求。 resolver服务器首先会检查缓存,并且在必要的时候再向authoritative nameservers发送查询请求。
但是在这两件事情背后,我们有几个问题需要思考
解析服务器(即上面提到的
resolver)的缓存中存放了一些什么东西?在计算机中,在发起一个
DNS请求的时候调用的是哪一部分的库?举个例子,一个请求有可能是由
libc中提供的getaddrinfo发起的,这部分代码或是来自glibc,或是musl,又或者是apple提供的库文件;这个请求也有可能是在浏览器中发起,由浏览器进行处理;当然也有可能是某些特定的自定义实现。在不同的阶段和方法进行
DNS请求做的事情都会略有不同,他们或多或少会有不一样的配置、缓存以及功能。举个例子来说,直到今年(2023)musl的DNS才开始支持TCP询问解析器和权威域名服务器(即上文中提到的
authoritative nameservers)之间是如何进行通话的?在这里我们如果能知道在
DNS请求期间询问了哪些下游的权威域名服务器,以及他们提供了哪些信息,则很多东西都会非常好理解。
了解系统背后发生的事情
获取更详细的DNS信息
为了让我们可以在获取DNS请求的时候,获取到更多的调试信息,我们可以尝试用dig工具来获取一些信息。一个例子如下
1 | |
在上面这个示例中,我通过向一个不存在的域名:whatdontexist.lol发起了一个DNS请求。我们可以在这里看到许多有意思的信息,比如我们是对223.5.5.5这个DNS服务器,通过UDP发起的请求等等。
奇妙的调试信息
通过使用dig,我们可以知道很多额外的信息。举个例子,我们可以使用dig +norecurse指令来弄清楚DNS解析服务器目前有没有针对某个特定记录的缓存。对于某个特定的记录,如果不存在缓存,则会返回一个SERVFAIL的状态
举个例子,我们首先可以向223.5.5.5这个阿里的DNS请求www.baidu.com的DNS条目(大概率来说应该是缓存了百度的域名解析的)

我们可以看到上图中获取到的status为NOERROR,也就意味着百度在阿里云的DNS服务器中可以正常返回需要的结果。
现在我们再尝试请求一个不是很常见的域名homestarrunner.com,我们可以发现status变成了SERVFAIL

这里的SERVFAIL并不代表不存在对于homestarrunner.com的域名解析,只是在阿里云的服务器中并没有缓存这个域名而已。
不过对于上面这一长串由dig提供的调试信息,如果我们不是经常和它打交道的话看着还是会有点迷惑。比如:
->>HEADER<<-,flags:,OPT PSEUDOSECTION:,QUESTION SECTION:,MSG SIZE rcvd: 59这些都是啥玩意?- 为什么有的地方看起来是新内容但没换行(比如
OPT PSEUDOSECTION:和QUESTION SECTION:)? - 为什么有的返回报文中
MSG SIZE rcvd: 59这里是59,而有的报文又是90?不同的报文之间是有啥不同的字段吗?
总之,从上面的一些问题中,我们可以发现dig的一些输出内容有点像是某些人临时写的一个用于获取这些信息的脚本,而并非有意为了可读性进行一些刻意的设计,为此我们有的时候需要查阅文档来搞懂发生了什么。
一些小工具
这里有一篇文章是原博客作者介绍了如何使用
dig的:How to use dig (jvns.ca)还有三个工具可以用于更友好的进行一些简单的调试:
- ogham/dog: A command-line DNS client. (github.com)
- mr-karan/doggo: 🐶 Command-line DNS Client for Humans. Written in Golang (github.com)
- a simple DNS lookup tool (jvns.ca)
不过对于这些工具有的时候会缺少一些高级功能,比如在原博客发布的时候
dog和doggo都还不支持+norecurse这样的功能。所以有的时候学会dig还是有用的通过添加一个
+yaml的参数,可以让dig的输出信息更加格式化。不过这样的缺点是返回的信息有点太多了。
小心踩坑
在DNS请求中,总是会出现一些奇奇怪怪但是容易不小心就掉进去的陷进。
一些更常见的问题可以翻阅这篇博客:Some ways DNS can break (jvns.ca)
被动缓存:由于
DNS会记录不存在的域名,因此假设你在类似cloudflare一类的平台添加自己的DNS解析之前,如果先在自己电脑上访问并被系统缓存了"该域名不存在解析条目"这个结果,即使你后面为这个域名添加了有效的记录,但也要等到之前那条缓存失效了,新的有效结果才能被识别到。在不同平台上对于
getaddrinfo的实现并不相同,比如在今年之前,你是没法通过tcp来在musl平台上发起一个dns请求的。有的解析服务器并不尊重解析本应该有的
TTL,比如原本你设置了一个域名abc.com的TTL为一分钟或者两分钟。但是路由器认为大部分网络服务都是稳定的(对于非开发人员来说其实倒也是),有可能就会忽略这一点,而硬给你设置一个比如一两个小时的TTL。结果就会导致本来两分钟就应该生效的修改过了一两个小时都没好在
Nginx中,如果你按下面的方式配置了一个反向代理1
2
3location / {
proxy_pass https://some.domain.com;
}那么
Nginx只会在第一次启动的时候尝试解析这个域名,之后则再也不会进行解析。如果你在这个过程中修改了some.domain.com域名指向的IP,就很有可能会出现一些不应该出现的问题。这个问题实际上也有一些解决方案,不过不是这篇博客的重点
ndonts会使得k8s中的DNS请求变慢:Kubernetes pods /etc/resolv.conf ndots:5 option and why it may negatively affect your application performances (pracucci.com)由于我自己对
k8s目前接触不多,因此这里只是贴了一个链接。等以后有接触以后再回过头来研究研究
在DNS上踩坑往往可能是一件不起眼,但是遇到了就挺难排查的问题。也许最好的解决方案还是尽可能的去见识下别人遇到的问题。再放一次作者记录的常见问题:Some ways DNS can break (jvns.ca)