现象

在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 to default, 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为啥一直没有影响到其他事情呢?

  1. 这种情况少
  2. 一般客户端会处理好这些事,最常见就是查询AAAA无应答,会转去查询A记录。

最终结论

K8s给pod加上了ndots,导致pod里会拿search domain拼装的域名去查。

路由器的dns server写的不太合理,导致会把上游的NXDOMAIN变成No Answer。

最终就导致了:对Linux的解析过程来说,这个search domain域名存在,不再往下查了。但是是No Answer,就是个bad address。

至于为什么nslookup没问题: nslookup不认这个ndots参数。