现象
在K8s的集群中,有部分容器内部(其实大部分,也许是所有),可以正常nslookup域名,但ping一个域名的时候,会报错
/ # nslookup baidu.com
Server: 10.43.0.10
Address: 10.43.0.10:53
Non-authoritative answer:
Non-authoritative answer:
Name: baidu.com
Address: 220.181.38.148
Name: baidu.com
Address: 220.181.38.251
/ # ping baidu.com
ping: bad address 'baidu.com'
但是在k8s各个节点上用docker运行同样的镜像,却没有这个问题。
最后几经搜索,发现了问题。
奇怪的配置(其实不算坑
k8s会替换容器内的/etc/resolv.conf
。
If a Pod’s
dnsPolicy
is set todefault
, it inherits the name resolution configuration from the node that the Pod runs on. The Pod’s DNS resolution should behave the same as the node. But see Known issues.
会修改DNS配置,还会额外附赠一项新的参数
ndots:5
意思就是,当查询的域名中的.
少于5的时候,会先用你的域名拼接上search domain,进行查询,如果查不到,再去查询该域名。
比如某个容器内部的/etc/resolv.conf
nameserver 10.43.0.10
search default.svc.cluster.local svc.cluster.local cluster.local lan
options ndots:5
当查询github.com
,会先去查询
github.com.default.svc.cluster.local
github.com.svc.cluster.local
github.com.cluster.local
github.com.lan
前三个域名,都是k8s内部的域名,没有任何问题,CoreDNS会处理好。
第四个域名,就是另外一个坑了。
菜🐔的DNS实现
这个lan的search domain,是路由器下发的,当你直接用pve/win等hostname去进行访问的时候,几乎所有系统都会帮你拼接上某个search domain,也几乎所有路由器系统都会妥善处理这些问题。
然而路由器openwrt上的dns是我自己写的。虽然实现了对search domain的支持。(读取dhcp的租约文件
当一个search domain的域名不存在于租约文件中时,我没找到相关的RFC,应该怎么着都行。直接返回NXDOMAIN(不存在该域名)应该是不会有什么问题的。
我的处理是直接扔给上游处理了,理论上也不会有太大问题,就是有点慢,毕竟.lan不是一个公开的合法域名后缀。
问题在于我从拿到上游的返回到扔给客户端中间做的一个操作,导致了问题的发生。
我这边封装了缓存的实现,有时候拿到的response是缓存的,需要让缓存的response也能和最新的request对应起来,调用了一句
resp.SetReply(w.msg)
但是第三方包的这个函数还做了一些隐含的事情。
/github.com/miekg/dns@v1.1.43/defaults.go:15
func (dns *Msg) SetReply(request *Msg) *Msg {
dns.Id = request.Id
dns.Response = true
dns.Opcode = request.Opcode
if dns.Opcode == OpcodeQuery {
dns.RecursionDesired = request.RecursionDesired // Copy rd bit
dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
}
dns.Rcode = RcodeSuccess
if len(request.Question) > 0 {
dns.Question = make([]Question, 1)
dns.Question[0] = request.Question[0]
}
return dns
}
其中包括了一句dns.Rcode = RcodeSuccess
,让所有的错误都消失了。
这个bug为啥一直没有影响到其他事情呢?
- 这种情况少
- 一般客户端会处理好这些事,最常见就是查询AAAA无应答,会转去查询A记录。
最终结论
K8s给pod加上了ndots,导致pod里会拿search domain拼装的域名去查。
路由器的dns server写的不太合理,导致会把上游的NXDOMAIN变成No Answer。
最终就导致了:对Linux的解析过程来说,这个search domain域名存在,不再往下查了。但是是No Answer,就是个bad address。
至于为什么nslookup没问题: nslookup不认这个ndots参数。