用心打造
VPS知识分享网站

Docker容器能访问IP却不能解析域名怎么办?完整排查教程

容器里的程序突然提示 Temporary failure in name resolution,直接访问某个公网IP却能得到响应,这类现象很容易被误判成应用故障。重启容器后偶尔恢复,也不代表问题已经解决,因为DNS上游、Docker网络和宿主机解析配置都可能在下一次重建时再次触发。

我处理容器解析故障时,会先证明IP链路正常,再沿着应用解析库、容器里的 resolv.conf、Docker内置DNS和宿主机上游逐层检查。直接把公共DNS写进容器虽然可能暂时生效,却可能破坏内部服务名解析,也可能绕开企业内部域名和安全策略。

下面以Linux上的Docker Engine和Docker Compose为主。Docker Desktop、Swarm、Kubernetes及rootless模式的网络实现不同,命令和配置入口不能完全照搬。生产环境还要记录故障时间、容器名称、网络名称和镜像版本,避免容器重建后丢失现场。

容器解析失败

先确认故障只发生在域名解析

进入目标容器,分别测试IP连通和域名解析:

docker exec -it CONTAINER sh
ip route
ping -c 3 1.1.1.1
getent hosts example.com

精简镜像可能没有 pingdignslookup,但多数glibc镜像带有 getent。Alpine使用musl,命令和解析行为也可能不同。不要为了排查临时在生产容器里安装大量工具,这会改变容器状态;更稳妥的方式是启动同网络的诊断容器。

docker run --rm -it --network container:CONTAINER nicolaka/netshoot

使用诊断镜像前要确认来源可信,并与目标架构兼容。能够连接固定IP,却无法通过 getent hosts 得到地址,才支持DNS方向。连固定IP也失败,应先检查默认路由、NAT、宿主机转发和云端出站规则,不要继续把所有现象归到DNS。

HTTP服务还可以分别测试IP和域名:

curl -I --connect-timeout 5 http://93.184.216.34/
curl -I --connect-timeout 5 https://example.com/

HTTPS直接访问IP可能因为证书和SNI失败,因此只能用来辅助确认TCP连接,不能把证书错误当成网络不通。

查看容器实际使用的解析配置

先读取目标容器中的配置,不要只看宿主机:

docker exec CONTAINER cat /etc/resolv.conf
docker inspect CONTAINER --format '{{json .HostConfig.Dns}}'
docker inspect CONTAINER --format '{{json .NetworkSettings.Networks}}'

连接到用户自定义网络的容器通常会看到 nameserver 127.0.0.11,这是Docker内置DNS地址。它负责容器服务名解析,并把外部域名请求转发给上游。看到这个地址本身并不是错误,也不能把它替换成宿主机的 127.0.0.1

容器里的环回地址只指向容器自己。宿主机若通过systemd-resolved、dnsmasq或本地安全软件在 127.0.0.1127.0.0.53 上监听,把这个地址直接写给容器后,容器通常无法访问宿主机的监听进程。Docker会根据宿主机配置选择可用的上游,但历史配置、手工挂载和网络管理器变更仍可能造成异常。

还要检查 /etc/resolv.conf 是否被应用镜像、启动脚本或Compose卷手工覆盖:

docker inspect CONTAINER --format '{{json .Mounts}}'
docker inspect CONTAINER --format '{{json .HostConfig.Binds}}'

手工挂载解析文件会让容器绕过Docker正常生成逻辑。宿主机网络改变后,容器里仍保留旧地址,就可能出现重启宿主机后解析失败。

区分外部域名与容器服务名故障

Docker Compose创建的自定义网络会为服务名提供解析。先从应用容器查询同一网络中的服务:

docker exec APP getent hosts db
docker exec APP getent hosts redis
docker network inspect NETWORK_NAME

外部域名能解析,只有 dbredis 等服务名失败,重点检查两个容器是否真的连接到同一个用户自定义网络、服务名或网络别名是否正确,以及容器是否已被重新创建。默认 bridge 网络不会像用户自定义桥接网络那样自动提供完整的容器名称解析能力。

docker inspect APP --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
docker inspect DB --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'

两个结果没有共同网络时,不要通过 /etc/hosts 长期硬编码容器IP。容器重建后地址可能变化,更合理的修复是调整Compose网络和服务依赖。

内部服务名正常,只有公网域名失败,则Docker内置DNS本身还在工作,问题更可能发生在上游转发、宿主机解析器或53端口出站路径。

对比宿主机与容器的DNS结果

在宿主机执行同样查询:

getent ahosts example.com
resolvectl query example.com
cat /etc/resolv.conf

宿主机也解析失败,应先修宿主机的DNS上游、网络管理器或云平台DNS。只修Docker不会改变坏掉的上游。宿主机正常而容器失败,再查看Docker日志和网络命名空间的请求。

sudo journalctl -u docker --since '30 minutes ago' --no-pager
docker info

