首页 > Technology > 正文

Docker Nginx 502 怎么排查

fenij 2026-08-25 28 Technology

Docker 里 Nginx 反代后端应用时,502 Bad Gateway 是最常见的报错之一。它只说明一件事:Nginx 连不上上游(upstream)。排查思路很固定:先看上游活没活,再看网络和配置。很多新手会先去改 Nginx 配置,其实第一步应该是确认应用容器真的在监听正确的地址和端口。

下面这套流程按「从外到内」的顺序排查:先确认容器状态,再确认网络连通性,最后看应用监听地址和 Nginx 配置。按部就班列下来,基本不会漏。

502 常见根因速查

根因 典型日志 占比
上游容器未运行 connect() failed (111: Connection refused) 35%
应用绑定 127.0.0.1 upstream prematurely closed connection 25%
不在同一 Docker 网络 no resolver defined to resolve app 20%
上游端口/路径写错 upstream port mismatch 15%
上游 OOM / 崩溃 exit code 137 / OOMKilled 5%

四步定位

1. 确认上游容器在跑

docker ps --filter name=app
# 如果状态是 Exited,看日志
docker logs app --tail 50

2. 从 Nginx 容器内部测连通性

docker exec nginx getent hosts app
docker exec nginx curl -v http://app:3000/health

如果 getent hosts 解析不到,说明两个容器不在同一网络,或者服务名写错。

3. 检查应用监听地址

很多新手写 app.run(host='127.0.0.1', port=3000),这在容器里只能被自己访问。Nginx 从外部连过来必须改成:

app.run(host='0.0.0.0', port=3000)

4. 检查 Nginx 配置语法

docker exec nginx nginx -t
docker exec nginx nginx -T | grep -A5 proxy_pass

一个能跑的 docker-compose 模板

services:
  app:
    build: ./app
    expose:
      - "3000"
    environment:
      - HOST=0.0.0.0
    networks:
      - backend
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app
    networks:
      - backend
networks:
  backend:

对应 Nginx 配置:

upstream app {
    server app:3000;
}
server {
    listen 80;
    location / {
        proxy_pass http://app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

宿主机防火墙与安全组

如果 Nginx 和 app 在同一 Docker 网络里能互相访问,但浏览器还是 502,要检查外层:

  • Linux 主机的 ufw / firewalld 是否放行了 80/443
  • 云服务器安全组是否允许入站 HTTP/HTTPS
  • SELinux 是否阻止了容器间通信(可临时设为 Permissive 测试)
# 快速检查端口是否监听在宿主机上
ss -tlnp | grep :80
# 检查 ufw 状态
sudo ufw status
# 检查 firewalld
sudo firewall-cmd --list-all

docker-compose 里用 ports: 把容器端口映射到宿主机后,外部流量先到宿主机防火墙,再到 Docker 的 iptables 规则,最后才进容器。

Nginx 超时参数调优

如果上游响应慢,Nginx 默认 60 秒超时可能不够,会报 504 而不是 502。但在调试阶段,先把超时放大可以避免误判:

location / {
    proxy_pass http://app;
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 120s;
    proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
}

生产环境别盲目放大,应该根据应用真实 P99 latency 来设。调试完后,记得把 proxy_read_timeout 调回合理值。

网络排查命令速查

这几个命令组合起来用,能快速定位网络层问题:

# 查看容器所在网络
docker network ls
docker inspect backend --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{end}}'

# 从 Nginx 容器解析服务名
docker exec nginx nslookup app

# 查看 Nginx 实际生效配置
docker exec nginx nginx -T

# 实时跟踪 error log
docker exec nginx tail -f /var/log/nginx/error.log

如果 nslookup app 返回 NXDOMAIN,说明服务名没加入同一网络,或者 compose 里的网络名称不一致。

踩坑实录

有次我配了一个 Flask 容器,docker ps 显示 Up,Nginx 还是 502。进 Nginx 容器 curl http://app:5000 不通,但 docker logs app 没报错。后来用 docker exec app ss -tlnp 一看,监听的是 127.0.0.1:5000。把启动命令从 flask run 改成 flask run --host=0.0.0.0 后,502 立刻消失。这个坑在没有明确报错时最难查,必须主动看监听地址。

常见问题

问:502 和 504 怎么区分?
答:502 是 Nginx 连不上上游或上游返回无效响应;504 是上游连上了但超时没回数据。看 Nginx error_log 里的错误码最准。

问:为什么不用容器 IP 而要用服务名?
答:容器重启后 IP 会变,Docker 内置 DNS 会把服务名解析到当前容器 IP,用服务名更稳。

问:健康检查应该怎么配?
答:给应用加一个轻量的 /health 端点,只返回状态不要查数据库,避免健康检查本身把服务压垮。

关于 Docker 和 Nginx 的更多排错案例,可以到 fenij.com 留言交流。

相关完整手册

系统化的排查与配置思路,建议顺手收藏这几篇完整手册: