一、Linux DNS查询解析原理(对于调用glibc库函数gethostbyname的程序)
我们在浏览器访问www.baidu.com这个域名,dns怎么查询到这台主机呢?
1、在浏览器中输入www.baidu.com域名,操作系统会先查找本地DNS解析器缓存(nscd),是否有这个网址映射关系,如果有,直接返回,完成域名解析。
2、如果本地DNS解析器缓存(nscd)里没有这个域名的映射或是没有安装nscd服务,则通过/etc/nsswitch.conf中的hosts配置项来决定域名查询获取顺序。
通常该配置项为"hosts: files dns myhostname",则表示先读/etc/hosts,否则就读/etc/resolv.conf向DNS服务器发出域名解析请求。
3、如果本地DNS解析器缓存与/etc/hosts都没有相应的网址映射关系,首先会找/etc/resolv.conf中设置的第一个DNS服务器,在此我们叫它本地DNS服务器,
此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析记过给客户端,完成域名解析,此解析具有权威性。 4、如果要查询域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。 5、如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(baidu.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找baidu.com域服务器,重复上面的动作,进行查询,直至找到www.baidu.com主机。 6、如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把请求转至上上级,以此循环。不管是本地DNS服务器用是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。
二、K8S DNS解析过程
1、node节点(VM)DNS解析
(1) ping www.baidu.com -->Node上 local DNS cache(Linux nscd服务,如果有DNS缓存服务的情况,否则跳过) ->
(2) Node上/etc/hosts文件 ->
(3) DNS Server(Node上/etc/resolv.conf中记录的nameserver(一般是按顺序取第一个) ) ->
(4) DNS Server Cache ->
(5) 如果第一个上游DNS Server没查到,那么该DNS Server转发给上一级DNS Server或根DNS Server,以此循环
2、K8S Pod DNS解析
(1) pingwww.baidu.com -> Pod的容器里 local DNS cache(一般情况下容器镜像不会安装Linux nscd服务,这里跳过) ->
(2) Pod的容器里/etc/hosts文件->
例如:测试Pod对应容器里的 /etc/hosts
(3) CoreDNS(Pod的容器里/etc/resolv.conf中记录的nameserver(一般这里Pod dnsPolicy策略默认设置的是ClusterFirst,所以该nameserver为CoreDNS的Cluster IP) )->
例如:测试Pod对应容器里的 /etc/resolv.conf, 这里的10.0.248.10为CoreDNS的Cluster IP
下图是对应CoreDNS的Service信息:
(4) CoreDNS Cache(CoreDNS cache插件,该插件会缓存已经查询过的DNS解析的信息,见下图的cache字段)->
(5) 如果CoreDNS没查到(一般只提供了kubernetes集群内的部域名的解析,具体是CoreDNS kubernetes插件),那么CoreDNS可以通过forward(内置,见上图中的forward字段)或proxy插件(第三方单独提供)转发给上游DNS Server。
当然,forward插件可配置查找当前容器内的/etc/resolv.conf文件的nameserver(配置可写为:forward . /etc/resolv.conf),注意CoreDNS的Pod dnsPolicy策略为Default,所以/etc/resolv.conf文件内容与node节点保持一致,如下:
三、参考文献
https://www.cnblogs.com/zhangxingeng/p/9970733.html
https://coredns.io/plugins/
https://coredns.io/manual/toc/
https://github.com/coredns/proxy
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#coredns
K8S DNS 5s延迟问题说明
一、问题复现环境
k8s托管集群版本:1.14
k8s node镜像系统:centos 7.4/7.6
node内核版本:3.10.xxx
二、问题描述
1.客户在k8s node上做curl请求(没有dns缓存),经常有响应时间达到5秒以上的请求。
2.客户在k8s pod上做curl请求(没有dns缓存),经常有响应达5s以上的请求。
命令行: curl -o /dev/null -s -w "time_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_nslookup:%{time_namelookup}\ntime_total: %{time_total}\n" "https://api.weixin.qq.com"
三、问题定位
问题1:
(1)使用curl命令行做测试,问题概率性复现,10%左右。
(2)使用dig 命令行做测试, dig api.weixin.qq.com, 问题概率性复现。
(3)tcpdump在node上抓包分析,结合wireshark做分析,发现DNS客户端通过UDP协议并发查询A记录(ipv4)和AAAA记录(ipv6),并且使用了相同五元组,但是有AAAA记录未收到的情况。
当AAAA记录未收到时,即产生了5s左右的延迟。通过查询文档,发现dns请求默认超时时间为5s。
(4)SDN侧定位发现没有丢包,丢包位置为内核协议栈。
(5)内核侧定位,认为丢包是由内核协议栈contrack模块bug导致的。触发条件为:UDP使用相同五元组做并发请求时。
参见:
https://cloud.tencent.com/developer/article/1449227
https://www.weave.works/blog/racy-conntrack-and-dns-lookup-timeouts
4.19以上内核合入了修复patch。
问题2:定位方式及原因同1。
四、解决方案
1.规避方案:并发dns请求使用不同的源端口来避免内核收包冲突。
对于node,在resolv.conf中增加options single-request-reopen。
对于pod,在pod yaml中添加dns配置,需要客户侧自己管理配置。
template:
spec:
dnsConfig:
options:
- name: single-request-reopen