服务顺序启动,解决 docker-compose 启动顺序 mysql启动慢导致服务失败

本文介绍了一种确保Docker服务按依赖顺序启动的方法,尤其解决了MySQL启动慢导致Spring Boot应用启动失败的问题。通过自定义脚本监听MySQL及Redis端口,确认服务可用后再启动Spring Boot应用。

问题的产生

我使用docker-compose 编写了一个脚本,包括了mysqlredisspring boot server。但是由于mysql 数据量大了后启动会变慢,导致sprong boot server 启动失败

即使使用了 depends_on 这个配置也无效,因为这个配置只能保证容器的启动顺序,但是在mysql容器启动了但是服务还未启动,因此出现了这个问题。

解决问题

你赶时间的话请直接采用我的最终解,将你的 docker-compose,中的镜像命令修改一下

  • image:一个包含了监听其他端口脚本的镜像
  • entrypoint: 等待mysqlredis启动后 执行 -c 后的命令
my_server:
    image: sunbrightness/jdk-wait_run:8
    container_name: my_server
    ports:
      - 8080:8080
    environment:
      - LANG=C.UTF-8
      - TZ=Asia/Shanghai
    volumes:
      # 配置文件
      - ./server/:/home/java/
    privileged: true
    entrypoint: 'wait_run -d my_mysql:3306,my_redis:6379 -c "java -jar admin.jar"'
    restart: always
    depends_on:
      - km_mysql
      - km_redis

问题细节

编写一个sh 脚本

利用 linux 中的 netcat 工具监听端口,在端口启动后运行spring boot server

touch wait_run
chmod +x wait_run
vim wait_run
#!/bin/bash
#set -x

: ${SLEEP_SECOND:=2}

wait_for() {
    echo Waiting for $1 to listen on $2...
    while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
}

declare DEPENDS
declare CMD

while getopts "d:c:" arg
do
    case $arg in
        d)
            DEPENDS=$OPTARG
            ;;
        c)
            CMD=$OPTARG
            ;;
        ?)
            echo "unkonw argument"
            exit 1
            ;;
    esac
done

