【DEBUG】一次 k3s 拉镜像失败的 DNS Debug:强制使用 Go Resolver 修复
·
一次 k3s 拉镜像失败的 DNS Debug:强制使用 Go Resolver 修复
背景
在一台 rancher 主机上运行 k3s,安装 cert-manager 时遇到网络问题。
环境大致如下:
Kubernetes: k3s
Runtime: k3s 内置 containerd
节点 IP: 10.10.5.30
系统 DNS: 127.0.0.53,systemd-resolved
上游 DNS: 10.10.0.1
网络出口: 路由器侧 TUN / fake-ip 代理
现象是,直接curl访问网站是正常的
curl https://github.com/...
但拉取构建脚本失败
root@rancher:~# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.crds.yaml
Unable to connect to the server: dial tcp: lookup github.com: Try again
r
拉取镜像失败
root@rancher:~# k3s crictl pull quay.io/jetstack/cert-manager-controller:v1.20.2
E0618 04:47:52.374034 3136 log.go:32] "PullImage from image service failed" err="rpc error: code = Unknown desc = failed to pull and unpack image \"quay.io/jetstack/cert-manager-controller:v1.20.2\": failed to resolve reference \"quay.io/jetstack/cert-manager-controller:v1.20.2\": failed to do request: Head \"https://quay.io/v2/jetstack/cert-manager-controller/manifests/v1.20.2\": dial tcp: lookup quay.io: Try again" image="quay.io/jetstack/cert-manager-controller:v1.20.2"
FATA[0005] pulling image: failed to pull and unpack image "quay.io/jetstack/cert-manager-controller:v1.20.2": failed to resolve reference "quay.io/jetstack/cert-manager-controller:v1.20.2": failed to do request: Head "https://quay.io/v2/jetstack/cert-manager-controller/manifests/v1.20.2": dial tcp: lookup quay.io: Try again
问题现象
手动拉镜像时报错:
failed to pull and unpack image "quay.io/jetstack/cert-manager-controller:v1.20.2":
failed to resolve reference "quay.io/jetstack/cert-manager-controller:v1.20.2":
failed to do request:
Head "https://quay.io/v2/jetstack/cert-manager-controller/manifests/v1.20.2":
dial tcp: lookup quay.io: Try again
同时 k3s 日志里,CoreDNS Pod 也无法启动,因为 pause 镜像拉不下来:
failed to get sandbox image "registry.cn-hangzhou.aliyuncs.com/rancher/mirrored-pause:3.6":
dial tcp: lookup registry.cn-hangzhou.aliyuncs.com: Try again
结论:问题发生在 DNS lookup 阶段,不是镜像仓库、TLS、认证或 cert-manager 配置问题。
抓包分析
抓 DNS 包:
sudo tcpdump -ni any -vvv port 53
然后执行:
sudo k3s crictl pull quay.io/jetstack/cert-manager-controller:v1.20.2
抓包结果类似:
lo In IP 127.0.0.1.33481 > 127.0.0.53.53: AAAA? quay.io.
ens18 Out IP 10.10.5.30.55586 > 10.10.0.1.53: AAAA? quay.io.
ens18 In IP 10.10.0.1.53 > 10.10.5.30.55586: 0/1/0
lo In IP 127.0.0.53.53 > 127.0.0.1.33481: ServFail

