CI/CD基础与Docker Compose集成方式归纳总结
说到 CI/CD 和 Docker Compose 的集成,我琢磨了很久才找到一个舒服的套路。说白了,Compose 在 CI 里就是帮你快速拉起一套多服务环境。你想想看,本地开发时用 Compose 跑一套微服务多方便。可到了 CI 里,很多团队却把它当成黑盒一把梭——结果快照回归出错时一脸懵。
我个人习惯把集成方式分成三类:原生命令式、CI Runner 插件式、自建编排脚本式。下面这张图能帮你看清整体脉络:
核心观点:CI 里用 Docker Compose 不是为了炫技,而是为了环境一致和多服务编排的可重复性。快照回归错误大多数原因就出在「环境残留」和「服务依赖时机」上。
1. 原生命令式集成 — 简单直接
这是最原始的方式。直接在 CI 任务的 shell 里写:
docker-compose up -d
# 等待服务就绪
docker-compose exec web python manage.py test
# 跑完清理
docker-compose down --volumes
记得有一次,我在 Jenkins 里这么写,结果快照回归总失败。查了半天才发现——docker-compose down 默认不清理匿名卷,上个 job 的数据库残留把快照污染了。从那以后,我习惯强制加 --volumes(或者 -v)。
这种方式很透明,但缺点明显:
- 需要 CI Runner 节点上预装 Docker Compose
- 版本要统一,避免与 Compose 文件不兼容
- 每次 job 都要重复拉镜像、建网络,慢
2. CI Runner 插件式集成
GitLab CI、Drone、CircleCI 这些平台提供了官方或社区插件。比如 GitLab 的 services: docker:dind 加上 compose 配置。我建议只在快照回归场景中用它,因为插件对底层网络、卷的控制比较有限。
| 集成方式 | 优点 | 我遇到过的坑 |
|---|---|---|
| 原生命令式 | 可控、调试方便 | 镜像预拉取太慢,建议在 `before_script` 用 `pull` 提前拉 |
| CI 插件式 | 安装简单、日志集成好 | 快照回滚时插件不会自动清理 volumes,要自己写脚本补丁 |
| 自建编排脚本 | 完全自主,可加入重试、健康检查轮询 | 脚本里忘了处理 `docker-compose down --remove-orphans`,服务残留搞乱下一个 job |
⚠️ 无论选哪种方式,快照回归测试必须在独立的 Docker 网络里运行。我曾经在共享默认网络中跑,结果两个并发 job 的容器端口冲突,快照全错。
3. 自建编排脚本 — 灵活但麻烦
当 Compose 文件超过两层依赖时,我用 Python 脚本或者 Makefile 来封装。核心是做到:
- 启动前清场:先
docker-compose down -v干掉所有旧容器和卷 - 等待健康:用
docker-compose ps或内置 healthcheck 轮询,直到所有服务状态为 healthy - 执行快照操作:在环境稳定后打快照(比如 dump 数据库或保存容器状态)
- 回归测试:运行测试,对比快照
- 回滚保证:如果测试失败,自动
docker-compose restart并从快照恢复
这里贴一段我在 GitLab CI 里常用的脚本片段:
#!/bin/bash
set -euo pipefail
SNAPSHOT_DIR="/tmp/snapshots/${CI_JOB_ID}"
echo "📦 清理环境"
docker-compose down -v 2>/dev/null || true
echo "🚀 启动服务"
docker-compose up -d --build
echo "⏳ 等待所有服务健康"
until docker-compose exec -T db pg_isready -U test; do sleep 2; done
echo "💾 保存快照"
mkdir -p $SNAPSHOT_DIR
docker exec -t $(docker ps -q --filter name=db) pg_dump -U test > $SNAPSHOT_DIR/db.sql
echo "🧪 运行回归测试"
docker-compose exec -T web pytest --snapshot-update
if [ $? -ne 0 ]; then
echo "❌ 回归失败,恢复快照"
docker-compose down -v
docker-compose up -d --build
sleep 5
docker exec -i $(docker ps -q --filter name=db) psql -U test < $SNAPSHOT_DIR/db.sql
exit 1
fi
echo "✅ 回归通过,清理"
rm -rf $SNAPSHOT_DIR
docker-compose down -v
你想想看,这段脚本核心就是「先快照、再测试、不行就回滚」。但这里有个隐患——如果 docker-compose exectimeout。
4. 集成时最容易踩的三个坑
- 环境变量泄漏:Compose 会把宿主机的环境变量透传给容器,CI 里可能把密钥带进去。我建议用
.env文件加--env-file严格管控。 - 服务启动顺序依赖:
depends_on只能保证容器启动了,不保证服务可用。快照回归测试需要真正的 healthcheck,否则在数据库还未响应时打快照,基线就歪了。 - 并发 job 冲突:多个 job 在同一台 Runner 上跑时,容器名/端口会冲突。解决方案:用
${CI_JOB_ID}作为 Compose 项目名(docker-compose -p snap-${CI_JOB_ID} up)。
💡 一个小技巧:在快照回归中,我会把基线节点(比如数据库)的状态编码成文件 md5 值。如果回归后 md5 没变,就说明容器状态一致——这比单纯看 exit code 更可靠。
好了,以上就是我对 CI/CD 与 Docker Compose 集成方式的总结。无论你选哪种方式,记住一条原则:快照回归的核心是「隔离 + 可重复」。环境不干净,再花哨的 CI 也救不了你。