启动顺序不等于就绪
depends_on 只保证依赖容器先于应用启动,但依赖可能仍在初始化。例如 PostgreSQL 可能已打开端口,却仍在恢复或创建数据库。应用立刻连接,得到连接拒绝或认证错误。
services:
db:
image: postgres:15-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 2s
timeout: 3s
retries: 20
app:
build: .
depends_on:
db:
condition: service_healthy
让健康检查有意义
健康检查应测试应用真正需要的服务,而不只是进程存在。pg_isready 检查数据库能否接受连接;Redis 的 PING 或 HTTP readiness 端点也比 TCP 端口检查更合适。设置适合最慢启动时间的间隔和重试次数。
应用内也要重试
即使有健康检查,启动后仍可能发生重启或网络抖动。应用应使用有界连接重试、退避和清晰的失败信息。这能抵御编排器差异,并让本地与生产行为一致。
从冷启动验证
执行 docker compose down -v 再 docker compose up,然后不带卷重复一次。确认应用等待健康状态并成功启动。在启动期间停止数据库,验证应用会重试或清晰失败,而不是以难以理解的错误崩溃。
修复与验证
用真实的依赖健康检查、condition: service_healthy 和应用内有界重试修复启动竞态。通过冷卷启动、启动中停止依赖,以及确认应用要么成功等待要么报告可重试错误来验证。固定的 sleep 不算修复,它只改变同一竞态的发生时机。