Appearance
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: bridgeNginx 配置片段如下(特意写长一行以验证代码块不截断):
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 不可达。