for var in ${DEPENDS//,/ }
do
    host=${var%:*}
    port=${var#*:}
    wait_for $host $port
done

eval $CMD

这样你就可以通过如下命令,等待多个端口启动后再执行你的命令

/bin/bash wait_run -d 127.0.0.1:3306,127.0.0.1:6379 -c "java -jar admin.jar"

注意 jdk的docker容器不包含这个命令 nc

因此我们需要自定义一个Dockerfile 文件,让其安装好 netcat

# 基础镜像
FROM openjdk:8
# author
MAINTAINER sunbrightness

# 挂载目录
VOLUME /home/java
# 创建目录
RUN mkdir -p /home/java
# 指定路径
WORKDIR /home/java

# fix apt update error
RUN echo "deb https://mirrors.cernet.edu.cn/debian/ bullseye main contrib non-free" > /etc/apt/sources.list
RUN apt-get update
# 安装netcat用于监听端口启动
RUN apt-get -y install netcat

# 将刚刚创建的nc命令移动到镜像中
COPY ./wait_run /bin
RUN chmod +x /bin/wait_run

这个便是我在最上文提到的sunbrightness/jdk-wait_run:8 镜像了。

花了好长时间才搞好的。也知道了java8的docker镜像是基于Debian 11 系统的

<think>我们正在分析的问题:在容器内直接执行SQL脚本成功,但通过docker-compose初始化时失败。 可能的原因: 1. 执行时机问题:docker-compose启动容器时,可能数据库服务尚未完全准备好就执行了脚本。 2. 文件路径问题:在docker-compose中指定的init脚本路径可能不正确。 3. 权限问题:docker-compose启动时可能以非root用户执行,导致权限不足。 4. 环境差异:容器内直接执行时环境变量可能和docker-compose启动的环境有差异。 5. 脚本格式问题:例如换行符(CRLF vs LF)或编码问题,可能在不同执行方式下表现不同。 解决方案: 1. 确保数据库服务已就绪:在docker-compose中使用健康检查(healthcheck)来等待数据库就绪后再执行初始化。 2. 检查文件挂载:确认docker-compose.yml中正确挂载了initdb.d目录。 3. 使用官方MySQL镜像的初始化机制:官方镜像会自动执行/docker-entrypoint-initdb.d/下的脚本,但需要确保该目录被挂载且文件为.sh、.sql或.sql.gz格式。 4. 查看日志:使用docker-compose logs查看错误信息。 具体步骤: 步骤1:在docker-compose.yml中为MySQL服务添加健康检查 ```yaml services: db: image: mysql:5.7 command: --default-authentication-plugin=mysql_native_password restart: always environment: MYSQL_ROOT_PASSWORD: Qinyu456 volumes: - ./initdb:/docker-entrypoint-initdb.d # 确保挂载目录 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pQinyu456"] interval: 5s timeout: 10s retries: 10 ``` 步骤2:确保其他服务(如Web应用)在数据库健康后才启动 ```yaml web: depends_on: db: condition: service_healthy ``` 步骤3:检查initdb目录下的iot.sql文件 - 文件必须是Unix格式(LF) - 文件内容不能有语法错误(已在之前问题中修复) 步骤4:如果问题仍然存在,尝试在docker-compose.yml中增加初始化超时等待 - 官方镜像在启动时会自动执行/docker-entrypoint-initdb.d/下的脚本,但有时数据库启动,可以在脚本开头增加等待逻辑(如使用sleep或循环检查数据库是否可连接)。 示例:在SQL脚本前增加一个等待的shell脚本(wait-for-mysql.sh) ```bash #!/bin/bash until mysqladmin ping -h"$DB_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" --silent; do sleep 1 done ``` 然后在docker-compose中先执行此脚本,再执行SQL脚本。 但注意:官方MySQL镜像会按字母顺序执行/docker-entrypoint-initdb.d/下的所有脚本,因此可以这样组织: - 将等待脚本命名为01_wait_for_mysql.sh - SQL脚本命名为02_schema.sql 步骤5:检查日志 - 运行docker-compose up后,查看数据库容器的日志,确认初始化过程。 常见错误原因: - 挂载的目录不正确,导致脚本未被执行。 - 脚本没有执行权限(如果是.sh脚本,需要chmod +x)。 - 脚本中使用了相对路径或环境变量未设置。 如果直接执行成功而compose失败,最可能的原因是数据库服务未就绪时脚本就被执行。因此,健康检查和等待脚本是最常用的解决方案。</think>### Docker ComposeMySQL 初始化失败的原因分析与解决方案 #### 核心问题分析: 当容器内手动执行成功但 Docker Compose 初始化失败时,通常由以下原因导致: 1. **启动顺序问题** MySQL 服务尚未完全启动时,初始化脚本已开始执行 2. **权限问题** Docker Compose 默认以非 root 用户执行初始化脚本 3. **文件挂载时机** Volume 挂载发生在服务启动之后,导致脚本未被识别 4. **环境变量差异** Compose 环境与手动执行环境存在配置差异 5. **脚本执行限制** 官方镜像对 `/docker-entrypoint-initdb.d` 有特殊执行规则 --- ### 完整解决方案: #### 方案1:添加服务依赖等待(推荐) ```yaml services: mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: Qinyu456 volumes: - ./initdb:/docker-entrypoint-initdb.d healthcheck: # 添加健康检查 test: ["CMD", "mysqladmin", "ping", "-uroot", "-pQinyu456"] interval: 5s timeout: 3s retries: 10 app: # 依赖MySQL的其他服务 depends_on: mysql: condition: service_healthy # 等待MySQL就绪 ``` #### 方案2:使用初始化等待脚本 在 SQL 脚本前添加 `wait-for-mysql.sh`: ```bash #!/bin/bash # 等待MySQL服务就绪 until mysqladmin ping -uroot -pQinyu456 --silent; do sleep 2 done ``` 在 Compose 中优先执行: ```yaml volumes: - ./initdb:/docker-entrypoint-initdb.d # 确保等待脚本优先执行 command: - bash - -c - "chmod +x /docker-entrypoint-initdb.d/*.sh && exec docker-entrypoint.sh mysqld" ``` #### 方案3:调整权限和路径 ```yaml services: mysql: user: root # 强制使用root权限 volumes: - ./initdb:/docker-entrypoint-initdb.d:ro # 只读挂载 environment: MYSQL_DATABASE: iot_db # 预先声明数据库 ``` #### 方案4:使用自定义入口点 创建 `custom-entrypoint.sh`: ```bash #!/bin/bash # 等待MySQL启动 while ! mysqladmin ping -uroot -p$MYSQL_ROOT_PASSWORD --silent; do sleep 1 done # 执行初始化脚本 mysql -uroot -p$MYSQL_ROOT_PASSWORD < /docker-entrypoint-initdb.d/iot.sql # 启动原始进程 exec docker-entrypoint.sh mysqld ``` Compose 配置: ```yaml volumes: - ./custom-entrypoint.sh:/usr/local/bin/custom-entrypoint.sh - ./iot.sql:/docker-entrypoint-initdb.d/iot.sql entrypoint: ["custom-entrypoint.sh"] ``` --- ### 关键排查步骤: 1. **查看日志定位错误**: ```bash docker-compose logs mysql | grep "ERROR" ``` 2. **验证文件挂载**: ```bash docker-compose exec mysql ls -l /docker-entrypoint-initdb.d ``` 3. **检查执行顺序**: ```bash docker-compose exec mysql cat /proc/1/cmdline ``` 4. **测试连接可用性**: ```bash docker-compose exec mysql mysqladmin -uroot -pQinyu456 ping ``` --- ### 最佳实践总结: 1. **始终添加健康检查**:确保服务就绪后再初始化 2. **使用官方初始化目录**:`/docker-entrypoint-initdb.d` 3. **显式声明数据库**:通过 `MYSQL_DATABASE` 环境变量 4. **脚本命名规范**:`.sh` 脚本需有执行权限,`.sql` 按字母顺序执行 5. **避免复杂脚本**:拆分大文件为多个小脚本(如 `01-schema.sql`, `02-data.sql`) > **注意**:MySQL 官方镜像在首次启动空数据库时才会执行 `docker-entrypoint-initdb.d` 中的脚本,若已有数据存在则不会执行。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值