Docker容器内无法解析域名
应用提示“无法解析主机名”时,先确认失败的是公网域名、私网域名,还是另一个容器的服务名。这三类名称走的路径可能不同,直接换成公共 DNS,反而可能让私网数据库失联。本文适用于 Linux Docker Engine 的普通桥接网络容器。
先定位:宿主机和容器分别失败在哪里
下面命令都在宿主机终端执行;docker exec 后面的程序才在容器内运行。app、db 是示例容器名,请替换为实际名称;example.com 是示例公网域名,业务排查还应测试实际失败的域名。
宿主机已有 getent 时,先检查宿主机名称解析,再检查容器:
getent hosts example.com
docker exec app getent hosts example.com
getent 检查系统名称解析,也可能使用 hosts 文件,并非只查询 DNS。如果已有 nslookup 或 dig,可用同一域名补充 DNS 查询。容器提示“executable file not found”只说明镜像缺少这个程序;精简镜像也可能没有 shell、cat 或 curl,不能据此认定 DNS 故障,更不必临时往业务容器里安装整套工具。
| 观察结果 | 优先检查 |
|---|---|
| 宿主机与容器都失败 | 域名是否写错、记录是否存在,以及宿主机所用 DNS 是否正常。 |
| 宿主机成功,容器失败 | 容器实际 DNS 配置,以及容器网络到 DNS 的访问路径。 |
| 公网域名成功,私网域名失败 | 是否错误改用了不了解私网记录的解析服务器。 |
| 域名成功,只有服务名失败 | 服务名、网络归属及网络别名,而非先改公网 DNS。 |
看到 127.0.0.11,不要急着改
容器有 cat 时,从宿主机读取其解析配置;第二条只查看容器显式设置的 DNS 地址,不输出完整容器配置:
docker exec app cat /etc/resolv.conf
docker inspect -f '{{json .HostConfig.Dns}}' app
用户自定义网络通常使用 Docker 内置 DNS,地址为 127.0.0.11。它负责同一网络内的名称解析,并把外部查询转发给上游;这个地址本身不是配置错误。默认 bridge 网络与用户自定义网络的解析行为不同,不能套用同一个判断。输出 [] 只说明未给该容器单独指定 DNS,不代表没有解析服务器。
普通隔离容器中的 127.0.0.1 指向容器自身。把宿主机回环地址上的 DNS 原样填进容器,通常访问不到宿主机的解析服务。应确认容器实际能访问的解析地址;需要私网解析时,保留对应私网 DNS,不要照抄其他环境的公共地址。
只有 db 解析失败:核对是否在同一网络
以下两条仍在宿主机执行,分别列出两个实际容器加入的网络:
docker inspect -f '{{range $name, $v := .NetworkSettings.Networks}}{{println $name}}{{end}}' app
docker inspect -f '{{range $name, $v := .NetworkSettings.Networks}}{{println $name}}{{end}}' db
Compose 默认让同一项目中的服务加入项目网络,并通过服务名发现彼此。这里用于查询的 db 应是实际服务名或该网络上的别名;它不一定等于生成出来的容器名。不同项目通常有不同的默认网络,两个服务没有共同网络时,不能期待直接解析对方的服务名。默认 bridge 网络也不提供相同的自动名称发现能力。
确认网络后,若 app 内已有 getent,再从宿主机执行:
docker exec app getent hosts db
修复后,按原路径复查
把确认过的 DNS、网络或别名修正写回实际部署配置,避免只改运行中的容器而在下次部署时丢失。涉及 Compose 配置变化时,沿用原项目名与配置文件,在维护窗口重建受影响的单个服务。先检查数据挂载:已挂载卷可保留,容器可写层中的文件不能靠重建保留。
复查同一个域名或服务名,再验证业务连接。名称能解析仅说明得到了地址,不代表端口、认证或应用健康正常。排查到这里仍无法确定时,保留错误信息、容器网络名及解析结果即可;不需要先重启整台服务器、Docker 服务或删除整个网络。