日志中出现DNS超时、上游不可达或网络创建失败时,记录完整时间和网络ID。Docker守护进程重启会影响所有容器网络,生产环境不要把它当成第一步。必须重启时先确认容器的重启策略、业务冗余和维护窗口。

使用systemd-resolved的宿主机可能同时存在 /etc/resolv.conf/run/systemd/resolve/stub-resolv.conf/run/systemd/resolve/resolv.conf。先用 readlink -f /etc/resolv.conf 确认实际指向,再判断Docker取得了哪组上游地址,不能只修改其中一个文件。

直接测试指定DNS服务器

dignslookup 时,可以分别查询容器当前DNS和已批准的上游:

dig @127.0.0.11 example.com A +time=2 +tries=1
dig @DNS_SERVER example.com A +time=2 +tries=1

第一个失败、第二个成功,说明请求到指定上游可用,Docker内置DNS或容器网络路径值得继续检查。两者都超时,则检查UDP和TCP 53端口是否被宿主机防火墙、云端规则或上游网络拦截。

sudo nft list ruleset
sudo iptables -S
sudo iptables -t nat -S

DNS通常使用UDP,但响应过大、DNSSEC和部分场景会回退TCP。只放行UDP 53可能产生小查询正常、大查询失败的隐蔽问题。排查时可对同一服务器分别测试UDP与TCP:

dig @DNS_SERVER example.com A +tcp +time=2 +tries=1

不要在未确认网络政策前随意改成公共DNS。企业内部域名、私有服务发现和合规过滤可能依赖指定解析器,外部DNS也可能在服务器所在地区不可达。

检查IPv4、IPv6与搜索域差异

有些应用报告解析失败,实际是拿到IPv6地址后连接失败。分别查看A与AAAA记录:

getent ahostsv4 example.com
getent ahostsv6 example.com

两类地址都能返回,应用却只在某个镜像里超时,要检查应用的地址选择顺序、容器IPv6配置和出口能力。不能为了隐藏IPv6链路问题直接删掉所有AAAA解析,尤其是同一镜像还部署在支持IPv6的环境中。

短名称解析还受到 searchoptions ndots 影响。容器先拼接多个搜索域再查公网域名,会增加延迟,错误的搜索域甚至会返回NXDOMAIN。读取完整 resolv.conf,用带结尾点的完整域名做对照:

getent hosts api.example.com
getent hosts api.example.com.

结果存在差异时,再核对Compose里的 dns_searchdns_opt 和公司内部域名设置。

用受控配置验证上游是否有问题

确认业务允许使用指定DNS后,可以为单个临时容器做对照:

docker run --rm --dns DNS_SERVER busybox nslookup example.com

单个容器指定DNS后恢复,说明问题与默认上游链路有关,但这仍不是最终修复。要继续查宿主机为何向Docker提供了不可用地址,以及其他容器是否依赖内部解析。

Compose可在服务级配置:

services:
  app:
    image: your-app:tag
    dns:
      - DNS_SERVER_1
      - DNS_SERVER_2

配置变更通常需要重新创建容器才能生效。执行前使用 docker compose config 检查最终合并结果,并确认环境变量没有把地址替换为空值。DNS服务器应使用实际通过审核的地址,上面的占位符不能原样投入生产。

当宿主机网络历史配置过多,难以判断是云端DNS还是Docker自身问题,可以在 萤光云 建立相同发行版和Docker版本的最小环境,或在按小时计费的 LightNode 上做外部解析对照。只复制脱敏后的Compose网络部分,不要直接导入生产密钥和完整数据卷。

修改后怎样验收

修复后要同时验证公网域名、内部服务名、A与AAAA查询,以及容器重建后的行为:

docker exec APP getent hosts example.com
docker exec APP getent hosts db
docker compose up -d --force-recreate APP
docker exec APP cat /etc/resolv.conf
docker exec APP getent hosts example.com

生产环境不能为了验收随意强制重建关键容器,应使用滚动实例或维护窗口。记录修改前后的查询耗时、失败率、容器网络名称和解析文件内容。应用有连接池或DNS缓存时,还要观察缓存过期后的结果,不能以一次成功查询作为最终结论。

完整的验收标准是宿主机与容器解析路径都能解释清楚,外部域名和内部服务名持续正常,并且容器重新创建后配置仍然有效。

常见问题

容器里的127.0.0.11是异常地址吗?

不是。用户自定义网络中的容器常通过Docker内置DNS地址解析服务名并转发外部查询。只有查询超时、响应异常或上游不可用时才需要继续排查。

修改宿主机resolv.conf后,旧容器会立刻更新吗?

不一定。容器创建方式、Docker版本和解析文件挂载方式都会影响结果。先读取容器实际配置,再在业务允许时重新创建单个实例验证。

直接给所有容器设置公共DNS可以吗?

不建议作为通用方案。它可能让内部域名失效,也可能绕开既有网络策略。先确认上游故障和业务依赖,再决定服务级或守护进程级配置。

赞(0)
未经允许不得转载;国外VPS测评网 » Docker容器能访问IP却不能解析域名怎么办?完整排查教程
分享到