Docker容器DNS解析异常排查:resolv.conf与自定义网络

    发布时间:2026-09-19 04:31 更新时间:2026-09-19 04:31 阅读量:0

    容器跑得好好的,突然某个服务连不上数据库域名,或者进容器执行 apt update 报 Temporary failure resolving,第一反应往往是网络不通。但很多时候链路是通的,问题出在 DNS:容器拿到的 /etc/resolv.conf 里写着一个不可达的地址,或者压根没写。本文按「先看容器里是什么 → 再查这个文件从哪来 → 最后改对地方」的顺序走一遍,涉及的命令都在真实环境验证过思路,具体输出以你的环境和官方文档为准。

    一、先看容器里的 /etc/resolv.conf 到底长什么样

    排查 DNS 永远从现象开始,不要一上来就改配置。进到出问题的容器里,直接读它实际使用的解析文件:

    docker exec -it myapp cat /etc/resolv.conf
    docker exec -it myapp getent hosts example.com
    docker exec -it myapp nslookup example.com 127.0.0.11
    

    这里有两个关键信息点。第一,nameserver 指向谁。如果指向 127.0.0.11,说明容器接在用户自定义网络(user-defined network)上,这个地址是 Docker 内嵌的 DNS 解析器,负责把容器名、服务名解析成 IP,同时把外部域名转发给宿主机上的上游 DNS。如果指向的是宿主机 /etc/resolv.conf 里的真实 DNS 地址(比如内网 DNS 或 114 之类),说明容器跑在默认的 bridge 网络上,此时容器之间无法通过名字互相解析。

    第二,search 和 options 行是否被截断。宿主机 resolv.conf 里如果有很多 search 域,Docker 只会复制其中一部分,默认最多 3 个 search 域、3 个 nameserver,这是 glibc 的历史限制。如果你依赖某个内网短域名,而它恰好被截掉了,解析自然失败。

    用 getent hosts 和 nslookup 分别验证,能区分「文件内容看着对但实际解析不了」和「文件内容本身就错」。注意很多精简镜像里没有 nslookup,可以临时用 getent,或者装 bind-utils/dnsutils 后重试。

    二、容器 resolv.conf 的来源与优先级

    Docker 决定容器 DNS 配置的顺序大致是这样的,优先级从高到低:

    第一,docker run 时通过 --dns、--dns-search、--dns-opt 显式指定的参数,会直接写入容器。第二,如果没指定,Docker 会读取守护进程配置 /etc/docker/daemon.json 里的 dns 字段。第三,如果 daemon.json 也没配,Docker 默认把宿主机的 /etc/resolv.conf 复制进容器,但会过滤掉 127.0.0.1 这类回环地址,因为容器里的 127.0.0.1 指的是容器自己,拿它当 DNS 必然失败。

    而一旦容器接入的是自定义网络,127.0.0.11 会出现在最前面,Docker 内嵌解析器接管所有查询:容器名、网络别名走本地解析,其他域名转发给上游。这就是为什么同一个镜像,在默认 bridge 网络里解析不了别的容器名,换到自定义网络就好了。

    要在全局层面固定 DNS,可以编辑 daemon.json,注意它是 JSON 格式,不能写注释,改完需要重启 Docker 守护进程,已运行的容器不会自动生效,需要重建:

    # /etc/docker/daemon.json
    {
      "dns": ["10.0.0.2", "223.5.5.5"],
      "dns-search": ["corp.internal"],
      "dns-opts": ["ndots:1", "timeout:2"]
    }
    
    sudo systemctl restart docker
    docker run --rm --dns 10.0.0.2 alpine cat /etc/resolv.conf
    

    这里有个容易踩的坑:ndots 默认是 1,意味着只有名字里点数大于等于 1 才先当绝对域名查。内网如果大量使用短名(比如 db、cache),解析会先拼接 search 域再查询,慢且容易失败。把 ndots 调小能减少一次无谓查询,但具体取值要结合你的域名结构决定,别盲目照抄。

    另外,如果你在容器里手动改了 /etc/resolv.conf,重启容器就丢,因为该文件是 Docker 在容器启动时生成并挂载进去的。要持久化,正确做法是走 --dns 参数、daemon.json 或者编排文件的 dns 字段,而不是改文件。

    三、自定义网络与编排场景下的配置

    生产环境更常见的是用 Compose 或自定义 bridge 网络。在 Compose 文件里,DNS 可以写在服务级别,也可以放在网络级别。服务级别写在 dns 字段,等价于 --dns;如果想让同一网络内所有容器共享一套解析策略,可以在顶层 networks 里定义并让服务引用。

    # docker-compose.yml 片段
    services:
      web:
        image: nginx:stable
        dns:
    
    • 10.0.0.2
    dns_search:
    • corp.internal
    networks:
    • backend
    networks: backend: driver: bridge

    排查自定义网络下的解析问题时,先确认容器确实连在这个网络上:docker network inspect backend 看 Containers 列表,再用 docker inspect --format '{{json .NetworkSettings.Networks}}' 容器名 看它实际挂了哪些网络。一个容器可以同时接多个网络,但默认只会在第一个网络上注册别名,跨网络解析同一个容器名可能拿不到结果,这点在多网络编排里要特别留意。

    还有一种情况是宿主机能解析、容器不能。此时对比两边:宿主机 resolvectl status 或 cat /etc/resolv.conf 看到的是 systemd-resolved 监听在 127.0.0.53,这个地址被 Docker 过滤掉了,容器拿不到有效上游,于是解析失败。解决办法是给容器显式指定真实 DNS,或者把 daemon.json 的 dns 指向内网递归服务器,具体以你的网络架构和官方文档为准。

    最后补充一个排查顺序:先在容器里 getent hosts 确认解析失败 → 看 /etc/resolv.conf 的 nameserver 是否可达 → 用 docker exec 进去 ping 或 nc -z -u 测 DNS 的 53 端口 → 对比宿主机解析结果 → 检查 daemon.json、--dns 与网络类型。多数问题卡在前两步就能定位。

    小结一下:容器 DNS 不是玄学,它有一份明确的来源优先级,容器里那份 resolv.conf 只是结果。养成「先读容器内文件、再追溯来源、最后改对层级」的习惯,比反复重启容器有效得多。如果你用了 Kubernetes,Pod 的 DNS 由 CoreDNS 接管,配置位置换成 dnsPolicy 和 dnsConfig,思路相通但字段不同,需要单独对照官方文档处理。

    继续阅读

    📑 📅
    MySQL连接被拒绝Connection refused逐层排查思路 2026-09-19
    systemd-journald 日志转发远程 syslog:rsyslog 对接与丢日志排查 2026-09-19
    Nginx 静态资源 404 与权限被拒排查:root、alias、try_files 的坑 2026-09-18
    Linux网卡丢包与TCP重传排查:ip -s link、ss -ti与ethtool实战 2026-09-18
    journald 日志占满 /var/log:持久化与容量限制配置 2026-09-18
    PHP-FPM 进程数怎么调:pm.max_children 与内存估算 2026-09-19
    Nginx gzip 与 Brotli 压缩配置实战:静态资源体积优化 2026-09-19
    Docker 容器内 CPU 被限流排查:cfs_quota、cpuset 与 top 显示异常 2026-09-19
    Nginx 与后端长连接调优:keepalive 与 upstream 复用 2026-09-20
    Docker 容器日志写满磁盘:json-file 限制与 max-size 配置 2026-09-20