k3s的Pod无法解析内网域名

问题

jetson tx2开发板上安装了dockerk3s,部署了一个pod,发现日志报错

1
"dial tcp: lookup esmp-cloud-sync.dev.ennew.com on 10.43.0.10:53: no such host"

其中esmp-cloud-sync.dev.ennew.com是内网域名,说明pod无法解析该域名。

但是宿主机上能ping通该域名。

环境

  • os : ubuntu18.04
  • k3s: v1.18.9+k3s1
  • cpu arch : arm64
  • coreDNS: 1.6.9

排查过程

首先检查宿主机的域名服务器设置。 如果不正常,则需要设置正确的域名服务器。并重启k3s服务和pod。 如果正常,则进一步检查k3s的DNS设置。而k3s的网络域名解析是由coreDNS控制的。

查看宿主机的DNS设置

宿主机的DNS设置,查看/etc/resolv.conf

$ cat /etc/resolv.conf 
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
# 127.0.0.53 is the systemd-resolved stub resolver.
# run "systemd-resolve --status" to see details about the actual nameservers.

nameserver 127.0.0.53
search addom.xinaogroup.com

运行”systemd-resolve –status”查看实际的nameservers。

$ systemd-resolve --status
...
      DNS Servers: 10.36.8.40
                    10.36.8.41
                    127.0.0.1
        DNS Domain: ~.
                    addom.xinaogroup.com 
...

可以看到实际的域名服务器。pod里无法ping通的域名esmp-cloud-sync.dev.ennew.com,在宿主机环境是可以的。

查看pod的DNS设置

pod的DNS设置是由CoreDNS控制的。但是进入CoreDNS的pod,使用kubectl exec是不行的。需要使用边车模式, 先查看运行CoreDNS的容器的ID,然后用docker再启动一个容器。(因为k3s server是基于dockerd运行的,所以 可以用docker ps查看CoreDNS pod里的容器)

docker ps | grep coredns
ID=8afb33b91c9f
docker run -it --net=container:$ID --pid=container:$ID --volumes-from=$ID alpine sh

然后就可以查看CoreDNS的corefile配置文件

# cat /etc/coredns/Corefile 
.:53 {
    errors
    health
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
      pods insecure
      upstream
      fallthrough in-addr.arpa ip6.arpa
    }
    hosts /etc/coredns/NodeHosts {
      reload 1s
      fallthrough
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}

forward . /etc/resolv.conf 可以看出,由/etc/resolv.conf文件接管DNS设置。进一步查看该文件,

  # cat /etc/resolv.conf 
  nameserver 8.8.8.8

可以发现这里设置的nameserver8.8.8.8,而不是10.36.8.40。因此在pod里虽然可以ping通公网域名 ,例如www.baidu.com,但是无法ping通内网域名esmp-cloud-sync.dev.ennew.com

另外查看CoreDNS的pod日志也可以看到无法解析内网域名esmp-cloud-sync.dev.ennew.com的错误。

分析问题

从排查结果可以看到,主要问题在于CoreDNS的DSN设置与宿主机的DNS设置不同,导致解析内网域名解析失败。这一点比较奇怪,通常k3s默认 会继承宿主机的DNS设置。

也就是pod里的/etc/resolv.conf,没有与宿主机的/etc/resolv.conf的内容一致。

再回头看看我们要解决的问题——pod里解析内网域名,那么最直接的方案就是修改CoreDNS的Corefile, 修改forward . /etc/resolv.confforward . 10.36.8.40

但是这种方法的缺点是显而易见的,写死了。 我们还是希望找到一个方法能够令/etc/resolv.conf与宿主机的/etc/resolv.conf的内容一致

解决方法

宿主机的/etc/resolv.conf文件是一个指向 /run/systemd/resolve/resolv.conf的软链接,查看该文件的内容

$ cat /run/systemd/resolve/resolv.conf
# This file is managed by man:systemd-resolved(8). Do not edit.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 10.36.8.40
nameserver 10.36.8.41
nameserver 127.0.0.1
search addom.xinaogroup.com

可以看到含有正确的nameservers。

另外 k3s 有启动参数--resolv-conf,可以指定默认的resolv.conf

修改/etc/systemd/system/k3s.service,增加启动参数--resolv-conf /run/systemd/resolve/resolv.conf

...
ExecStart=/usr/local/bin/k3s \
server \
'--docker' \
'--write-kubeconfig' \
'/home/tx2/.kube/config' \
'--write-kubeconfig-mode' \
'666' \
'--resolv-conf' \
'/run/systemd/resolve/resolv.conf'

然后重新启动k3s,并且删除CoreDNS的pod(kubectl delete pod -n kube-system coredns-xxxx),令其自动创建一个新的pod。

这时候再查看CoreDNS pod里的/etc/resolv.conf, 内容一致了,

/ # cat /etc/resolv.conf
nameserver 10.36.8.40
nameserver 10.36.8.41
nameserver 127.0.0.1
search addom.xinaogroup.com

问题解决了。

参考