• Docker
  • 网络
  • 运维

Docker 网络排障:从容器状态到 DNS 的排查顺序

容器间解析失败、端口不通、nginx 502,三类问题占了大半。按网络、DNS、监听地址、发布方式的顺序排查,比反复重启有效。

容器网络问题的现象很相似:解析不到、连不上、502。但原因可能分布在容器状态、网络、DNS、监听地址、发布方式五层。按顺序排查,比反复重启容器有效得多。

先画一条最短路径:客户端 → 宿主机端口 → 容器端口 → 进程监听。任何一段断了,现象都可能是 connection refused 或 timeout,但处理方式完全不同。下面按这条路径从外到内排查。

一、先确认容器真的在跑

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker inspect -f '{{.State.Status}} restarts={{.RestartCount}}' app
docker logs --tail 100 app

“Up”不等于“可用”:进程可能启动后立刻退出,又被 restart 策略拉起来。RestartCount 持续增长、日志里有 panic 或端口绑定失败,都说明问题在应用本身,不在网络。

二、是不是同一个网络

Compose 会为每个项目创建默认网络,服务名只有在同一网络里才能解析。跨项目、或者手动 docker run 起的容器,很容易落在不同网络:

docker network ls
docker inspect -f '{{json .NetworkSettings.Networks}}' app
docker network inspect app_default

不在同一网络时,两个选择:把容器接入同一网络,或者用 extra_hosts 明确写死地址。后者只适合临时排查,长期还是靠网络别名。

services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
  db:
    image: mysql:8.4
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
      interval: 5s
      retries: 10

depends_on 的 condition 只保证启动顺序,不保证业务可用;真正的就绪判断要放在应用启动逻辑或入口脚本里。

三、DNS 解析

Docker 内置 DNS 是 127.0.0.11,容器内解析服务名走它。进入容器验证:

docker exec -it app getent hosts db
docker exec -it app cat /etc/resolv.conf
docker exec -it app nslookup db 127.0.0.11

如果 getent 失败但 IP 能通,问题就在解析:服务名拼错、容器不在同一网络,或者用了 container_name 却期待服务别名。反过来,如果解析成功但连接被拒,继续看下一层。

四、监听地址与端口

容器里的服务如果监听 127.0.0.1:8080,从其他容器和宿主机都连不上,必须监听 0.0.0.0。检查:

docker exec -it app ss -lntp
docker port app
curl -v http://127.0.0.1:8080/health

expose 只是声明,不会发布到宿主机;ports 才会做端口映射。映射写成 127.0.0.1:8080:8080 时只绑定本机回环,公网访问不到,这是有意的安全设置,不要为了“能访问”改成 0.0.0.0。

五、nginx 的 upstream 缓存

容器重建后 IP 会变,而 nginx 默认在启动时解析一次 upstream 域名,之后一直用旧 IP,表现就是 502。两种处理:

  • 让 nginx 通过 Docker DNS 动态解析:配置 resolver 127.0.0.11 valid=10s;,proxy_pass 里使用变量;
  • 或者在容器重建后 reload nginx:docker exec nginx nginx -s reload。

前者更适合容器频繁重建的环境,后者更简单。关键是知道“改完 compose 要 reload”,而不是怀疑应用挂了。

六、宿主机与 MTU

能 ping 通、小请求正常、大文件卡住,典型是 MTU 问题。先确认宿主机到对端的路径 MTU,再决定是否在 compose 里调小:

ping -M do -s 1472 10.0.0.1
docker network inspect app_default -f '{{json .Options}}'

云环境、VPN、overlay 网络都可能让实际 MTU 小于 1500。把 mtu: 1450 写进 compose 的 network 配置,比在应用层反复重试更直接。

七、host.docker.internal 不是默认就有

Linux 上容器访问宿主机,需要显式添加:

extra_hosts:
  - "host.docker.internal:host-gateway"

macOS 和 Windows 的 Docker Desktop 自带这个域名,Linux 没有,直接写会解析失败。这是跨平台 compose 文件最常见的差异之一。

排查时按“容器状态 → 网络 → DNS → 监听地址 → 发布方式 → 代理缓存”的顺序走,每一步都用命令验证,不要同时改三个地方。另外,network_mode: host 的容器没有独立网络命名空间,服务名解析和端口映射都不适用;排查前先确认 compose 文件里没有这一行,否则前面几步的结论都会失真。

本文基于公开文档与本地实验整理,示例命令请在测试环境验证后再用于生产。

  • Docker
  • 网络
  • 运维