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)