📄articles 📋snippets 📁categories ⚙️uses ✏️about 🔍search

Docker 网络排查实录:bridge / 自定义网络 / DNS 隔离

三个容器,一个 docker-compose.yml——docker compose ps 全绿,docker compose logs 没报错。但浏览器打开就是 502,应用日志里 Redis 连接超时。这是一次真实的 Docker 网络排查过程,从症状到根因,每一步都有命令和输出。

Docker 网络排查的核心难点不在命令本身,而在于理解 Compose 网络的隔离机制:默认 bridge、自定义 network、DNS 解析范围,这三者的边界在哪。这篇文章把一个同时踩了两个坑的案例拆开,演示 Docker 网络排查的完整思路。

场景:一个 WordPress 开发环境

本地搭了一套 WordPress + Redis + Nginx 的开发环境。也正是在这个环境下,Docker 网络排查的典型问题暴露了出来:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf

  wordpress:
    image: wordpress:php8.3-fpm
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_REDIS_HOST: redis
    volumes:
      - ./wp-content:/var/www/html/wp-content

  db:
    image: mariadb:11

  redis:
    image: redis:7-alpine

docker compose up -d 之后:

$ docker compose ps
NAME                STATUS
app-db-1            Up 2 minutes
app-nginx-1         Up 2 minutes
app-redis-1         Up 2 minutes
app-wordpress-1     Up 2 minutes

四个容器全绿。然后 curl 一下:

$ curl -sI http://localhost
HTTP/1.1 502 Bad Gateway

502。Nginx 连不上 PHP-FPM。Docker 网络排查正式开始。

Docker 网络排查第一层:Nginx → WordPress

先看 Nginx 配置里 upstream 指向哪:

upstream php {
    server wordpress:9000;
}

用的是服务名 wordpress:9000。进去 Nginx 容器验证 DNS 解析——这是 Docker 网络排查的核心步骤:

$ docker compose exec nginx nslookup wordpress
Server:         127.0.0.11
Address:        127.0.0.11#53

Name:   wordpress
Address: 172.20.0.4

解析正常。再直接连 9000 端口:

$ docker compose exec nginx nc -zv wordpress 9000
wordpress (172.20.0.4:9000) open

端口也是通的。那问题不在 Nginx 到 WordPress 这条链路上。

回头看 WordPress 自己的日志,继续 Docker 网络排查:

