引言:为什么需要CI/CD?——从“手动发布”到“自动交付”
在现代软件开发中,快速迭代、持续交付已成为企业竞争力的核心要素。然而,在传统的开发流程中,代码提交后往往需要人工干预完成构建、测试、打包、部署等环节,不仅效率低下,还容易因人为失误导致生产环境故障。
问题示例:某团队每周发布一次版本,每次发布前需手动执行以下操作:
- 拉取最新代码
- 手动编译项目
- 执行单元测试与集成测试
- 构建 Docker 镜像
- 上传镜像至私有仓库
- 登录服务器,停止旧服务,启动新容器
- 验证服务是否正常运行
整个过程耗时约1.5小时,且一旦出错,排查成本极高。
为解决上述痛点,CI/CD(持续集成 / 持续部署) 成为现代 DevOps 实践的基石。它通过自动化流水线将代码变更快速、安全地交付到生产环境。
本文将以一个典型的 Web 应用项目为例,深入讲解如何基于 Docker 容器化技术、Jenkins 自动化引擎 和 GitLab CI/CD 平台,构建一套完整、可复用、高可用的自动化部署体系。我们将覆盖从代码提交到生产上线的全流程,并提供真实可运行的配置示例和最佳实践建议。
一、架构设计:整体系统拓扑与组件职责
1.1 整体架构图(文字描述)
[开发者] → [GitLab 仓库] → [GitLab CI/CD Pipeline]
↓
[Jenkins CI/CD Server]
↓
[Docker Registry (e.g., Harbor)]
↓
[Kubernetes / Docker Compose / VM] → [Production]
1.2 核心组件说明
| 组件 | 职责 |
|---|---|
| GitLab | 代码托管平台 + 内置 CI/CD 引擎,用于触发流水线、管理变量、存储日志 |
| Jenkins | 可扩展的 CI/CD 引擎,负责执行复杂任务(如多阶段构建、审批流、灰度发布) |
| Docker | 应用容器化工具,将应用及其依赖打包成标准化镜像 |
| Docker Registry | 私有镜像仓库(如 Harbor、ECR、ACR),用于存储和分发镜像 |
| 目标部署环境 | Kubernetes 集群、虚拟机或 Docker Compose 环境,承载最终运行的服务 |
✅ 架构优势:
- 解耦性好:各组件独立部署,便于维护与扩展。
- 灵活性强:可按需选择使用 GitLab CI 或 Jenkins,甚至两者结合。
- 安全性高:敏感信息(如密钥)可通过 Jenkins Credentials Store 管理。
二、前置准备:环境搭建与权限配置
2.1 服务器资源规划
建议至少三台服务器:
| 服务器角色 | 推荐配置 | 用途 |
|---|---|---|
| GitLab Server | 4vCPU, 8GB RAM, 100GB SSD | 代码托管与 CI/CD 触发 |
| Jenkins Server | 8vCPU, 16GB RAM, 200GB SSD | CI/CD 主控节点 |
| Docker Registry Server | 4vCPU, 8GB RAM, 500GB SSD | 存储镜像 |
| Target Deployment Host(s) | 2vCPU, 4GB RAM, 100GB SSD | 生产运行环境 |
💡 建议使用 Linux(Ubuntu 22.04 LTS / CentOS 7+)作为操作系统。
2.2 安装与配置核心组件
2.2.1 安装 GitLab(Omnibus 版本)
# Ubuntu 22.04
sudo apt update && sudo apt install -y curl openssh-server ca-certificates
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
sudo apt install -y gitlab-ee
# 启动并配置
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
访问 http://<your-gitlab-ip>,设置管理员密码并初始化。
2.2.2 安装 Jenkins
# 添加 Jenkins GPG Key
wget -qO - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -
# 添加源
echo deb https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list
# 安装
sudo apt update
sudo apt install -y jenkins
# 启动
sudo systemctl enable jenkins
sudo systemctl start jenkins
默认端口为 8080,首次启动需通过 /var/lib/jenkins/secrets/initialAdminPassword 获取初始密码。
2.2.3 部署 Harbor(私有 Docker Registry)
# 安装 Helm
curl -fsSL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
# 添加 Harbor Chart
helm repo add harbor https://helm.goharbor.io
helm repo update
# 部署 Harbor(以单机模式)
helm install harbor harbor/harbor \
--namespace harbor \
--create-namespace \
--set expose.type=ingress \
--set expose.ingress.hosts.core=harbor.example.com \
--set hostname=harbor.example.com \
--set credentials.adminPassword="Harbor123!" \
--set externalURL=https://harbor.example.com
⚠️ 注意:需提前配置 DNS 将
harbor.example.com解析到该服务器 IP。
三、项目结构与基础配置文件
假设我们有一个简单的 Node.js Express 项目,目录结构如下:
my-web-app/
├── .gitlab-ci.yml # GitLab CI 配置文件
├── Jenkinsfile # Jenkins 流水线定义(可选)
├── docker-compose.yml # 本地测试部署
├── Dockerfile # 容器化构建脚本
├── app.js # 主应用逻辑
├── package.json # 依赖清单
└── test/
└── unit.test.js # 单元测试
3.1 Dockerfile(关键:构建可移植镜像)
# Dockerfile
FROM node:18-alpine AS base
WORKDIR /app
# 复制 package.json
COPY package*.json ./
# 安装依赖(减少缓存失效)
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "app.js"]
✅ 最佳实践:
- 使用
npm ci而非npm install,确保一致性。- 使用 Alpine 镜像减小体积。
- 分层构建,利用缓存机制提升构建速度。
3.2 docker-compose.yml(本地测试环境)
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
volumes:
- ./logs:/app/logs
restart: unless-stopped
启动方式:
docker-compose up -d
访问 http://localhost:3000 即可验证服务正常。
四、GitLab CI/CD 流水线配置详解
4.1 .gitlab-ci.yml 示例(轻量级流水线)
stages:
- build
- test
- push-image
- deploy
variables:
IMAGE_NAME: registry.harbor.example.com/myapp/web-app
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
# 构建阶段:编译并生成镜像
build:
stage: build
image: docker:20.10.13
services:
- docker:dind
script:
- docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
- docker login -u $DOCKER_USER -p $DOCKER_PASSWORD registry.harbor.example.com
only:
- main
cache:
key: docker-cache
paths:
- ~/.npm
policy: pull-push
# 测试阶段:运行单元测试
test:
stage: test
image: node:18-alpine
script:
- npm install
- npm run test
artifacts:
reports:
junit: test/results.xml
allow_failure: false
# 推送镜像到私有仓库
push-image:
stage: push-image
image: docker:20.10.13
services:
- docker:dind
script:
- docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
- docker push ${IMAGE_NAME}:${IMAGE_TAG}
- docker push ${IMAGE_NAME}:latest
only:
- main
environment: production
when: manual # 手动触发,防止误推
# 部署到生产环境(通过 SSH)
deploy-production:
stage: deploy
image: alpine:latest
script:
- apk add --no-cache openssh-client
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh -o StrictHostKeyChecking=no user@prod-server << 'EOF'
cd /opt/web-app
docker stop web || true
docker rm web || true
docker rmi registry.harbor.example.com/myapp/web-app:${CI_COMMIT_SHORT_SHA} || true
docker pull registry.harbor.example.com/myapp/web-app:${CI_COMMIT_SHORT_SHA}
docker run -d --name web \
-p 3000:3000 \
-e NODE_ENV=production \
registry.harbor.example.com/myapp/web-app:${CI_COMMIT_SHORT_SHA}
EOF
only:
- main
environment: production
when: manual
4.2 GitLab CI 变量管理(安全策略)
在 GitLab 项目 Settings → CI/CD → Variables 中添加以下变量:
| 变量名 | 值 | 类型 |
|---|---|---|
DOCKER_USER |
admin |
Plain text |
DOCKER_PASSWORD |
Harbor123! |
Secret |
SSH_PRIVATE_KEY |
-----BEGIN RSA PRIVATE KEY-----... |
Secret |
PROD_SERVER_IP |
192.168.1.100 |
Plain text |
🔒 重要提醒:
- 所有密码类变量必须设为 Secret。
SSH_PRIVATE_KEY必须是完整的私钥内容(含换行符),不可使用文件路径。- 使用
when: manual控制部署节奏,避免频繁误操作。
五、Jenkins 流水线深度实践(复杂场景支持)
虽然 GitLab CI 已经足够强大,但在复杂场景下(如多环境审批、蓝绿部署、灰度发布),仍推荐使用 Jenkins。
5.1 Jenkinsfile(声明式流水线)
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.harbor.example.com'
APP_NAME = 'web-app'
IMAGE_TAG = "${env.BUILD_ID}"
PROD_HOST = 'user@192.168.1.100'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build & Test') {
steps {
sh 'npm install'
sh 'npm run test:ci' // 生成 JUnit 报告
archiveArtifacts artifacts: 'test/results.xml', pattern: false
}
}
stage('Build Docker Image') {
steps {
script {
def dockerImage = docker.build("${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.IMAGE_TAG}")
dockerImage.push()
}
}
}
stage('Approval - Production Deploy') {
steps {
input message: '确认部署到生产环境?',
ok: 'Deploy Now',
cancel: 'Cancel Deployment'
}
}
stage('Deploy to Production') {
steps {
script {
def cmd = """
ssh -o StrictHostKeyChecking=no ${env.PROD_HOST} '
cd /opt/web-app &&
docker stop web || true &&
docker rm web || true &&
docker rmi ${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.IMAGE_TAG} || true &&
docker pull ${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.IMAGE_TAG} &&
docker run -d --name web \\
-p 3000:3000 \\
-e NODE_ENV=production \\
${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.IMAGE_TAG}
'
"""
sh cmd
}
}
}
}
post {
success {
mail to: 'dev-team@example.com',
subject: "✅ Build Success: ${env.BUILD_ID}",
body: "Application deployed successfully!"
}
failure {
mail to: 'dev-team@example.com',
subject: "❌ Build Failed: ${env.BUILD_ID}",
body: "Check Jenkins logs for details."
}
}
}
5.2 Jenkins 插件推荐
| 插件名称 | 功能说明 |
|---|---|
| Pipeline | 支持 Groovy 编写的声明式流水线 |
| Docker Pipeline | 提供 docker.build, docker.push 等 DSL |
| Credentials Binding | 安全读取用户名/密码 |
| Email Extension | 发送邮件通知 |
| Blue Ocean | 可视化流水线界面 |
安装路径:Jenkins → Manage Plugins → Available Plugins
5.3 Jenkins 安全配置(最佳实践)
- 启用安全认证
- 设置 Jenkins URL 为
https://jenkins.example.com - 启用 LDAP / GitHub OAuth 认证
- 设置 Jenkins URL 为
- 最小权限原则
- 为不同团队分配不同角色(Read, Build, Admin)
- 敏感信息保护
- 使用
CredentialsStore 存储密钥 - 在 Jenkinsfile 中使用
withCredentials绑定凭证
- 使用
withCredentials([usernamePassword(
credentialsId: 'harbor-credentials',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh "docker login -u $DOCKER_USER -p $DOCKER_PASS registry.harbor.example.com"
}
六、高级功能拓展:灰度发布与滚动更新
6.1 灰度发布策略(基于标签路由)
在生产环境中,可采用灰度发布降低风险。例如:
# docker-compose.prod.yml
version: '3.8'
services:
web:
image: registry.harbor.example.com/myapp/web-app:v1.2.0
labels:
- "traefik.http.routers.web.middlewares=gray-middleware@file"
networks:
- app-net
web-gray:
image: registry.harbor.example.com/myapp/web-app:v1.2.0
labels:
- "traefik.http.routers.gray.rule=Headers(`X-User-ID`, `12345`)"
networks:
- app-net
配合 Traefik 反向代理实现用户级灰度分流。
6.2 滚动更新(Rolling Update)配置
# docker-compose.yml (for production)
version: '3.8'
services:
web:
image: registry.harbor.example.com/myapp/web-app:${TAG}
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
order: start-first
rollback_config:
parallelism: 1
delay: 10s
ports:
- "3000:3000"
📌 优点:
- 服务不中断
- 逐步替换实例
- 出现问题可快速回滚
七、监控与日志收集(运维视角)
7.1 日志聚合方案(ELK Stack)
- Filebeat:采集 Jenkins/GitLab/应用日志
- Logstash:解析日志格式
- Elasticsearch:索引存储
- Kibana:可视化分析
部署示例(简化版):
version: '3'
services:
elasticsearch:
image: elasticsearch:8.7.0
environment:
- discovery.type=single-node
volumes:
- esdata:/usr/share/elasticsearch/data
kibana:
image: kibana:8.7.0
depends_on:
- elasticsearch
ports:
- "5601:5601"
filebeat:
image: docker.elastic.co/beats/filebeat:8.7.0
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml
- /var/log:/var/log
command: >
filebeat -c /usr/share/filebeat/filebeat.yml
-e
volumes:
esdata:
7.2 Jenkins 与 GitLab 日志保留策略
| 平台 | 日志保留周期 | 清理策略 |
|---|---|---|
| Jenkins | 30 天 | 通过 Retention Time 配置 |
| GitLab CI | 90 天 | 项目设置中可调整 |
✅ 建议开启日志归档并定期导出备份。
八、总结与最佳实践清单
8.1 关键成功要素
| 要素 | 说明 |
|---|---|
| ✅ 基础设施即代码(IaC) | 使用 Ansible/Terraform 管理服务器配置 |
| ✅ 镜像签名与扫描 | 使用 Trivy、Clair 对镜像进行漏洞扫描 |
| ✅ 流水线版本化 | 将 .gitlab-ci.yml、Jenkinsfile 提交至 Git |
| ✅ 失败自动重试 | 在 Jenkins/GitLab 中配置 retry 机制 |
| ✅ 变更追踪与审计 | 所有部署操作记录在日志中,可追溯 |
8.2 推荐工具链组合
| 场景 | 推荐工具 |
|---|---|
| 轻量级项目 | GitLab CI + Docker + Docker Compose |
| 大型企业 | Jenkins + Harbor + Kubernetes + Argo CD |
| 微服务架构 | GitLab CI + Jenkins Pipeline + Istio + Prometheus |
九、结语:迈向真正的 DevOps 之路
通过本实践,我们实现了从代码提交到生产部署的全流程自动化。这不仅显著提升了交付效率(从数小时缩短至几分钟),更增强了系统的稳定性与可追溯性。
记住一句话:
“自动化不是目的,而是手段;真正的目标是让工程师专注创造价值,而不是重复劳动。”
未来,随着云原生生态的发展,我们可以进一步引入 ArgoCD、Tekton、Flux 等现代化工具,构建更智能、自愈的 CI/CD 系统。
但无论工具如何演进,清晰的流程设计、完善的监控体系、持续的安全意识,永远是 DevOps 成功的基石。
📌 附录:一键部署脚本(Bash)
#!/bin/bash
# deploy-all.sh —— 快速部署整套 CI/CD 环境
# 检查依赖
command -v docker >/dev/null || { echo "Docker not installed"; exit 1; }
# 启动 Harbor
helm install harbor harbor/harbor \
--namespace harbor \
--create-namespace \
--set hostname=harbor.example.com \
--set credentials.adminPassword="Harbor123!"
# 启动 Jenkins
docker run -d --name jenkins \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
echo "✅ All components deployed! Access:"
echo " Jenkins: http://localhost:8080"
echo " Harbor: https://harbor.example.com"
📝 使用前请根据实际网络环境修改域名和密码。
🌐 参考资料
📌 作者:资深 DevOps 工程师
📅 最后更新:2025年4月5日
🔖 标签:DevOps, Docker, Jenkins, GitLab CI, 自动化部署

评论 (0)