如何在Docker Compose中先执行Alembic迁移再启动FastAPI
解决方案:Docker Compose 中实现 PostgreSQL 就绪后执行 Alembic 迁移再启动 FastAPI
核心问题分析
你遇到的连接拒绝/表不存在问题,本质是PostgreSQL 容器启动后并非立刻能接受连接,直接用 && 串联命令会导致迁移或 API 提前执行,无法等待数据库完全就绪。
实现步骤
1. 编写启动脚本(推荐 Shell 脚本)
创建 start.sh 脚本,负责等待 PostgreSQL 就绪、执行迁移、启动 API:
#!/bin/bash # 等待 PostgreSQL 就绪(使用官方工具 pg_isready) echo "等待 PostgreSQL 启动..." until pg_isready -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER"; do sleep 2 done # 执行 Alembic 迁移(已存在表会自动跳过) echo "PostgreSQL 就绪,执行数据库迁移..." alembic upgrade head # 启动 FastAPI 应用 echo "迁移完成,启动 API 服务..." uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
给脚本添加执行权限:
chmod +x start.sh
2. 修改 Dockerfile
确保镜像包含 postgresql-client(用于 pg_isready),并将脚本设置为容器启动命令:
FROM python:3.11-slim WORKDIR /app # 安装系统依赖(含 pg_isready)和 Python 依赖 COPY requirements.txt . RUN apt-get update && apt-get install -y --no-install-recommends postgresql-client \ && rm -rf /var/lib/apt/lists/* \ && pip install --no-cache-dir -r requirements.txt # 复制应用代码、Alembic 配置和启动脚本 COPY . . COPY start.sh . # 设置容器启动命令 CMD ["./start.sh"]
3. 配置 Docker Compose
添加 PostgreSQL 健康检查,确保 API 容器仅在数据库完全就绪后启动:
version: '3.8' services: db: image: postgres:15-alpine environment: POSTGRES_USER: your_db_user POSTGRES_PASSWORD: your_db_password POSTGRES_DB: your_db_name volumes: - postgres_data:/var/lib/postgresql/data/ # 健康检查:检测数据库是否可正常连接 healthcheck: test: ["CMD-SHELL", "pg_isready -U your_db_user -d your_db_name"] interval: 5s timeout: 5s retries: 5 api: build: . environment: DB_HOST: db DB_PORT: 5432 DB_USER: your_db_user DB_PASSWORD: your_db_password DB_NAME: your_db_name ports: - "8000:8000" volumes: - .:/app # 开发环境热重载用 # 依赖数据库容器,且仅在数据库健康后启动 depends_on: db: condition: service_healthy volumes: postgres_data:
4. 确保 Alembic 读取环境变量
修改 alembic/env.py,从环境变量获取数据库连接信息,避免硬编码:
from sqlalchemy import create_engine import os from alembic import context # 从环境变量读取数据库配置 db_user = os.getenv("DB_USER") db_password = os.getenv("DB_PASSWORD") db_host = os.getenv("DB_HOST") db_port = os.getenv("DB_PORT") db_name = os.getenv("DB_NAME") # 设置 Alembic 数据库连接 URL config = context.config config.set_main_option( "sqlalchemy.url", f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" )
替代方案:用 Python 脚本等待数据库
如果不想安装 postgresql-client,可以写一个 Python 脚本 wait_for_db.py:
import time import os from sqlalchemy import create_engine db_url = f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}" while True: try: engine = create_engine(db_url) with engine.connect(): print("PostgreSQL 已就绪") break except Exception as e: print(f"等待数据库中:{str(e)}") time.sleep(2)
然后修改启动命令为:
python wait_for_db.py && alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
内容的提问来源于stack exchange,提问作者TheGreatO96
相关产品推荐
相关产品推荐

