docker compose 是 ci 中启动 mysql 的最佳实践:固定版本镜像、显式设 mysql_root_password、挂载 init 脚本到 /docker-entrypoint-initdb.d/、用 mysqladmin ping 健康检查、轮询等待而非 sleep、通过 ~/.my.cnf 安全注入凭据,并统一时区与字符集。

用 Docker Compose 在 CI 流水线中启动 MySQL 实例
CI 环境里不推荐从源码编译或用系统包管理器装 MySQL,耗时且版本难控。Docker 是最轻量、可复现的选择,docker-compose up -d 通常 3 秒内就能拉起一个干净的 mysql:8.0 或 mysql:5.7 实例。
关键点在于:必须显式指定 MYSQL_ROOT_PASSWORD,否则容器会卡在初始化阶段;同时建议挂载初始化 SQL 脚本到 /docker-entrypoint-initdb.d/,MySQL 启动时自动执行(仅对空数据目录生效)。
- CI 配置中避免使用
latest标签,固定为mysql:8.0.33这类具体版本,防止因镜像更新导致字符集或默认认证插件变化(比如caching_sha2_password不被旧客户端支持) - 若测试需多库并存,直接在 init SQL 里用
CREATE DATABASE IF NOT EXISTS test_db;,别依赖 CI 脚本额外执行mysql -e - 健康检查别只靠端口探测,加一句
mysqladmin ping -h127.0.0.1 -P3306 -uroot -p$$ROOT_PASS --silent,避免容器已运行但 mysqld 还没 ready
绕过 MySQL 服务等待的常见错误写法
很多 CI 脚本用 sleep 5 等 MySQL 启动,这不可靠——在负载高的 runner 上可能要等 8 秒,而轻量环境 2 秒就 OK,硬 sleep 既拖慢流水线又埋隐患。
更稳妥的方式是轮询 + 超时退出:
until mysql -h127.0.0.1 -P3306 -uroot -p$$ROOT_PASS -e "SELECT 1" > /dev/null 2>&1; do sleep 1 done
注意:这里用 -h127.0.0.1 而非 -hlocalhost,因为后者会触发 Unix socket 连接,而容器内往往没挂载 socket 文件;密码用环境变量 $$ROOT_PASS(CI 中需双美元符转义)而非明文写死。
- 如果 CI 使用 GitHub Actions,
mysql客户端默认不存在,得先apt-get update && apt-get install -y mysql-client - GitLab CI 的
alpine镜像没有mysql,改用mariadb-client并确保协议兼容(mysql --protocol=tcp)
数据库迁移与 schema 初始化不能靠人工 SQL 文件
把 CREATE TABLE 写死在 init 脚本里,会导致每次 CI 运行都重建表结构,但实际开发中 DDL 是渐进演化的。应该用轻量迁移工具衔接。
推荐 golang-migrate/migrate CLI(单二进制、无依赖),配合 file:// 驱动读取 .sql 迁移文件:
migrate -path ./migrations -database 'mysql://root:pass@tcp(127.0.0.1:3306)/test_db?parseTime=true' up
- 迁移文件名必须严格按
000001_create_users_table.up.sql格式,否则migrate无法识别顺序 - CI 中首次运行时,若目标库不存在,得先
mysql -e "CREATE DATABASE test_db",再跑 migrate;否则报错Unknown database 'test_db' - 别在迁移脚本里写
DROP TABLE IF EXISTS—— 这会清空测试数据,导致后续集成测试断言失败
敏感配置和连接信息如何安全注入
CI 流水线里绝不能出现明文密码,但 MySQL 客户端又必须传认证参数。最佳实践是用配置文件 + 文件权限控制,而非环境变量裸露。
例如生成临时 ~/.my.cnf:
cat > ~/.my.cnf <p>之后所有 <code>mysql</code>、<code>mysqldump</code> 命令自动读取该文件,无需再拼接 <code>-u -p</code> 参数。</p>
- GitHub Actions 中
$$ROOT_PASS应设为secrets.MYSQL_ROOT_PASSWORD,并在 step 中用env:注入 - GitLab CI 的
variables:不加密,必须用file_variables:或before_script:动态写入配置文件 - 若用
mysql_config_editor,注意其生成的~/.mylogin.cnf权限必须是600,否则 MySQL 客户端会拒绝读取
真正容易被忽略的是时区和字符集一致性:CI 容器默认 UTC,而本地开发机可能是 CST,datetime 字段插入值可能偏移 8 小时;务必在 Docker Compose 的 environment 中加 TZ=UTC 和 MYSQL_TIME_ZONE=UTC,并在建库语句里显式指定 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci。











