应让容器在数据库服务真正就绪(可连接+初始化完成)后执行数据迁移,需结合healthcheck就绪检测与depends_on condition服务健康依赖,并在容器内通过wait-for-it或原生轮询执行迁移脚本,失败时明确退出。
要让容器在依赖服务(比如数据库)真正就绪后,才自动执行数据迁移(如 flyway、liquibase、django migrate、spring boot schema init 等),关键不是“等容器启动”,而是“等服务可连接 + 迁移脚本可安全运行”。这需要就绪检测 + 启动逻辑编排双层配合。
✅ 1. 先确保数据库服务被正确标记为“就绪”
仅靠 depends_on 不够——它只等容器 running,不等数据库初始化完成。必须加 healthcheck,并设置合理参数:
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d myapp"]
interval: 10s
timeout: 5s
retries: 12
start_period: 90s # 给足时间:启动 + 初始化 + 数据导入(如有)
⚠️
start_period很关键:PostgreSQL 首次启动可能需加载扩展、恢复备份或执行initdb,90 秒是常见安全值;retries × interval应覆盖该周期。
✅ 2. 应用服务声明强依赖条件
在 docker-compose.yml 中,让应用服务明确等待数据库“健康”后再启动主进程:
app:
build: .
depends_on:
db:
condition: service_healthy # ← 关键!不是 just 'service_started'
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/myapp
✅ 此配置要求 Docker Compose v2.20+ 或 Compose file v3.4+,旧版本不支持
condition字段。
✅ 3. 在应用容器内执行迁移(推荐方式)
把迁移逻辑放在容器启动入口(entrypoint 或 command),而非外部调度。这样更可靠、可调试、与环境解耦。
方式一:使用 wait-for-it.sh + 迁移命令(通用、轻量)
# Dockerfile 中复制脚本 COPY wait-for-it.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/wait-for-it.sh # 启动时先等 DB 就绪,再跑迁移,最后启应用 ENTRYPOINT ["sh", "-c", \ "./wait-for-it.sh db:5432 --timeout=120 --strict -- \ ./run-migration.sh && \ exec java -jar app.jar"]
其中 run-migration.sh 示例(Spring Boot + Flyway):
#!/bin/sh java -Dspring.profiles.active=migrate -jar app.jar
方式二:原生命令轮询(免额外脚本)
ENTRYPOINT ["sh", "-c", \
"until pg_isready -h db -p 5432 -U postgres -d myapp; do \
echo 'Waiting for DB...'; \
sleep 5; \
done; \
echo 'DB ready, running migration...'; \
java -Dspring.profiles.active=migrate -jar app.jar; \
echo 'Migration done, starting app...'; \
exec java -jar app.jar"]
✅ 优点:无外部依赖,日志清晰;缺点:需确保容器内有
pg_isready或mysqladmin等客户端工具(建议apt-get install -y postgresql-client)。
✅ 4. 迁移失败时要有明确反馈和退出
不要静默忽略错误。迁移脚本应:
- 使用非零退出码表示失败;
- 输出具体错误(如 SQL 报错、权限拒绝、连接超时);
- 避免无限重试导致容器卡住。
示例(带失败终止):
if ! java -Dspring.profiles.active=migrate -jar app.jar; then echo "❌ Migration failed — container will exit" exit 1 fi
Docker 会捕获该退出码,docker-compose ps 显示 Exit 1,方便快速定位。
❌ 不推荐的做法
在 host 主机上用 shell 脚本循环
docker exec检查再触发迁移
→ 割裂容器生命周期,难移植,不满足不可变基础设施原则。把迁移写进数据库的
init.sql或docker-entrypoint-initdb.d
→ 仅适用于首次空库初始化;无法处理升级、回滚、多环境差异。依赖
sleep 30等固定延时
→ 不稳定:慢机器可能不够,快机器又浪费;CI/CD 和不同云环境表现不一。
不复杂但容易忽略:就绪 ≠ 启动,迁移 ≠ 一次性的“启动前动作”。把检测、等待、执行、校验串成原子流程,才是生产级容器编排的常态。











