一文搞懂域名解析DNS相关问题
本文希望通过一篇文章解决所有域名解析中相关的问题
最后会通过实际工作中碰到的不同场景下几个DNS问题的分析过程来理解DNS
这几个Case描述如下:
- 一批ECS nslookup 域名结果正确,但是 ping 域名 返回 unknown host
- Docker容器中的域名解析过程和原理
- 中间件的VipClient服务在centos7上域名解析失败
- 在公司网下,我的windows7笔记本的wifi总是报dns域名异常无法上网(通过IP地址可以上网)
因为这些问题都不一样,但是都跟DNS服务相关所以打算分四篇文章挨个介绍,希望看完后能加深对DNS原理的理解并独立解决任何DNS问题。
下面我们就先开始介绍下DNS解析原理和流程。
Linux下域名解析流程
- DNS域名解析的时候先根据 /etc/host.conf、/etc/nsswitch.conf 配置的顺序进行dns解析(name service switch),一般是这样配置:hosts: files dns 【files代表 /etc/hosts ; dns 代表 /etc/resolv.conf】(ping是这个流程,但是nslookup和dig不是)
- 如果本地有DNS Client Cache,先走Cache查询,所以有时候看不到DNS网络包。Linux下nscd可以做这个cache,Windows下有 ipconfig /displaydns ipconfig /flushdns
- 如果 /etc/resolv.conf 中配置了多个nameserver,默认使用第一个,只有第一个失败【如53端口不响应、查不到域名后再用后面的nameserver顶上】
上述描述主要是阐述的图中 stub resolver部分的详细流程。这部分流程出问题才是程序员实际中更多碰到的场景
以下是一个 /etc/nsswitch.conf
1 | # cat /etc/nsswitch.conf |grep -v "^#" |grep -v "^$" |
这个配置中的解析顺序是:files->dns->myhostname, 这个顺序可以调整和配置。
Linux下域名解析流程需要注意的地方
- 如果 /etc/resolv.conf 中配置了rotate,那么多个nameserver轮流使用. 但是因为底层库的原因用了rotate 会触发nameserver排序的时候第二个总是排在第一位
- 如果被解析的域名不是以 “.” 结尾,那么解释失败后还会尝试resolv.conf中search追加到后面,resolv.conf最多支持6个search域
- ping 调用的是 glibc的 gethostbyname() 函数流程–背后是NameServiceSwitch,nslookup 不是.所以你会经常看到其中一个可以另一个不可以,那么就要按第一部分讲解的流程来排查了。
Linux下域名解析诊断工具
- ping
- nslookup (nslookup domain @dns-server-ip)
- dig (dig +trace domain)
- tcpdump (tcpdump -i eth0 host server-ip and port 53 and udp)
- strace
案例
如下,向 /etc/hosts 中添加两条记录,一条是test.unknow.host 无法解析到,但是另一条 test.localhost 可以解析到,为啥呢?
$head -2 /etc/hosts
127.0.0.1 test.unknow.host
127.0.0.1 test.localhost
$ping test.unknow.host
ping: unknown host test.unknow.host
$ping -c 1 test.localhost
PING test.localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from test.localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.016 ms
为什么 test.unknow.host 没法解析到? 可能有哪些因素导致这种现象?尝试 ping -c 1 test.localhost 的目的是做什么?
看完前面的理论我的猜测是两种可能导致这种情况:
- /etc/hosts 没有启用
- 有本地缓存记录了一个unknow host记录
验证
strace -e trace=open -f ping -c 1 test.localhost
可以通,说明 /etc/hosts 是在起作用的,所以最好验证 /etc/hosts 在起作用的方法是往其中添加一条新纪录,然后验证一下
那接下来只能看本地有没有启动 nscd 这样的缓存了,见后发现也没有,这个时候就可以上 strace 追踪ping的流程了
从上图可以清晰地看到读取了 /etc/host.conf, 然后读了 /etc/hosts, 再然后读取到我们添加的那条记录,似乎没问题,仔细看这应该是 ip地址后面带的是一个中文字符的空格,这就是问题所在。
到这里可能的情况要追加第三种了:
- /etc/hosts 中添加的记录没生效(比如中文符号)
dhcp
如果启用了dhcp,那么dhclient会更新在Network Manager启动的时候更新 /etc/resolv.conf
dnsmasq
一般会在127.0.0.1:53上启动dns server服务,配置文件对应在:/run/dnsmasq/resolv.conf。集团内部的vipclient就是类似这个原理。
微服务下的域名解析、负载均衡
微服务中多个服务之间一般都是通过一个vip或者域名之类的来做服务发现和负载均衡、弹性伸缩,所以这里也需要域名解析(一个微服务申请一个域名)
域名解析通过jar、lib包
基本与上面的逻辑没什么关系,jar包会去通过特定的协议联系server,解析出域名对应的多个ip、机房、权重等
域名解析通过dns server
跟前面介绍逻辑一致,一般是/etc/resolv.conf中配置的第一个nameserver负责解析微服务的域名,解析不到的(如baidu.com)再转发给上一级通用的dns server,解析到了说明是微服务自定义的域名,就可以返回来了
如果这种情况下/etc/resolv.conf中配置的第一个nameserver是127.0.0.1,意味着本地跑了一个dns server, 这个服务使用dns协议监听本地udp 53端口
验证方式: nslookup 域名 @127.0.0.1 看看能否解析到你想要的地址
kubernetes 和 docker中的域名解析
一般是通过iptables配置转发规则来实现,这种用iptables和tcpdump基本都可以看清楚。如果是集群内部的话可以通过CoreDNS来实现,通过K8S动态向CoreDNS增删域名,增删ip,所以这种域名肯定只能在k8s集群内部使用
nginx 中的域名解析
nginx可以自定义resolver,也可以通过读取 /etc/resolv.conf转换而来,要注意对 /etc/resolv.conf中 注释的兼容
https://github.com/blacklabelops-legacy/nginx/issues/36 可能是nginx读取 /etc/resolv.conf没有处理好 # 注释的问题
进一步的Case学习:
- 一批ECS nslookup 域名结果正确,但是 ping 域名 返回 unknown host
- Docker容器中的域名解析过程和原理
- 中间件的VipClient服务在centos7上域名解析失败
- 在公司网下,我的windows7笔记本的wifi总是报dns域名异常无法上网(通过IP地址可以上网)