问题现场

kubernetes coredns配置

.:53 {
        errors
        health
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          fallthrough in-addr.arpa ip6.arpa
        }
        hosts /etc/coredns/NodeHosts {
          ttl 60
          reload 15s
          fallthrough
        }
        prometheus :9153
        forward . 172.x.8.8 {
          prefer_udp
        }
        cache 30
        loop
        reload
        loadbalance
        log
    }

pod resolv.conf配置

nameserver 10.43.0.10
search default.svc.cluster.local svc.cluster.local cluster.local svc.inner
options ndots:5

预期解析:

  • 集群内service用服务名解析到集群内服务。
  • svc.inner域内forward到172.x.8.8内网dns上,解析到内网dns注册的服务。
  • 其他公共域通过内网172.x.8.8的再上游,同样可以解析。

实际现象:

  • baidu.com在使用ubuntu基础镜像的pod内可以ping通。
  • baidu.com在使用alpine linux 3.15基础镜像的pod内报bad address,其他内网域名但是不是svc.inner域的报could not resolve host。

根因分析

当程序使用系统libc库做dns resolv的时候,走resolv.conf文件的配置,使用非完全限定域(即后面不带".", “baidu.com.“为完全限定,“baidu.com"为非完全限定)且”.“数量不超过ndots配置(这里kubernetes默认是5)时会走search的配置,超过ndots会认为约等于完全限定直接解析。

search逻辑是,在请求域名后面拼上配置的子域,逐个向后搜索解析,如果都找不到最后直接使用请求域名做解析,所以解析域名尝试如下。

baidu.com.default.svc.cluster.local.
baidu.com.svc.cluster.local.
baidu.com.cluster.local.
baidu.com.svc.inner.
baidu.com.

打开集群coredns的日志抓alpine/musl libc的请求解析日志如下:

[INFO] 10.42.0.84:42628 - 64655 "AAAA IN baidu.com.default.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.000218161s
[INFO] 10.42.0.84:42628 - 64204 "A IN baidu.com.default.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.00024845s
[INFO] 10.42.0.84:51564 - 50608 "AAAA IN baidu.com.svc.cluster.local. udp 45 false 512" NXDOMAIN qr,aa,rd 138 0.000113998s
[INFO] 10.42.0.84:51564 - 50368 "A IN baidu.com.svc.cluster.local. udp 45 false 512" NXDOMAIN qr,aa,rd 138 0.000119976s
[INFO] 10.42.0.84:55236 - 16067 "AAAA IN baidu.com.cluster.local. udp 41 false 512" NXDOMAIN qr,aa,rd 134 0.00011871s
[INFO] 10.42.0.84:55236 - 15864 "A IN baidu.com.cluster.local. udp 41 false 512" NXDOMAIN qr,aa,rd 134 0.00010077s
[INFO] 10.42.0.84:57937 - 2123 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059392615s
[INFO] 10.42.0.84:57937 - 1930 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.061797511s
[INFO] 10.42.0.84:57937 - 2123 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059379512s
[INFO] 10.42.0.84:57937 - 1930 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.062035159s
[INFO] 10.42.0.84:57937 - 2123 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059311395s
[INFO] 10.42.0.84:57937 - 1930 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.061721482s
[INFO] 10.42.0.84:57937 - 2123 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059639921s
[INFO] 10.42.0.84:57937 - 1930 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.061894263s
[INFO] 10.42.0.84:57937 - 2123 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059525564s
[INFO] 10.42.0.84:57937 - 1930 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.062518585s
[INFO] 10.42.0.84:57937 - 2123 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059316764s
[INFO] 10.42.0.84:57937 - 1930 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.061810188s

ubuntu/glibc的请求日志如下:

[INFO] 10.42.0.75:49573 - 11268 "AAAA IN baidu.com.app-ops.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.000235088s
[INFO] 10.42.0.75:49573 - 59129 "A IN baidu.com.app-ops.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.000188461s
[INFO] 10.42.0.75:54693 - 6359 "AAAA IN baidu.com.svc.cluster.local. udp 45 false 512" NXDOMAIN qr,aa,rd 138 0.00012188s
[INFO] 10.42.0.75:54693 - 44242 "A IN baidu.com.svc.cluster.local. udp 45 false 512" NXDOMAIN qr,aa,rd 138 0.000096248s
[INFO] 10.42.0.75:34199 - 10294 "A IN baidu.com.cluster.local. udp 41 false 512" NXDOMAIN qr,aa,rd 134 0.000104275s
[INFO] 10.42.0.75:34199 - 64057 "AAAA IN baidu.com.cluster.local. udp 41 false 512" NXDOMAIN qr,aa,rd 134 0.000173845s
[INFO] 10.42.0.75:42076 - 10254 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059601407s
[INFO] 10.42.0.75:42076 - 24081 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.062046559s
[INFO] 10.42.0.75:42076 - 24081 "AAAA IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.059077476s
[INFO] 10.42.0.75:42076 - 10254 "A IN baidu.com.svc.inner. udp 37 false 512" SERVFAIL qr,rd 37 0.061514225s
[INFO] 10.42.0.75:52980 - 30220 "AAAA IN baidu.com. udp 27 false 512" NOERROR qr,aa,rd,ra 97 0.000639532s
[INFO] 10.42.0.75:52980 - 39944 "A IN baidu.com. udp 27 false 512" NOERROR qr,rd,ra 77 0.024779792s

baidu.com.svc.inner.在内网dns上肯定是不存在的(我们的内网dns也是用coredns),返回了一个SERVFAIL状态码。

问题就出在这:

  • glibc在DNS服务器返回SERVFAIL后,继续向后搜并最终返回到根域”.“解析获取到了NOERROR 正确的DNS结果。
  • musl libc认为SERVFAIL是DNS服务器的错误,应该报告给调用者,并且不向后继续搜了,并且还不返回到根域”.",所以就获取不到结果了。

musl libc和glibc这个行为差异,有不同的观点,所以至今(2024-07-19)还是这样的行为,也有相关的patch, 如: https://www.openwall.com/lists/musl/2018/03/30/6 并未合并。

个人观点: musl的态度没错,DNS服务器的实现返回的状态码不符合语义,但从客户端兜底和大家都错了这么多年了讲,咱就别较真了呗。

问题解决

最简单的方法:生产别用alpine镜像。

个人当场的解决方法: 因为我是开发环境,没有调用内网其他服务的需要,就在自己集群干掉了svc.inner内网域,没有人返回SERVFAIL了,就没有问题了。

实测一波,把最后一个域改成svc.lan,这个svc.lan forward到内网dns,不是内网的私域会再次forward到公共dns解析,返回NXDOMAIN,musl认为没问题,继续向后搜索到根域,就正确获取结果了。

所以musl libc是对的?DNS服务器的问题: 是自己的域,自己没有记录,为啥返回SERVFAIL。

貌似基于coredns写插件定义dns很容易犯"不标准"这个错?自己遇到不止一次基于coredns开发做权威的问题了,待有空研究研究coredns插件的玩法。

[INFO] 10.42.0.84:55744 - 20786 "AAAA IN baidu.com.default.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.000199026s
[INFO] 10.42.0.84:55744 - 20370 "A IN baidu.com.default.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.000095497s
[INFO] 10.42.0.84:47264 - 56647 "AAAA IN baidu.com.svc.cluster.local. udp 45 false 512" NXDOMAIN qr,aa,rd 138 0.000128401s
[INFO] 10.42.0.84:47264 - 56392 "A IN baidu.com.svc.cluster.local. udp 45 false 512" NXDOMAIN qr,aa,rd 138 0.00009345s
[INFO] 10.42.0.84:41203 - 59447 "AAAA IN baidu.com.cluster.local. udp 41 false 512" NXDOMAIN qr,aa,rd 134 0.00014027s
[INFO] 10.42.0.84:41203 - 59202 "A IN baidu.com.cluster.local. udp 41 false 512" NXDOMAIN qr,aa,rd 134 0.000207786s
[INFO] 10.42.0.84:54762 - 27818 "AAAA IN baidu.com.svc.lan. udp 35 false 512" NXDOMAIN qr,aa,rd,ra 110 0.000680326s
[INFO] 10.42.0.84:54762 - 27556 "A IN baidu.com.svc.lan. udp 35 false 512" NXDOMAIN qr,rd,ra 110 0.031160567s
[INFO] 10.42.0.84:35257 - 46005 "A IN baidu.com. udp 27 false 512" NOERROR qr,rd,ra 77 0.008211487s
[INFO] 10.42.0.84:35257 - 46267 "AAAA IN baidu.com. udp 27 false 512" NOERROR qr,rd,ra 97 0.196307927s

其他发现

一番研究alpine/musl libc的dns resolv实现,返现和传统不一致的地方还不止这一处。

alpine linux 3.18/musl libc 1.2.4之前,当dns udp结果大于512字节,会截断就获取不到正确结果了,因为貌似按照rfc截断语义,是这样,没毛病。但是传统大部分resolve实现大于512字节的结果会fallback 使用tcp。

也许是经不住大家diss了,musl终于在去年(2023)加上了dnc fallback tcp的实现,这个commit

参考链接

https://stackoverflow.com/questions/65181012/does-alpine-have-known-dns-issue-within-kubernetes
https://wiki.musl-libc.org/functional-differences-from-glibc.html#Name-Resolver/DNS
https://git.musl-libc.org/cgit/musl/tree/src/network/lookup_name.c?h=v1.2.4#n190
https://www.openwall.com/lists/musl/2018/03/30/6
https://help.dnsfilter.com/hc/en-us/articles/4408415850003-DNS-return-codes