Docker Compose服务需等待MySQL初始化脚本完成数据导入
问题描述
我有一个依赖MySQL服务器的Docker Compose服务,首次启动时需要初始化数据库(导入75MB的SQL文件,耗时约1分钟)。当前遇到的问题:
- 使用
service_completed_successfully条件时,myservice始终无法启动 - 使用
service_healthy条件时,默认健康检查仅能验证MySQL是否可连接,导致myservice在数据导入完成前就启动;而MySQL完成数据导入后会自动重启服务器,干扰应用程序的正常连接
当前db服务配置:
version: '3.7' services: db: image: mysql:8.0.33 environment: MYSQL_DATABASE: 'test' MYSQL_ROOT_PASSWORD: 'test' ports: - 3306:3306 expose: - 3306 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"] timeout: 20s retries: 10 volumes: - ./sql/test.sql:/docker-entrypoint-initdb.d/test.sql - mysql:/var/lib/mysql
从MySQL日志可见,数据导入完成后会重启主服务,此时myservice已启动,必然出现连接异常:
db_1 | 2023-05-11T18:36:01.172885Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.33) MySQL Community Server - GPL. db_1 | 2023-05-11 18:36:01+00:00 [Note] [Entrypoint]: Temporary server stopped db_1 | db_1 | 2023-05-11 18:36:01+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up. db_1 | db_1 | 2023-05-11T18:36:01.769092Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead. db_1 | 2023-05-11T18:36:01.770116Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.33) starting as process 1 db_1 | 2023-05-11T18:36:01.776905Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2023-05-11T18:36:01.904675Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2023-05-11T18:36:02.168020Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. db_1 | 2023-05-11T18:36:02.168064Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. db_1 | 2023-05-11T18:36:02.172087Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. db_1 | 2023-05-11T18:36:02.193999Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock db_1 | 2023-05-11T18:36:02.194118Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.33' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
解决方案
方法一:增强健康检查,验证数据导入完成
修改MySQL的健康检查命令,不仅验证服务连通性,还要检查导入的数据是否存在(比如查询目标表或记录)。只有当数据完全导入且主服务正常运行时,健康检查才会通过,myservice才会启动。
假设SQL文件导入后会创建users表,调整后的db服务健康检查配置:
db: # 其他配置保持不变 healthcheck: test: ["CMD", "mysql", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD", "-D", "test", "-e", "SELECT 1 FROM users LIMIT 1;"] timeout: 20s retries: 15 # 增加重试次数,适配大文件导入时长 interval: 10s # 缩短检查间隔,更快检测到服务就绪
原理
MySQL官方镜像的docker-entrypoint-initdb.d脚本会先启动临时服务器执行SQL导入,完成后关闭临时服务器再启动主服务器。增强后的健康检查会在主服务器启动后,尝试查询导入的数据,只有查询成功才会标记服务为健康,彻底避免MySQL重启带来的连接问题。
方法二:自定义初始化脚本,避免MySQL重启(可选)
如果不想依赖官方镜像的默认初始化流程,可以自定义entrypoint脚本,直接在主服务中完成数据导入,跳过临时服务器启动和重启步骤。
- 创建
custom-entrypoint.sh脚本:
#!/bin/bash set -e # 启动MySQL主服务 mysqld_safe & MYSQL_PID=$! # 等待MySQL服务就绪 until mysqladmin ping -h localhost -u root -p"$MYSQL_ROOT_PASSWORD" --silent; do echo "等待MySQL启动..." sleep 2 done # 执行数据导入 mysql -h localhost -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < /docker-entrypoint-initdb.d/test.sql # 保持MySQL服务运行 wait $MYSQL_PID
- 在Docker Compose中挂载脚本并设置为entrypoint:
db: # 其他配置保持不变 volumes: - ./sql/test.sql:/docker-entrypoint-initdb.d/test.sql - ./custom-entrypoint.sh:/custom-entrypoint.sh - mysql:/var/lib/mysql entrypoint: ["/bin/bash", "/custom-entrypoint.sh"]
注意事项
- 方法一更贴合MySQL官方镜像设计,无需额外脚本,优先推荐使用
- 调整健康检查的
retries和interval参数时,需根据SQL文件大小估算导入时间,确保有足够等待时长
内容的提问来源于stack exchange,提问作者helion3
相关产品推荐
相关产品推荐