$ docker compose logs wordpress | tail -5
WordPress 6.6.2  PHP 8.3.10
MySQL connected successfully
Redis: Connection timed out [tcp://redis:6379]

MySQL 正常,Redis 超时。WordPress 连不上 Redis。之前做 Redis Object Cache 配置时也遇到过类似现象,但那次是配置问题,这次是网络层面的。

Docker 网络排查第二层:WordPress → Redis

进 WordPress 容器测试 Redis 连通性:

$ docker compose exec wordpress sh
# nslookup redis
Server:  127.0.0.11
Address: 127.0.0.11#53
** server can't find redis: NXDOMAIN

# ping redis
ping: bad address 'redis'

redis 这个主机名根本解析不了。但 docker compose ps 里 redis 容器明明是 Up 的。这就是 Docker 网络排查的关键转折——DNS 不通,但容器在跑:

# ip addr show eth0
3: eth0@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
    inet 172.20.0.3/16 brd 172.20.255.255

WordPress 在 172.20.0.0/16 网段。Redis 在同一个 docker inspect 输出里显示 app_default 网络——但 DNS 为什么解析不到?

重新检查 docker-compose.yml——前面贴的配置漏了一行,而这正是 Docker 网络排查的核心发现:

  redis:
    image: redis:7-alpine
    networks:
      - cache          # <-- 独立网络

Redis 被手动指定到了 cache 网络。翻到文件底部的 networks 声明:

networks:
  default:
    driver: bridge
  cache:
    driver: bridge

Docker Compose 创建了两个隔离的 bridge 网络:app_default 和 app_cache。WordPress 在 app_default,Redis 在 app_cache——不同网络,DNS 互不可见。

这是 Docker 网络排查的第一个结论:Compose 里每个 named network 都是隔离的 bridge,容器只能解析同一网络内其他容器的服务名。这个行为在 Docker Compose 网络官方文档里有详细说明。

验证一下,分别检查两个网络中的容器列表:

$ docker network inspect app_default | python3 -c "
import sys,json
net=json.load(sys.stdin)
containers=[c['Name'] for c in net[0]['Containers'].values()]
print(containers)
"
['app-nginx-1', 'app-wordpress-1', 'app-db-1']

$ docker network inspect app_cache | python3 -c "
import sys,json
net=json.load(sys.stdin)
containers=[c['Name'] for c in net[0]['Containers'].values()]
print(containers)
"
['app-redis-1']

确认了:Redis 独自在 app_cache,其他三个在 app_default。Docker 网络排查定位到了根因。

修复:Docker 网络排查的两种解法

方案 A:把 Redis 也加到 default 网络

把 Redis 的 networks 改成同时加入两个网络:

  redis:
    image: redis:7-alpine
    networks:
      - default
      - cache

重建后验证 DNS——这是 Docker 网络排查的验证习惯,每次改完都测一遍:

$ docker compose down && docker compose up -d
$ docker compose exec wordpress nslookup redis
Name:   redis
Address: 172.20.0.5

$ docker compose exec wordpress nc -zv redis 6379
redis (172.20.0.5:6379) open

方案 B:全部放在同一个网络,删掉 cache

Redis 不需要独立网络的话,直接删 networks 声明:

  redis:
    image: redis:7-alpine
    # 删掉 networks 声明

同时删掉 bottom 的 cache 网络声明。这样所有服务都在 app_default 里,DNS 全互通。选方案 B——更简单,后续做 Docker 网络排查也不用考虑多网络场景。

Docker 网络排查第三层:Nginx → WordPress(Round 2)

Redis 通了,但 curl 还是 502:

$ curl -sI http://localhost
HTTP/1.1 502 Bad Gateway

不过这次 Redis 日志正常了——Docker 网络排查的一个收获是排除了一个变量:

$ docker compose logs wordpress | grep Redis
Redis connected successfully

那 502 还是 Nginx → WordPress 的问题。前面测过 TCP 端口通,但 Nginx 找的不是端口——是文件。看 fastcgi 配置:

$ cat nginx.conf | grep -A5 fastcgi_pass
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

SCRIPT_FILENAME 指向 $document_root。而这个 root 在 Nginx 容器内部:

$ docker compose exec nginx grep root /etc/nginx/nginx.conf
root /var/www/html;

$ docker compose exec nginx ls /var/www/html
ls: /var/www/html: No such file or directory

Nginx 容器里根本没有 /var/www/html——网站文件在 WordPress 容器里。这个问题在之前的 Nginx 反向代理排坑中也遇到过类似的路径认知偏差。

修复:Nginx 里删掉 root,SCRIPT_FILENAME 直接用 FPM 容器内路径:

location ~ .php$ {
    fastcgi_pass wordpress:9000;
    fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
    include fastcgi_params;
}
$ docker compose restart nginx
$ curl -sI http://localhost
HTTP/1.1 200 OK
X-Powered-By: PHP/8.3.10

200。这次 Docker 网络排查的两个问题全部解决。

Docker 网络排查总结

这个案例里有两个独立问题,表现相同(都是 502),但根因完全不同:

问题 现象 根因 Docker 网络排查命令
Redis 不可达 Connection timed out 独立 network 导致 DNS 隔离 nslookup redis
Nginx 502 curl 返回 502 Nginx root 指向不存在路径 ls /var/www/html

总结 Docker 网络排查的四条核心规则:

  • 不声明 networks,所有服务自动加入 {项目名}_default bridge 网络,DNS 互通
  • 声明了自定义 network,容器只加入声明的网络——没加的网络里 DNS 不可见
  • docker compose exec 容器名 nslookup 服务名——Docker 网络排查第一步,区分 DNS 失败和端口不通
  • nc -zv 服务名 端口——Docker 网络排查第二步,确认 TCP 层可达后再看应用层

另外一条教训:Nginx + PHP-FPM 分离部署时,Nginx 容器里不需要有网站文件。fastcgi_pass 把请求转发给 PHP-FPM 容器,SCRIPT_FILENAME 用 FPM 容器内的路径,不要写 Nginx 本地的路径。

© 2026 Docker 网络排查实录:bridge / 自定义网络 / DNS 隔离 · 本文由 Charlie 原创撰写,发布于 sodebug.com。 未经授权禁止转载、洗稿、机器抓取。AI 训练数据使用需获得书面授权。
GK
独立开发者,在 WordPress、Nginx 和各种 API 之间切换。不写没用的东西。

related相关文章

#01Docker 入门:安装、容器、Compose 一条龙2 min
$ echo "less bullshit, more debugging" | send-to-inbox
新文章直接发到邮箱,一个月 2-4 封。
$ subscribe →