作者:懒人
环境:Ubuntu 22.04 + Docker Compose + FastAPI + PostgreSQL + Nginx(前端容器)
关键词:Docker、FastAPI、PostgreSQL、Nginx、docker-compose、netcat、500错误、端口映射


💡 背景

在使用 Docker Compose 部署一个包含 FastAPI 后端PostgreSQL 数据库Nginx 前端 的全栈应用时,遇到了两个典型问题:

  1. 后端启动卡在 Waiting for database...,日志提示 sh: 1: nc: not found
  2. 访问 /docs(Swagger UI)时返回 Nginx 500 Internal Server Error

经过多次调试,最终成功解决。本文总结关键踩坑点和最佳实践,供后来者参考。


🔧 问题一:nc: not found —— 后端无法检测数据库就绪

❌ 现象

后端容器日志循环打印:

Waiting for database...
sh: 1: nc: not found

✅ 原因

  • 后端启动脚本使用 nc -z host port 检测数据库是否可用
  • 但基础镜像(如 python:3.11-slim默认不包含 netcat 工具
  • 导致检测失败,无限等待

✅ 解决方案:在 Dockerfile 中安装 netcat-openbsd

# backend/Dockerfile
FROM python:3.11-slim

WORKDIR /app
ENV PYTHONPATH=/app

# 安装系统依赖(含 netcat)
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        libpq-dev \
        gcc \
        netcat-openbsd \
    && rm -rf /var/lib/apt/lists/*

# ... 其他步骤(pip install、COPY 等)

# 创建非 root 用户(安全)
RUN addgroup --system app && adduser --system --ingroup app app
RUN chown -R app:app /app
USER app

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

⚠️ 注意:Alpine 镜像请用 apk add netcat-openbsd


🛠 问题二:必须重建镜像才能生效!

❌ 错误操作

# 仅重启,不重建 → 无效!
docker compose restart backend

✅ 正确流程

# 1. 修改 Dockerfile 后,必须 rebuild
docker compose build backend

# 2. 再启动
docker compose up -d

📌 Docker 不会自动感知 Dockerfile 变更!必须手动 build


🌐 问题三:Nginx 返回 500 —— 端口不一致 or 代理未配置

❌ 现象

访问 http://IP/docs 返回 500 Internal Server Error

✅ 根本原因分析

情况 1:后端实际监听端口 ≠ 映射端口
# docker-compose.yml
command: uvicorn ... --port 8080   # 实际监听 8080
ports:
  - "8000:8000"                    # 但只映射了 8000!

→ 宿主机无法通过 8000 访问,Nginx 代理失败。

情况 2:前端 Nginx 未配置反向代理
  • 如果前端是纯静态 Nginx 容器(无代理配置)
  • 访问 /docs 会被当成静态资源 → 404/500

✅ 解决方案

✅ 方案 A:统一使用 8000 端口(推荐)
# docker-compose.yml
services:
  backend:
    command: >
      sh -c "while ! nc -z note-db 5432; do sleep 2; done;
             uvicorn main:app --host 0.0.0.0 --port 8000"
    ports:
      - "8000:8000"   # 宿主机 8000 → 容器 8000
✅ 方案 B:若用 8080,需暴露并代理
ports:
  - "8080:8080"

并在前端 Nginx 配置中添加:

location /docs {
    proxy_pass http://backend:8080;
}
location /api/ {
    proxy_pass http://backend:8080;
}

🔑 关键proxy_pass 中的服务名必须是 docker-compose.yml 中的 服务名(如 backend),不是 container_name


🧪 调试技巧

1. 查看服务名(不是容器名!)

docker compose config --services
# 输出:db, backend, frontend

2. 直连后端测试(绕过 Nginx)

curl http://localhost:8000/docs

3. 进入 Nginx 容器测试连通性

docker exec -it note-frontend sh
apk add curl
curl http://backend:8000/docs  # 应返回 HTML

4. 清理重建(开发阶段推荐)

docker compose down -v    # -v 删除卷(清空数据库)
docker compose build backend
docker compose up -d

⚠️ -v 会丢失数据库数据,生产环境慎用!


✅ 最终架构建议

# docker-compose.yml(精简版)
services:
  db:
    image: postgres:15
    container_name: note-db
    environment: { ... }
    volumes: [pgdata:/var/lib/postgresql/data]
    networks: [app-net]

  backend:
    build: ./backend
    container_name: note-backend
    command: sh -c "while ! nc -z db 5432; do sleep 2; done; uvicorn main:app --host 0.0.0.0 --port 8000"
    ports: ["8000:8000"]
    depends_on: [db]
    networks: [app-net]

  frontend:
    build: ./frontend  # 内含 Nginx + 代理配置
    ports: ["80:80"]
    networks: [app-net]

networks:
  app-net:
volumes:
  pgdata:

🎯 总结:关键注意事项

问题注意点
nc: not found必须在 Dockerfile 安装 netcat-openbsd
服务名 vs 容器名docker compose build 用 服务名docker logs 用 容器名
端口一致性uvicorn --port 必须与 ports 映射端口一致
Nginx 500检查 proxy_pass 是否指向正确的 服务名:端口
重建镜像修改 Dockerfile 后必须 docker compose build
数据库初始化PostgreSQL 日志中的“shutdown”是正常流程,非错误

❤️ 结语

Docker Compose 是强大的工具,但细节决定成败。
日志是第一线索,网络和端口是高频陷阱
希望本文能帮你少走弯路,快速部署成功!

记住:90% 的“连不上”问题,都是因为服务名、端口、网络或镜像未更新!

更多推荐