
容器里偶尔解析不到数据库服务名,重启容器后又恢复;宿主机 curl 正常,容器访问外部域名却间歇性超时。Docker DNS 问题常被归结为“改 /etc/resolv.conf”,但这个文件在容器里可能只是运行时生成结果,重建或网络变更后会被重新写入。
排查要先区分三条路径:容器访问同一 Docker 网络里的服务名,容器访问宿主机或外部 DNS,宿主机访问容器。它们经过的网络和解析器不同。Docker Engine 的用户自定义网络通常提供内置 DNS,服务名解析也依赖容器是否加入同一个网络。[1][2]
本文以常见 Linux Docker Engine、bridge 网络和 Compose 项目为例。rootless、Docker Desktop、Kubernetes、VPN 与企业 DNS 的行为可能不同;命令中的服务名、网络名和域名都要替换成自己的值。
先判断是解析失败还是连接失败
先在出问题的容器里分别测试服务名、公共域名和 IP。容器可能没有安装 dig,可以使用镜像已有的 getent 或临时使用诊断容器。不要把“域名解析成功”当成“服务端口可用”。
docker exec app getent hosts db
docker exec app getent hosts example.com
docker exec app sh -c 'cat /etc/resolv.conf'
docker exec app sh -c 'nc -vz -w 3 db 5432'
db 是同一 Docker 网络中的服务名,5432 是示例端口。结果可以这样分组:
getent hosts db失败,优先看网络成员、别名和 Docker 内置 DNS。- 服务名能解析到 IP,但端口连接失败,转向监听地址、防火墙、网络隔离或服务健康状态。
- 服务名正常,外部域名失败,重点看容器的 DNS 配置、宿主机转发、VPN 和上游解析器。
- IP 能连、域名不稳定,DNS 路径仍有问题,应用层重试不能代替修复解析。
同时记录容器 ID、网络名、失败时间和解析结果。只在宿主机运行 getent,无法证明容器的解析路径正常。
自定义网络中的服务名解析
Compose 默认会为项目创建网络,服务通常可以使用服务名访问同一网络中的其他服务。例如:
services:
app:
image: example/app:latest
networks: [backend]
depends_on:
- db
db:
image: postgres:16
networks: [backend]
networks:
backend:
在这个示例中,应用应连接 db:5432,而不是写死容器 IP。Docker 官方文档说明,用户自定义 bridge 网络提供容器间 DNS 解析;同一网络上的容器可以通过名称或别名找到彼此。[2]
如果 app 和 db 不在同一网络,服务名不会因为它们属于同一个 Compose 文件就自动跨网络可用。检查实际成员:
docker network ls
docker network inspect project_backend
docker inspect app --format '{{json .NetworkSettings.Networks}}'
docker inspect db --format '{{json .NetworkSettings.Networks}}'
网络名称可能带有项目名前缀,不能只凭 Compose 文件里的 backend 判断。若使用外部网络,网络必须事先存在,且不同项目共享同一网络时要考虑服务名冲突和信任边界。
Compose 的 aliases 可以给服务添加网络别名,但别名在网络范围内生效;同一网络上多个容器使用相同别名时,解析结果可能对应多个地址。应用是否支持多地址和重试,应按实际客户端处理。
/etc/resolv.conf 里出现 127.0.0.11 是什么
在 Docker 自定义网络中,容器的 /etc/resolv.conf 常会显示 Docker 内置 DNS 地址 127.0.0.11。这是容器网络命名空间内的本地解析入口,不应直接把它理解成宿主机回环地址,也不要把宿主机的 127.0.0.1 复制到容器里作为 DNS。
查看配置:
docker exec app cat /etc/resolv.conf
docker inspect app --format '{{json .HostConfig.Dns}}'
docker inspect app --format '{{json .HostConfig.DnsSearch}}'
docker inspect app --format '{{json .HostConfig.DnsOptions}}'
Docker 内置 DNS 会处理容器网络中的名称,并将外部查询转发到宿主机配置的上游 DNS。[1] 宿主机的 /etc/resolv.conf 若指向只在主机或 VPN 内可达的地址,容器转发外部查询时可能失败。
需要注意 systemd-resolved 的本地 stub 地址 127.0.0.53。它在宿主机上能工作,不代表容器网络能直接访问同一个回环地址。Docker 文档提到,Docker 会处理宿主机本地回环解析器无法被容器直接使用的场景,但自定义 DNS、VPN 和防火墙配置仍可能改变结果。[1]
不要只改正在运行容器里的 /etc/resolv.conf 后就结束排查:它可能在重建容器后恢复,也可能只改变一次测试结果。持久配置应通过 Docker daemon 的 dns、Compose 服务的 dns,或修复宿主机可供容器访问的上游解析器来完成。
外部域名失败时检查上游 DNS
先从宿主机查看当前解析器,再对比容器结果:
cat /etc/resolv.conf
getent hosts registry-1.docker.io
docker exec app cat /etc/resolv.conf
docker exec app getent hosts registry-1.docker.io
如果系统安装了 dig,可以明确指定解析器进行对照;这类工具可能需要安装,下面不假定生产容器一定自带:
dig +time=2 +tries=1 example.com
dig +time=2 +tries=1 @1.1.1.1 example.com
公共 DNS 地址只是诊断示例,不适合所有内网、合规或离线环境。企业域名通常必须使用内部解析器;直接替换成公共 DNS 可能让内部服务、分流策略和审计失效。
检查:
- 上游 DNS 是否只监听宿主机回环地址;
- 服务器到 DNS 的 UDP/TCP 53 是否被防火墙、云安全组或 VPN 拦截;
- DNS 响应是否过大,需要 TCP 回退;
- 容器是否继承了错误的
search域,导致查询延迟或尝试多个后缀; - IPv4、IPv6 的解析结果是否都可达;
- DNS 服务是否有速率限制或短时故障。
修复 DNS 前不要贸然把 /etc/resolv.conf 固定成一个公网地址。先确认业务需要的内部域名、出口策略和可用性,再决定 Docker 或 Compose 层的配置。
Compose 中配置 DNS 的边界
若确实需要为某个服务指定解析器,可在 Compose 中配置:
services:
app:
image: example/app:latest
dns:
- 192.0.2.53
dns_search:
- svc.example.internal
dns_opt:
- timeout:2
- attempts:2
192.0.2.53 是文档示例保留地址,不可直接用于生产。换成企业实际 DNS,并确认容器网络可以到达该地址。dns_search 会影响不带点号的短名称查询,写错搜索域可能造成额外等待;不需要时可以省略。
修改 Compose 后要重新创建相关容器,单纯 docker restart app 不一定应用新的创建参数:
docker compose config --quiet
docker compose up -d --force-recreate app
docker exec app cat /etc/resolv.conf
docker exec app getent hosts db
docker compose config 的完整输出可能展开环境变量和敏感配置,分享前先脱敏。--force-recreate 可能造成短暂中断,生产环境应安排窗口并确认应用连接池、数据卷和回滚文件。不要为了改 DNS 执行 docker compose down -v。
如果多个服务都需要同一解析器,也可以在 Docker daemon 层配置默认 DNS,但这会影响更多容器。修改 daemon 配置前备份文件,确认 JSON 格式,并评估重启 Docker 对运行中业务的影响;全局改动不应作为单个服务故障的第一步。
DNS 正常,服务发现仍可能“时好时坏”
名称能解析到多个 IP 时,客户端可能轮询或缓存结果。旧容器、滚动更新、重复网络别名和应用自身的 DNS 缓存都可能造成观感上的不稳定。检查解析结果是否随着容器更新变化,并确认客户端会在连接失败后重新解析,而非永久缓存旧地址。
容器启动顺序也容易被误解。Compose 的 depends_on 主要表达启动依赖,不等价于数据库已经完成初始化并可以接受业务请求。DNS 已经返回 db 的地址,数据库仍可能在启动中。应配合健康检查、应用重试和明确的连接超时处理。
不要在配置里长期写死动态容器 IP。若必须固定地址,先确认网络规划和重建策略,并记录维护成本;通常服务名更适合 Compose 内部调用。
从日志和抓包确认故障位置
应用日志中应记录解析失败、连接目标、错误类型和重试次数,但不要记录密码、Cookie 或完整连接字符串。Docker daemon、宿主机 resolver 和 DNS 服务日志可以按同一时间窗口对照。
若权限和环境允许,可以在受控时间短暂抓取 DNS 流量:
sudo tcpdump -ni any 'port 53'
抓包可能包含内部域名,文件不要随意外传。观察请求是否从容器经过宿主机发出、响应是否返回、是否只出现 UDP 而没有 TCP 回退。完成定位后停止抓包,避免长期收集敏感网络信息。
一个可复查的最小记录包括:容器及网络 ID、/etc/resolv.conf、docker network inspect 摘要、服务名解析结果、外部域名解析结果、目标端口测试和故障时间。只保存必要字段,避免把整份环境变量和配置打包上传。
验证修复不能只做一次 getent
配置调整后至少做四组测试:
- 连续解析同一 Docker 服务名,确认网络内发现稳定。
- 连续解析一个外部域名,观察超时、错误类型和 IPv4/IPv6 结果。
- 解析成功后连接实际服务端口,区分 DNS 与 TCP 问题。
- 重建容器或重启 Docker 后再次检查,确认修复没有依赖手工改文件。
如果服务有健康检查和连接池,还要让应用完成一次真实的连接、断开和重连。不要在生产环境制造高频 DNS 查询或重启所有容器来“验证稳定性”。
最终留下网络拓扑、上游 DNS、服务名和变更回滚方式。下次新增服务时先确认网络成员,再决定是否配置额外 DNS;把两个不同问题都归因于 DNS,反而会让排查绕远。
参考资料
资料核验日期:2026 年 11 月 9 日。示例地址、服务名和端口均需替换为实际环境。