链路是:
k3s/containerd
-> 127.0.0.53
-> systemd-resolved
-> 10.10.0.1
-> AAAA? quay.io
-> SERVFAIL
-> containerd 报 lookup quay.io: Try again
关键点:只看到了 AAAA 查询,没有看到 A 查询。
对比 Go Resolver 和 cgo Resolver
kubectl / k3s / containerd 都是 Go 生态程序。Go 有两类 DNS resolver:
netdns=go 使用 Go 纯 DNS resolver
netdns=cgo 使用 libc/getaddrinfo
测试 Go resolver:
GODEBUG=netdns=go+2 kubectl apply -f https://gith1ub.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.crds.yaml
抓包结果:
A? gith1ub.com
AAAA? gith1ub.com
测试 cgo resolver:
GODEBUG=netdns=cgo+2 kubectl apply -f https://gith1ub.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.crds.yaml
抓包结果:
AAAA? gith1ub.com
结论:
Go 纯 resolver 正常,会查 A 和 AAAA
cgo/libc resolver 异常,只查 AAAA
因此问题集中在:
cgo / libc / getaddrinfo / systemd-resolved 这条 DNS 解析链路
根因判断
k3s/containerd 默认 DNS 解析路径触发了 cgo/libc 行为:
k3s/containerd
-> libc/getaddrinfo
-> /etc/resolv.conf
-> 127.0.0.53
-> systemd-resolved
-> 10.10.0.1
这条链路只发起 AAAA 查询。
而 AAAA 查询返回 SERVFAIL 后,containerd 直接判定 DNS lookup 失败,没有继续正常使用 IPv4 A 记录。
所以最终表现为:
lookup quay.io: Try again
修复方案:强制 k3s 使用 Go DNS resolver
创建 systemd override:
sudo mkdir -p /etc/systemd/system/k3s.service.d
sudo tee /etc/systemd/system/k3s.service.d/dns.conf >/dev/null <<'EOF'
[Service]
Environment="GODEBUG=netdns=go"
EOF
重新加载 systemd:
sudo systemctl daemon-reload
重启 k3s:
sudo systemctl restart k3s
确认环境变量生效:
sudo systemctl show k3s --property=Environment
应该看到:
Environment=GODEBUG=netdns=go
测试镜像拉取:
sudo k3s crictl pull registry.cn-hangzhou.aliyuncs.com/rancher/mirrored-pause:3.6
sudo k3s crictl pull quay.io/jetstack/cert-manager-controller:v1.20.2
同时抓包验证:
sudo tcpdump -ni any -vvv port 53
预期可以看到:
A? quay.io
AAAA? quay.io
不再是只查 AAAA。
最终结论
这次问题不是路由器代理不可用,也不是 k3s 不支持 fake-ip,而是:
k3s/containerd 的 DNS resolver 走到 cgo/libc 路径时,只发起 AAAA 查询;
AAAA 查询经过 127.0.0.53 -> 10.10.0.1 后返回 SERVFAIL;
containerd 因此报 lookup Try again,镜像拉取失败。
最终修复:
给 k3s systemd 服务设置 GODEBUG=netdns=go
强制 k3s/containerd 使用 Go 纯 DNS resolver
排查命令汇总
# 查看 k3s 日志
sudo journalctl -u k3s -f
# 手动拉镜像
sudo k3s crictl pull quay.io/jetstack/cert-manager-controller:v1.20.2
# 抓 DNS 包
sudo tcpdump -ni any -vvv port 53
# 查看系统 DNS
cat /etc/resolv.conf
resolvectl status
# 查看 NSS 配置
grep '^hosts:' /etc/nsswitch.conf
# 查看 gai 配置
grep -v '^#' /etc/gai.conf | grep -v '^$'
# 测试 Go resolver
GODEBUG=netdns=go+2 kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.crds.yaml
# 测试 cgo resolver
GODEBUG=netdns=cgo+2 kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.crds.yaml
# 给 k3s 设置 Go resolver
sudo mkdir -p /etc/systemd/system/k3s.service.d
sudo tee /etc/systemd/system/k3s.service.d/dns.conf >/dev/null <<'EOF'
[Service]
Environment="GODEBUG=netdns=go"
EOF
sudo systemctl daemon-reload
sudo systemctl restart k3s
# 验证环境变量
sudo systemctl show k3s --property=Environment
经验教训
curl正常,不代表 k3s/containerd 正常。docker pull正常,不代表 k3s 内置 containerd 正常。lookup xxx: Try again优先排查 DNS。- 禁用 IPv6 不等于禁止 AAAA 查询。
- Go 程序的
netdns=go和netdns=cgo行为可能完全不同。 - 抓包比猜配置有效。
更多推荐




所有评论(0)