Docker Compose depends_on条件失效:Spring Boot API与MS SQL Server启动问题
我之前搭建Spring Boot + MS SQL的Docker环境时,也碰到过一模一样的问题——API总是抢在数据库初始化完成前启动,导致连接失败。给你几个亲测有效的解决方向:
1. 先修复健康检查的核心问题
你当前的健康检查有个致命漏洞:MS SQL容器里设置的SA_PASSWORD是<my pw>,但健康检查命令里硬编码的却是CorrectHorseBatteryStapleFor$,密码不匹配导致健康检查永远失败,service_healthy条件自然不会生效。
先把密码统一,再调整健康检查逻辑,确保它能准确判断数据库完全初始化完成(不仅是SQL Server启动,还要你的初始化脚本执行完毕):
HEALTHCHECK --interval=10s --timeout=10s --start-period=60s --retries=10 CMD /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SA_PASSWORD" -d dwh-demo -Q "SELECT 1" > /dev/null 2>&1 || exit 1
- 用
$SA_PASSWORD引用环境变量,避免硬编码密码不一致 - 把
start-period设为60s,给初始化脚本足够的执行时间 - 重定向输出到
/dev/null,减少不必要的日志噪音
2. 别再用service_completed_successfully(用错场景了)
service_completed_successfully是用来等待一次性任务容器执行完毕退出的(比如临时数据迁移容器),但你的MS SQL是长期运行的服务容器,这个条件永远不会满足,所以API会直接启动——这就是为什么这个配置完全没用。你必须用service_healthy,但前提是健康检查配置正确。
3. 在Spring Boot里添加连接重试(最可靠的兜底方案)
Docker的健康检查偶尔会有延迟或误判,最好在应用层面也做连接重试,确保启动时能等待数据库就绪。在application.properties(或yaml)里配置Hikari连接池参数:
# HikariCP连接池配置 spring.datasource.hikari.connection-timeout=60000 spring.datasource.hikari.validation-timeout=5000 spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.connection-test-query=SELECT 1 spring.datasource.hikari.initialization-fail-timeout=0
initialization-fail-timeout=0表示连接池会一直重试,直到成功连接数据库connection-timeout设为60秒,给足够的重试缓冲时间
另外,还可以用Spring Boot的@Retryable注解重试数据库初始化操作:
@Configuration @EnableRetry public class RetryConfig { } @Service public class DatabaseInitService { @Retryable(value = {SQLException.class}, maxAttempts = 10, backoff = @Backoff(delay = 2000)) public void initDatabase() { // 执行需要数据库就绪的初始化逻辑 } }
4. 用等待脚本让API启动前先等数据库
如果不想依赖Docker的健康检查,可以在API容器里加入wait-for-it.sh脚本,强制API启动前先检查数据库的连通性:
- 把
wait-for-it.sh下载到你的API项目目录,或者在Dockerfile里直接下载:RUN wget https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh -O wait-for-it.sh && chmod +x wait-for-it.sh - 修改API的Dockerfile的CMD:
CMD ["./wait-for-it.sh", "db:1433", "--", "mvn", "spring-boot:run"]
这个脚本会一直等待db容器的1433端口能连通,才会启动Spring Boot应用。
5. 优化数据库初始化脚本的执行顺序
确保你的初始化脚本执行完成后,SQL Server才会被标记为健康。可以在entrypoint.sh里先等待SQL Server启动,再执行初始化脚本:
# entrypoint.sh内容 # 等待SQL Server启动就绪 until /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SA_PASSWORD" -Q "SELECT 1" > /dev/null 2>&1; do echo "等待SQL Server启动中..." sleep 5 done # 执行初始化脚本 /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SA_PASSWORD" -i init.sql # 保持容器运行 tail -f /dev/null
这样初始化脚本只有在SQL Server完全启动后才会执行,完成后容器才能进入健康状态。
内容的提问来源于stack exchange,提问作者Playing With BI

