You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker Compose初始化数据库致服务启动失败问题求助

解决Docker Compose中MySQL初始化完成前API服务启动失败的问题

问题原因

depends_on: service_started仅能确保MySQL容器完成启动,但不代表数据库服务已就绪——首次启动时,MySQL需要初始化数据目录、执行初始化脚本,这个过程比容器启动慢得多,导致API服务启动时无法连接数据库而失败。

解决方案

方案1:给API服务添加重启策略

最简单的处理方式是让API容器在连接失败后自动重启,直到成功连接数据库。在app_user_api服务中添加重启配置:

app_user_api:
  depends_on:
    mysql_test_db:
      condition: service_started
  image: fast-api-beta
  container_name: test_user_api_1
  restart: on-failure  # 新增:启动失败时自动重试
  volumes:
    - /etc/certificates/site:/certs:ro
  # 其余环境变量、端口等配置保持不变

restart: on-failure会在容器启动失败时自动重试,直到成功连接数据库。如果希望容器始终保持运行(除非手动停止),也可以替换为restart: unless-stopped。

方案2:配置MySQL健康检查,让API等待数据库就绪

通过Docker的健康检查功能,检测MySQL服务是否真正可以接受连接,再启动API服务,从根源避免连接失败。

步骤1:给MySQL服务添加健康检查

在mysql_test_db服务中添加healthcheck配置:

mysql_test_db:
  image: mysql
  container_name: mysql_test_db
  command: --init-file /data/application/init.sql
  volumes:
    - ./init.sql:/data/application/init.sql
    - ./db_data/mysql:/var/lib/mysql
  environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_db_password
    MYSQL_DATABASE: TEST_SS_REST_API_1
  ports:
    - "3306"
  networks:
    - net_db
  secrets:
    - mysql_db_password
  restart: unless-stopped
  # 新增健康检查配置
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$(cat /run/secrets/mysql_db_password)"]
    interval: 5s          # 每5秒检查一次
    timeout: 5s           # 检查超时时间
    retries: 10           # 最多重试10次
    start_period: 10s     # 启动后10秒再开始检查(给初始化留缓冲时间)

健康检查命令通过mysqladmin ping验证MySQL服务可用性,使用secret中的密码进行认证($$是Docker Compose中对$的转义)。

步骤2:修改API服务的依赖条件

将app_user_api的depends_on条件改为service_healthy:

app_user_api:
  depends_on:
    mysql_test_db:
      condition: service_healthy  # 改为等待MySQL健康就绪
  # 其余配置保持不变

这样API服务只会在MySQL通过健康检查后才启动,确保数据库已完全初始化完成。

方案3:在API代码中添加数据库连接重试逻辑

从代码层面保障启动时能等待数据库就绪,适合需要更精确控制的场景。以FastAPI为例,在初始化数据库连接时添加重试逻辑:

import os
import time
from sqlalchemy import create_engine

def init_db_connection():
    max_retries = 20
    retry_delay = 2  # 每次重试间隔2秒
    engine = None

    for attempt in range(max_retries):
        try:
            # 从环境变量读取数据库配置
            username = os.getenv("APP_KEY_MYSQL_USERNAME")
            with open(os.getenv("APP_KEY_MYSQL_PASSWORD_FILE"), 'r') as f:
                password = f.read().strip()
            db_name = os.getenv("APP_KEY_MYSQL_DATABASE")
            host = os.getenv("APP_KEY_MYSQL_DOMAIN")
            port = os.getenv("APP_KEY_MYSQL_PORT")

            # 创建数据库引擎并测试连接
            engine = create_engine(f"mysql+pymysql://{username}:{password}@{host}:{port}/{db_name}")
            with engine.connect():
                print("数据库连接成功")
                break
        except Exception as e:
            print(f"连接失败,重试({attempt+1}/{max_retries}): {str(e)}")
            time.sleep(retry_delay)
    
    if not engine:
        raise RuntimeError("多次尝试后仍无法连接数据库,启动失败")
    return engine

# 在FastAPI启动前初始化数据库连接
db_engine = init_db_connection()

这段代码会在启动时循环尝试连接数据库,直到成功或达到重试上限。

额外优化:移除冗余的初始化脚本

你已经在MySQL环境变量中设置了MYSQL_DATABASE: TEST_SS_REST_API_1,MySQL容器启动时会自动创建该数据库,因此init.sql中的CREATE DATABASE IF NOT EXISTS TEST_SS_REST_API_1可以移除,减少不必要的初始化步骤。


内容的提问来源于stack exchange,提问作者Decaf Sux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 05:23:24