Skip to content

Nginx 反向代理 Docker 容器时 502 排查 ​

现象 ​

业务上线后,用户访问 https://api.example.com/health 间歇性返回 502 Bad Gateway。Nginx error.log 里频繁出现:

text
2026/09/30 10:23:11 [error] 1234#1234: *56789 connect() failed (111: Connection refused) while connecting to upstream, client: 10.0.0.5, server: api.example.com, request: "GET /health HTTP/1.1", upstream: "http://172.18.0.3:8080/health"

环境 ​

  • OS: Ubuntu 22.04 LTS
  • Nginx: 1.24.0(宿主机)
  • Docker: 27.0.3
  • 后端:Python FastAPI,运行在一个 Docker 容器内
  • 网络:默认 bridge 网络 docker0,容器 IP 为 172.18.0.3

排查过程 ​

先在宿主机上确认容器是否健康:

bash
docker ps

容器状态为 Up 3 hours,看起来正常。再查看容器日志:

bash
docker logs app-container --tail 50

输出显示 FastAPI 已启动:

text
INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)

注意这里监听的是 127.0.0.1:8080。进入容器内部测试:

bash
docker exec -it app-container bash
curl -s http://127.0.0.1:8080/health

返回正常:

json
{
  "status": "ok",
  "timestamp": "2026-09-30T10:25:00Z",
  "version": "1.2.3",
  "checks": {
    "database": "ok",
    "redis": "ok"
  }
}

但在宿主机上直接访问容器 IP:

bash
curl -s http://172.18.0.3:8080/health

直接 Connection refused。这说明服务虽然启动了,但没有监听容器网卡上的 IP,只监听了 loopback。

查看 Dockerfile:

dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8080

CMD ["uvicorn", "main:app", "--host", "127.0.0.1", "--port", "8080"]

问题就在这里:--host 127.0.0.1 让服务只接受来自容器内部的请求。Nginx 在宿主机上通过容器 IP 访问时,被操作系统拒绝。

查看 docker-compose 配置:

yaml
version: '3.8'

services:
  app:
    build: ./app
    container_name: app-container
    restart: unless-stopped
    networks:
      - backend
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/app
      - REDIS_URL=redis://redis:6379/0
    volumes:
      - ./app/config.ini:/app/config.ini:ro

  nginx:
    image: nginx:1.24.0
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    networks:
      - backend
    depends_on:
      - app

networks:
  backend:
    driver: bridge

Nginx 配置片段如下(特意写长一行以验证代码块不截断):

nginx
upstream app_backend {
    server app-container:8080 max_fails=3 fail_timeout=30s;
    keepalive 64;
}

server {
    listen 80;
    server_name api.example.com;

    location /health {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

另外,应用配置文件 config.ini 内容如下:

ini
[server]
host = 127.0.0.1
port = 8080
workers = 4

[logging]
level = INFO
format = %(asctime)s - %(name)s - %(levelname)s - %(message)s

[database]
pool_size = 10
max_overflow = 20

根因 ​

Docker 容器内应用监听 127.0.0.1:8080,只接受容器内部回环地址的连接。Nginx 通过容器 IP 或 docker-compose service name 访问时,请求到达的是容器的 eth0 网卡,被拒绝连接,导致 502。

修复 ​

将 Dockerfile 中的 --host 改为 0.0.0.0:

dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8080

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

同时把 config.ini 也改掉,避免配置覆盖:

ini
[server]
host = 0.0.0.0
port = 8080
workers = 4

重新构建并启动:

bash
docker compose down
docker compose up -d --build

宿主机上再次测试:

bash
curl -s http://172.18.0.3:8080/health

返回正常 JSON,Nginx 也不再报 502。

复盘 ​

  • 容器内服务监听地址尽量使用 0.0.0.0,除非明确只需要本地访问。
  • Dockerfile 审查加入启动命令检查,避免 --host 127.0.0.1 误用。
  • 健康检查 endpoint 建议同时在容器外和 Nginx 层配置探测,快速发现 upstream 不可达。

用 VitePress 构建