看到 Docker 容器里 curl、apt、pip 报 Temporary failure in name resolution,第一步不是改任何配置,而是先看一眼容器里的 /etc/resolv.conf 写的是什么、再到宿主机上用同样的 nameserver 验证一次:
docker exec <容器> cat /etc/resolv.conf
nslookup example.com 127.0.0.53
如果里面 nameserver 指向的地址在容器网络里根本不可达——最典型的就是 127.0.0.53——那这个报错就是必然结果,因为容器有自己的网络命名空间,里面 127.0.0.53 指向的是容器自己的 loopback,不是宿主机上 systemd-resolved 监听的那个地址。宿主机上能解析就说明上游解析服务是好的,问题出在容器隔离出来的网络栈访问不到宿主 loopback。这是绝大多数人第一次遇到这个报错时碰到的情况,三种里处理路径最短。
/etc/resolv.conf 在容器里大致会呈现三种样子,每一种对应不同的处理路径。
第一种是指向 127.0.0.53 的,这是 Ubuntu 18.04 之后默认用 systemd-resolved 时宿主机的写法。Docker 在构建容器网络时会把宿主这份文件拷进容器,结果就是把一个只在宿主机 loopback 上有监听的地址塞了进去。修法按侵入性从小到大有三个层次:单次启动可以用 --dns 把外部 DNS 注入到这一次容器;想长期生效就在 /etc/docker/daemon.json 里加 dns 字段然后重新加载 daemon 的配置;更激进的方案是直接把宿主机的 systemd-resolved 停掉、自己写一份 /etc/resolv.conf 指向公网 DNS ,但这会影响宿主机本身的解析,不建议在已经稳定运行的服务器上做。
前两层修法对应的命令:
docker run --dns 8.8.8.8 --dns 1.1.1.1 ...
systemctl reload docker
第二种是指向 8.8.8.8 之类但仍然报错的。这种情况要分两头查:一是 --dns 参数可能被 docker-compose 的服务配置或者 network_mode: host 覆盖回去;二是宿主机到 8.8.0.0/16 的出网被防火墙拦掉了。直接在宿主机上验证一次就能确认是哪种:
nslookup example.com 8.8.8.8
如果是后者,问题根本不在容器,而是宿主机本身就没有可用的 DNS,这种时候容器自然也救不回来。
第三种是文件几乎是空的、或者只有一行 nameserver 8.8.8.8。这种情况多半是 daemon.json 写完之后漏掉了重新加载 docker 那一步、或者容器是用改配置之前的旧配置启动的——Docker 只在容器创建时把解析配置写进去一次,已经跑着的容器不会因为 daemon.json 变了就自动更新。所以改完配置之后,旧容器必须重建才会生效:
systemctl reload docker
docker compose up -d
# 或者
docker rm && docker run
除了容器内 resolv.conf 这个问题,WSL2 上跑 Docker Desktop 时也偶发这个错,报错位置一样、根因不太一样。在 WSL2 里跑 Docker Desktop,DNS 链路是 WSL 自己的 stub resolver 转给 Windows 主机再出去,链路更长,Docker Desktop 启动时序一旦跟 WSL 的 resolv.conf 初始化对不上就会出现同样的 Temporary failure。微软 WSL 仓库里这个 issue 的解决路径是删掉默认 WSL 实例再重建,看着粗暴但确实管用;更精细的做法是在 /etc/wsl.conf 里设 generateResolvConf = false 然后手工写一份 resolv.conf,但这样会让 Ubuntu 和 Windows 主机之间的某些互连失效,所以那个 issue 的提问者一直拖着没动——他权衡下来觉得重建实例更省事。
Compose 项目里服务名互相解析失败的情况通常不是这个错。Docker 在自定义网络里内置了一个 DNS server,服务名解析走的是它而不是 /etc/resolv.conf,所以正常情况下 database、redis 这种服务名在同网络的其他容器里应该 ping 得通。一旦报 Temporary failure in name resolution,多半是 compose 文件里写了 network_mode: host——host 模式下容器直接用宿主网络栈,Docker 内置 DNS 不参与,服务名自然就没记录了,要解析就只能自己写到 /etc/hosts。另外一个边界情况是 Podman 5.3.0 上跑 docker-compose,曾经出现解析 db 容器失败的回归 bug ,podman 那边确认重启或回退到 5.2.5 就好——这种是上游的 bug,不是配置能解决的。
本文没覆盖到的边界还有几处。rootless Docker 的解析链路跟普通 Docker daemon 不一样,它走的是用户态的 slirp4netns 或者 pasta,DNS 配置在另一处;K8s Pod 里的 DNS 走 kubelet 的 CoreDNS,跟 docker daemon 没关系,处理方式完全不一样;WSL2 那个重建实例的方案在用户看来像是「重启一下就好了」,不是真正意义上的修复,WSL 上游并没有给出一个稳定的长期解。本文能覆盖到的范围是:你看到的报错如果是来自 Docker daemon 创建的普通容器,那上面的三种 resolv.conf 处理路径大概率够用;一旦环境涉及到 rootless、k8s 或者非 docker 的容器运行时,去对应的官方文档里查。











暂无评论内容