文章目录
-
- [一、核心认知:Docker Compose 与 CI/CD 集成的价值](#一、核心认知:Docker Compose 与 CI/CD 集成的价值)
-
- [1. 集成原理](#1. 集成原理)
- [2. 核心优势](#2. 核心优势)
- [3. 常用 CI/CD 工具对比(适配 Docker Compose)](#3. 常用 CI/CD 工具对比(适配 Docker Compose))
- [二、前置准备:CI/CD 集成必备条件](#二、前置准备:CI/CD 集成必备条件)
- [三、实操案例:三大主流 CI/CD 工具集成 Docker Compose](#三、实操案例:三大主流 CI/CD 工具集成 Docker Compose)
-
- [案例 1:GitHub Actions + Docker Compose 集成(开源项目首选)](#案例 1:GitHub Actions + Docker Compose 集成(开源项目首选))
-
- [场景:Spring Boot + MySQL 项目,实现 "代码提交→自动构建→测试→部署到测试环境"](#场景:Spring Boot + MySQL 项目,实现 “代码提交→自动构建→测试→部署到测试环境”)
- 前置条件:
- [步骤 1:项目结构(核心文件)](#步骤 1:项目结构(核心文件))
- [步骤 2:编写`docker-compose.yml`(支持多环境)](#步骤 2:编写
docker-compose.yml(支持多环境)) - [步骤 3:编写 GitHub Actions 流水线配置(`.github/workflows/cicd.yml`)](#步骤 3:编写 GitHub Actions 流水线配置(
.github/workflows/cicd.yml)) - [步骤 4:配置 GitHub Secrets(敏感信息)](#步骤 4:配置 GitHub Secrets(敏感信息))
- [步骤 5:触发流水线](#步骤 5:触发流水线)
- [案例 2:GitLab CI + Docker Compose 集成(企业内部项目)](#案例 2:GitLab CI + Docker Compose 集成(企业内部项目))
-
- [场景:Node.js + Redis 项目,实现 "代码提交→构建→测试→部署到生产环境"](#场景:Node.js + Redis 项目,实现 “代码提交→构建→测试→部署到生产环境”)
- [步骤 1:编写`.gitlab-ci.yml`(GitLab CI 配置文件)](#步骤 1:编写
.gitlab-ci.yml(GitLab CI 配置文件)) - [步骤 2:配置 GitLab CI 密钥](#步骤 2:配置 GitLab CI 密钥)
- [步骤 3:配置 GitLab Runner](#步骤 3:配置 GitLab Runner)
- [案例 3:Jenkins + Docker Compose 集成(复杂流水线)](#案例 3:Jenkins + Docker Compose 集成(复杂流水线))
-
- [场景:Java + MySQL + Redis 项目,实现 "多环境部署 + 滚动更新 + 回滚"](#场景:Java + MySQL + Redis 项目,实现 “多环境部署 + 滚动更新 + 回滚”)
- [步骤 1:Jenkins 流水线配置(Pipeline Script)](#步骤 1:Jenkins 流水线配置(Pipeline Script))
- [步骤 2:Jenkins 前置配置](#步骤 2:Jenkins 前置配置)
- [四、CI/CD 集成关键技巧与避坑指南](#四、CI/CD 集成关键技巧与避坑指南)
-
- [1. 多环境隔离与切换](#1. 多环境隔离与切换)
- [2. 敏感信息处理(避免明文暴露)](#2. 敏感信息处理(避免明文暴露))
- [3. 测试自动化技巧](#3. 测试自动化技巧)
- [4. 常见问题与解决方案](#4. 常见问题与解决方案)
- [五、生产环境 CI/CD 集成最佳实践](#五、生产环境 CI/CD 集成最佳实践)
一、核心认知:Docker Compose 与 CI/CD 集成的价值
1. 集成原理
CI/CD(持续集成 / 持续部署)的核心是 "自动化流水线",Docker Compose 作为多容器编排工具,在流水线中承担 "环境模拟 + 服务部署" 角色:
-
持续集成(CI):代码提交后,自动构建镜像、启动多容器测试环境(如应用 + 数据库 + 缓存),执行单元测试 / 集成测试,验证代码可用性;
-
持续部署(CD):测试通过后,自动将镜像部署到目标环境(测试 / 生产),通过 Docker Compose 一键启动服务,支持滚动更新与回滚。
2. 核心优势
-
环境一致性 :开发、测试、生产环境均基于同一
docker-compose.yml配置,避免 "开发能跑、测试报错"; -
部署简化 :无需手动执行
docker compose up/down,流水线自动完成服务启停、配置更新; -
多服务协同:自动化处理应用与依赖服务(数据库、缓存)的启动顺序、网络配置,无需人工干预;
-
可扩展性:支持多环境(dev/test/prod)、多版本并行部署,适配从小规模到中大型项目的需求。
3. 常用 CI/CD 工具对比(适配 Docker Compose)
| 工具 | 优势 | 适用场景 | 集成难度 |
|---|---|---|---|
| GitHub Actions | 与 GitHub 代码仓库深度集成、配置简单、免费额度充足 | 开源项目、GitHub 托管代码 | ★★☆ |
| GitLab CI | 与 GitLab 无缝对接、支持私有仓库、功能全面 | 企业内部项目、GitLab 托管 | ★★☆ |
| Jenkins | 高度可定制、插件丰富、支持复杂流水线 | 大型企业、复杂部署场景 | ★★★ |
| GitLab CI | 与 GitLab 无缝对接、支持私有仓库、功能全面 | 企业内部项目、GitLab 托管 | ★★☆ |
二、前置准备:CI/CD 集成必备条件
在开始集成前,需完成以下基础配置:
-
代码仓库 :将项目代码(含
docker-compose.yml、Dockerfile、测试脚本)托管到 Git 仓库(GitHub/GitLab); -
镜像仓库:准备 Docker 镜像仓库(Docker Hub / 私有仓库 / Harbor),用于存储 CI 构建的镜像;
-
目标服务器:部署目标环境(测试 / 生产)的服务器需安装 Docker、Docker Compose,且 CI/CD 工具能通过 SSH 访问;
-
敏感信息管理:通过 CI/CD 工具的 "密钥管理" 存储敏感信息(如镜像仓库账号、数据库密码、服务器 SSH 密钥),避免明文暴露。
三、实操案例:三大主流 CI/CD 工具集成 Docker Compose
案例 1:GitHub Actions + Docker Compose 集成(开源项目首选)
场景:Spring Boot + MySQL 项目,实现 "代码提交→自动构建→测试→部署到测试环境"
前置条件:
-
代码仓库:GitHub 托管(含
docker-compose.yml、Dockerfile、pom.xml); -
镜像仓库:Docker Hub(需在 GitHub 配置镜像仓库账号密码);
-
目标服务器:测试环境服务器(需配置 SSH 密钥,允许 GitHub Actions 访问)。
步骤 1:项目结构(核心文件)
my-project/
├── docker-compose.yml # 多服务配置(应用+MySQL)
├── docker-compose.test.yml # 测试环境专属配置(可选)
├── Dockerfile # 应用镜像构建文件
├── src/ # 源代码
├── pom.xml # Maven依赖(Java项目)
└── .github/workflows/cicd.yml # GitHub Actions流水线配置
步骤 2:编写docker-compose.yml(支持多环境)
version: '3.8'
services:
app:
image: ${DOCKER_REGISTRY}/my-app:${IMAGE_TAG} # 镜像名从环境变量获取(CI动态注入)
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
- SPRING_DATASOURCE_USERNAME=${MYSQL_USER}
- SPRING_DATASOURCE_PASSWORD=${MYSQL_PASS}
depends_on:
mysql:
condition: service_healthy
networks:
- app-network
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_PASS}
- MYSQL_DATABASE=mydb
- MYSQL_USER=${MYSQL_USER}
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 3
networks:
- app-network
networks:
app-network:
driver: bridge
步骤 3:编写 GitHub Actions 流水线配置(.github/workflows/cicd.yml)
name: Docker Compose CI/CD Pipeline
# 触发条件:main分支提交代码(或手动触发)
on:
push:
branches: [ main ]
workflow_dispatch: # 允许手动触发
# 环境变量(全局配置)
env:
DOCKER_REGISTRY: docker.io # 镜像仓库(Docker Hub)
IMAGE_NAME: my-app # 镜像名
IMAGE_TAG: ${{ github.sha }}# 镜像标签(用Git提交SHA值,确保唯一)
TARGET_SERVER: 192.168.1.100 # 测试环境服务器IP
TARGET_DIR: /opt/my-project # 服务器项目目录
# 流水线任务
jobs:
# 任务1:构建镜像并推送到镜像仓库
build-and-push:
runs-on: ubuntu-latest # 运行在Ubuntu环境
steps:
# 步骤1:拉取代码
- name: Checkout code
uses: actions/checkout@v4
# 步骤2:登录Docker Hub(使用GitHub Secrets存储账号密码)
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
# 步骤3:构建镜像(基于Dockerfile)
- name: Build Docker image
run: |
docker build -t ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG} .
# 步骤4:推送镜像到仓库
- name: Push Docker image
run: |
docker push ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG}
# 任务2:在测试环境部署(依赖build-and-push任务成功)
deploy-test:
needs: build-and-push # 依赖前一个任务完成
runs-on: ubuntu-latest
steps:
# 步骤1:拉取代码(获取docker-compose.yml)
- name: Checkout code
uses: actions/checkout@v4
# 步骤2:通过SSH连接目标服务器,部署服务
- name: Deploy to test server
uses: appleboy/ssh-action@master # SSH工具插件
with:
host: ${{ env.TARGET_SERVER }}
username: ${{ secrets.SSH_USERNAME }} # 服务器用户名(GitHub Secrets存储)
key: ${{ secrets.SSH_PRIVATE_KEY }} # 服务器SSH私钥(GitHub Secrets存储)
script: |
# 进入项目目录(若不存在则创建)
mkdir -p ${TARGET_DIR} && cd ${TARGET_DIR}
# 拉取最新代码(确保docker-compose.yml是最新的)
git pull origin main
# 导出环境变量(镜像标签、数据库密码等)
export DOCKER_REGISTRY=${{ env.DOCKER_REGISTRY }}
export IMAGE_TAG=${{ env.IMAGE_TAG }}
export MYSQL_USER=${{ secrets.MYSQL_USER }}
export MYSQL_PASS=${{ secrets.MYSQL_PASS }}
# 停止旧服务(保留数据卷),启动新服务
docker compose down
docker compose up -d --pull always # 拉取最新镜像并启动
# 查看服务状态,确保部署成功
docker compose ps
步骤 4:配置 GitHub Secrets(敏感信息)
在 GitHub 仓库 → Settings → Secrets and variables → Actions → New repository secret,添加以下密钥:
-
DOCKER_HUB_USERNAME:Docker Hub 用户名; -
DOCKER_HUB_TOKEN:Docker Hub 访问令牌(在 Docker Hub → Account Settings → Security 生成); -
SSH_USERNAME:测试服务器用户名(如 ubuntu); -
SSH_PRIVATE_KEY:测试服务器 SSH 私钥(本地生成后复制); -
MYSQL_USER:数据库用户名; -
MYSQL_PASS:数据库密码。
步骤 5:触发流水线
-
自动触发:向
main分支提交代码,GitHub Actions 自动执行流水线; -
手动触发:在 GitHub 仓库 → Actions → 选择流水线 → Run workflow。
案例 2:GitLab CI + Docker Compose 集成(企业内部项目)
场景:Node.js + Redis 项目,实现 "代码提交→构建→测试→部署到生产环境"
步骤 1:编写.gitlab-ci.yml(GitLab CI 配置文件)
# 定义流水线阶段(顺序执行)
stages:
- build # 构建镜像
- test # 启动服务执行测试
- push # 推送镜像到私有仓库
- deploy-prod # 部署到生产环境
# 全局变量(所有阶段共享)
variables:
DOCKER_REGISTRY: 192.168.1.200:5000 # 私有镜像仓库地址
IMAGE_NAME: node-app
IMAGE_TAG: $CI_COMMIT_SHA # Git提交SHA值作为镜像标签
TARGET_DIR: /opt/node-project # 生产服务器项目目录
# 阶段1:构建镜像(基于Dockerfile)
build-image:
stage: build
tags:
- docker # 运行在支持Docker的GitLab Runner上
script:
- docker build -t ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG} .
# 阶段2:启动服务执行测试(使用Docker Compose)
test-service:
stage: test
tags:
- docker
services:
- docker:20.10-dind # 启用Docker-in-Docker(允许Runner内启动容器)
script:
# 导出环境变量,启动应用+Redis服务
- export DOCKER_REGISTRY=${DOCKER_REGISTRY}
- export IMAGE_TAG=${IMAGE_TAG}
- docker compose up -d
# 等待服务就绪(执行健康检查)
- docker compose exec -T app npm test # 执行Node.js测试脚本
# 测试完成后停止服务
- docker compose down
# 阶段3:推送镜像到私有仓库
push-image:
stage: push
tags:
- docker
script:
# 登录私有镜像仓库(GitLab CI密钥管理中配置的凭证)
- docker login -u $REGISTRY_USER -p $REGISTRY_PASS ${DOCKER_REGISTRY}
- docker push ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG}
# 可选:打latest标签(生产环境默认拉取latest)
- docker tag ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG} ${DOCKER_REGISTRY}${IMAGE_NAME}:latest
- docker push ${DOCKER_REGISTRY}${IMAGE_NAME}:latest
# 阶段4:部署到生产环境(通过SSH)
deploy-prod:
stage: deploy-prod
tags:
- shell # 运行在支持SSH的Runner上
only:
- main # 仅main分支触发生产部署
script:
# 连接生产服务器,部署服务
- ssh $PROD_USER@$PROD_HOST "cd ${TARGET_DIR} && git pull origin main"
- ssh $PROD_USER@$PROD_HOST "cd ${TARGET_DIR} && export DOCKER_REGISTRY=${DOCKER_REGISTRY} && export IMAGE_TAG=${IMAGE_TAG} && docker compose down && docker compose up -d"
步骤 2:配置 GitLab CI 密钥
在 GitLab 仓库 → Settings → CI/CD → Variables,添加以下变量(保护敏感信息):
-
REGISTRY_USER:私有镜像仓库用户名; -
REGISTRY_PASS:私有镜像仓库密码; -
PROD_HOST:生产服务器 IP; -
PROD_USER:生产服务器用户名; -
SSH_PRIVATE_KEY:生产服务器 SSH 私钥(需在 Runner 中配置 SSH 免密登录)。
步骤 3:配置 GitLab Runner
需确保 GitLab Runner 支持:
-
Docker 环境(用于构建镜像、启动容器);
-
Docker-in-Docker(DinD):在 Runner 配置中启用
privileged = true,支持在 Runner 内启动 Docker 容器; -
SSH 客户端:用于连接生产服务器部署。
案例 3:Jenkins + Docker Compose 集成(复杂流水线)
场景:Java + MySQL + Redis 项目,实现 "多环境部署 + 滚动更新 + 回滚"
步骤 1:Jenkins 流水线配置(Pipeline Script)
pipeline {
agent any # 运行在任意Jenkins代理节点
environment {
// 环境变量(可通过Jenkins全局配置注入)
DOCKER_REGISTRY = 'harbor.example.com'
IMAGE_NAME = 'java-app'
IMAGE_TAG = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.substring(0,8)}" // 构建号+Git短SHA
TEST_SERVER = '192.168.1.100'
PROD_SERVER = '192.168.1.101'
}
stages {
// 阶段1:拉取代码
stage('Checkout') {
steps {
git url: 'https://gitlab.example.com/my-project.git', branch: 'main'
}
}
// 阶段2:构建镜像
stage('Build Image') {
steps {
sh "docker build -t ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG} ."
}
}
// 阶段3:测试环境部署与测试
stage('Deploy Test') {
steps {
// SSH部署到测试服务器
sshPublisher(publishers: [sshPublisherDesc(
configName: 'Test-Server', // Jenkins配置的SSH服务器名称
transfers: [sshTransfer(
sourceFiles: 'docker-compose.yml',
remoteDirectory: '/opt/test-project'
)],
execCommands: [
"cd /opt/test-project",
"export DOCKER_REGISTRY=${DOCKER_REGISTRY}",
"export IMAGE_TAG=${IMAGE_TAG}",
"docker compose down",
"docker compose up -d",
"docker compose exec -T app mvn test" // 执行Java测试
]
)])
}
}
// 阶段4:推送镜像到Harbor
stage('Push Image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'harbor-cred', // Jenkins存储的Harbor凭证
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
)]) {
sh "docker login -u ${HARBOR_USER} -p ${HARBOR_PASS} ${DOCKER_REGISTRY}"
sh "docker push ${DOCKER_REGISTRY}${IMAGE_NAME}:${IMAGE_TAG}"
}
}
}
// 阶段5:生产环境滚动更新
stage('Deploy Production') {
when {
branch 'main' // 仅main分支触发生产部署
}
steps {
sshPublisher(publishers: [sshPublisherDesc(
configName: 'Prod-Server',
transfers: [sshTransfer(
sourceFiles: 'docker-compose.yml',
remoteDirectory: '/opt/prod-project'
)],
execCommands: [
"cd /opt/prod-project",
"export DOCKER_REGISTRY=${DOCKER_REGISTRY}",
"export IMAGE_TAG=${IMAGE_TAG}",
// 滚动更新(先启动新容器,再停止旧容器)
"docker compose up -d --scale app=2 --no-recreate", // 启动2个新实例
"sleep 10", // 等待新实例就绪
"docker compose down --remove-orphans" // 停止旧实例
]
)])
}
}
}
// 失败回滚(生产部署失败时执行)
post {
failure {
when {
stage('Deploy Production') // 仅生产部署阶段失败时回滚
}
steps {
sshPublisher(publishers: [sshPublisherDesc(
configName: 'Prod-Server',
execCommands: [
"cd /opt/prod-project",
"export IMAGE_TAG=${env.PREVIOUS_IMAGE_TAG}", // 回滚到上一版本
"docker compose down",
"docker compose up -d"
]
)])
}
}
}
}
步骤 2:Jenkins 前置配置
-
安装插件:Docker、Docker Compose、SSH Pipeline Steps、Git Plugin;
-
配置凭证:在 Jenkins → 凭证 → 系统 → 全局凭证,添加镜像仓库账号、服务器 SSH 密钥;
-
配置 SSH 服务器:在 Jenkins → 系统 → 系统配置 → SSH Servers,添加测试 / 生产服务器信息。
四、CI/CD 集成关键技巧与避坑指南
1. 多环境隔离与切换
利用 Docker Compose 的Profiles或多配置文件实现多环境部署:
# docker-compose.yml(基础配置)
version: '3.8'
services:
app:
image: ${DOCKER_REGISTRY}/my-app:${IMAGE_TAG}
ports:
- "8080:8080"
depends_on:
- db
profiles: ["dev", "test", "prod"] # 所有环境共享
db:
image: mysql:8.0
volumes:
- mysql-data:/var/lib/mysql
profiles: ["dev", "test", "prod"]
# 开发环境特有服务:phpMyAdmin
phpmyadmin:
image: phpmyadmin/phpmyadmin
ports:
- "8081:80"
profiles: ["dev"]
volumes:
mysql-data:
-
CI/CD 流水线中通过
--profile指定环境:开发环境部署
docker compose --profile dev up -d
生产环境部署(不含phpMyAdmin)
docker compose --profile prod up -d
2. 敏感信息处理(避免明文暴露)
- 不要在
docker-compose.yml中写死密码、密钥,通过以下方式注入:
-
CI/CD 工具的 "密钥管理"(如 GitHub Secrets、GitLab Variables、Jenkins 凭证);
-
环境变量文件(
.env),在流水线中动态生成.env文件并挂载到容器; -
第三方密钥管理工具(如 Vault),在容器启动时动态获取密钥。
3. 测试自动化技巧
-
集成测试:通过
docker compose exec在容器内执行测试脚本(如npm test、mvn test); -
健康检查:配置
healthcheck确保服务就绪后再执行测试,避免 "服务未启动就测试"; -
测试后清理:测试完成后执行
docker compose down -v,清理容器和数据卷,避免占用资源。
4. 常见问题与解决方案
| 问题现象 | 排查方向 | 解决方案 |
|---|---|---|
| CI/CD Runner 无法启动 Docker 容器 | 1. Runner 未启用 Docker;2. 无 Docker-in-Docker 权限 | 1. 配置 Runner 支持 Docker;2. 启用 DinD(privileged: true) |
| 镜像推送失败 | 1. 镜像仓库地址错误;2. 登录凭证无效;3. 网络不通 | 1. 验证仓库地址;2. 检查 CI/CD 密钥配置;3. 测试 Runner 与仓库网络连通性 |
| 目标服务器部署时权限不足 | 1. SSH 用户无 Docker 权限;2. 目录权限不够 | 1. 将 SSH 用户加入 docker 组(usermod -aG docker $USER);2. 授权目录权限(chmod 777 $TARGET_DIR) |
| 服务启动顺序问题导致部署失败 | 1. 未配置depends_on: condition: service_healthy;2. 无等待脚本 |
1. 配置健康检查;2. 在流水线中添加sleep或wait-for-it脚本 |
五、生产环境 CI/CD 集成最佳实践
- 镜像版本管理:
-
镜像标签使用 "构建号 + Git 提交 SHA"(如
v1.0.0-abc123),避免latest标签导致版本混乱; -
推送镜像时同时打 "环境标签"(如
dev-abc123、prod-abc123),便于回滚。
- 流水线阶段划分:
-
拆分 "构建→测试→推送→部署" 独立阶段,前一阶段失败则终止流水线,避免无效部署;
-
生产部署前添加 "人工审批"(如 Jenkins 的
input步骤、GitLab CI 的when: manual),防止误部署。
- 部署策略:
-
小规模部署:使用
docker compose up -d --force-recreate(停止旧容器→启动新容器); -
中大规模部署:使用滚动更新(
--scale+ 逐步停止旧容器)或蓝绿部署(同时启动新旧版本,切换流量后停止旧版本)。
- 日志与监控:
-
流水线中添加日志收集(如
docker compose logs -f输出到 CI/CD 工具日志); -
部署后自动检查服务状态(
docker compose ps、curl接口健康检查),异常时触发告警。
- 回滚机制:
-
保存每次部署的镜像标签(如存储到数据库、配置文件),部署失败时快速回滚到上一版本;
-
回滚命令:
export IMAGE_TAG=上一版本标签 && docker compose down && docker compose up -d。