第1章:CI/CD流程设计
什么是CI/CD
核心概念
CI(Continuous Integration,持续集成):
开发者频繁地(一天多次)将代码集成到主干分支,
每次集成都通过自动化构建和测试来验证,
从而尽早发现集成错误。
CD(Continuous Delivery,持续交付):
在CI的基础上,确保代码始终处于可部署状态,
可以随时一键部署到生产环境。
CD(Continuous Deployment,持续部署):
在持续交付的基础上,自动将代码部署到生产环境,
无需人工干预。
三者关系
┌──────────────────────────────────────────────────────┐
│ DevOps 全流程 │
└──────────────────────────────────────────────────────┘
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ CI │ → │ CD │ → │ CD │
│持续集成 │ │持续交付 │ │持续部署 │
└─────────┘ └─────────┘ └─────────┘
│ │ │
├─ 代码提交 ├─ 自动化测试 ├─ 自动部署
├─ 自动构建 ├─ 制品发布 ├─ 无人工干预
└─ 单元测试 └─ 一键部署 └─ 快速迭代
传统开发 vs CI/CD
传统开发流程:
┌─────────┐
│开发完成 │ (1-2周)
└─────────┘
↓
┌─────────┐
│手动构建 │ (1-2小时)
└─────────┘
↓
┌─────────┐
│手动测试 │ (1-2天)
└─────────┘
↓
┌─────────┐
│手动部署 │ (半天)
└─────────┘
问题:
反馈周期长(2周后才发现问题)
人工操作易出错
部署效率低
回滚困难
CI/CD流程:
┌─────────┐
│代码提交 │ (实时)
└─────────┘
↓
┌─────────┐
│自动构建 │ (5-10分钟)
└─────────┘
↓
┌─────────┐
│自动测试 │ (10-20分钟)
└─────────┘
↓
┌─────────┐
│自动部署 │ (5分钟)
└─────────┘
优势:
快速反馈(30分钟内发现问题)
自动化,减少人为错误
提高部署频率(每天多次)
一键回滚
CI/CD核心流程
完整流程图
┌──────────────────────────────────────────────────────────┐
│ 开发阶段 │
└──────────────────────────────────────────────────────────┘
↓
┌──────────────────┐
│ 1. 代码提交 │
│ git push │
└──────────────────┘
↓
┌──────────────────────────────────────────────────────────┐
│ CI阶段 │
└──────────────────────────────────────────────────────────┘
↓
┌──────────────────┐
│ 2. 触发构建 │
│ Webhook │
└──────────────────┘
↓
┌──────────────────┐
│ 3. 代码检出 │
│ git clone │
└──────────────────┘
↓
┌──────────────────┐
│ 4. 编译构建 │
│ go build │
└──────────────────┘
↓
┌──────────────────┐
│ 5. 单元测试 │
│ go test │
└──────────────────┘
↓
┌──────────────────┐
│ 6. 代码质量检查 │
│ SonarQube │
└──────────────────┘
↓
┌──────────────────┐
│ 7. 打包制品 │
│ Docker build │
└──────────────────┘
↓
┌──────────────────────────────────────────────────────────┐
│ CD阶段 │
└──────────────────────────────────────────────────────────┘
↓
┌──────────────────┐
│ 8. 推送镜像 │
│ Docker push │
└──────────────────┘
↓
┌──────────────────┐
│ 9. 部署到测试环境│
│ kubectl apply │
└──────────────────┘
↓
┌──────────────────┐
│ 10. 集成测试 │
│ API测试 │
└──────────────────┘
↓
┌──────────────────┐
│ 11. 部署到生产 │
│ kubectl apply │
└──────────────────┘
↓
┌──────────────────┐
│ 12. 健康检查 │
│ Readiness Probe │
└──────────────────┘
Pipeline配置示例
GitLab CI配置(.gitlab-ci.yml):
# .gitlab-ci.yml
stages:
- build # 构建阶段
- test # 测试阶段
- package # 打包阶段
- deploy # 部署阶段
variables:
IMAGE_NAME: myapp
DOCKER_REGISTRY: harbor.example.com
# 1. 构建阶段
build:
stage: build
image: golang:1.21
script:
- go mod download
- go build -o bin/myapp ./cmd/myapp
artifacts:
paths:
- bin/myapp
expire_in: 1 hour
only:
- main
- develop
# 2. 单元测试
unit-test:
stage: test
image: golang:1.21
script:
- go test -v -cover ./...
coverage: '/coverage: \d+.\d+% of statements/'
only:
- main
- develop
# 3. 代码质量检查
code-quality:
stage: test
image: sonarsource/sonar-scanner-cli:latest
script:
- sonar-scanner
-Dsonar.projectKey=myapp
-Dsonar.sources=.
-Dsonar.host.url=$SONAR_HOST_URL
-Dsonar.login=$SONAR_TOKEN
only:
- main
- develop
# 4. 构建Docker镜像
docker-build:
stage: package
image: docker:latest
services:
- docker:dind
script:
- docker login $DOCKER_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
- docker build -t $DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA .
- docker tag $DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA $DOCKER_REGISTRY/$IMAGE_NAME:latest
- docker push $DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA
- docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
only:
- main
# 5. 部署到测试环境
deploy-test:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA -n test
- kubectl rollout status deployment/myapp -n test
environment:
name: test
url: https://test.example.com
only:
- develop
# 6. 部署到生产环境(手动触发)
deploy-prod:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA -n production
- kubectl rollout status deployment/myapp -n production
environment:
name: production
url: https://example.com
when: manual # 需要手动触发
only:
- main
分支策略
1. Git Flow
流程图:
main (生产) ─────●─────────────●───────────●────
│ │ │
│ │ │
release (预发) └─────●───────┘ │
│ │
│ │
develop (开发) ──●───────┴─────────●─────────┴────
│ │
│ │
feature (功能) └───●─────●───────┘
│ │
开发 合并
分支说明:
main: 生产环境分支,只接受合并,不直接提交
release: 预发布分支,用于发布前测试
develop: 开发主分支,日常开发的集成分支
feature: 功能分支,从develop拉取,开发完成后合并回develop
hotfix: 紧急修复分支,从main拉取,修复后合并到main和develop
工作流程:
# 1. 创建功能分支
git checkout develop
git pull
git checkout -b feature/user-login
# 2. 开发功能
git add .
git commit -m "feat: add user login"
git push origin feature/user-login
# 3. 创建Merge Request,合并到develop
# (触发CI/CD,自动部署到测试环境)
# 4. 从develop创建release分支
git checkout develop
git checkout -b release/v1.2.0
# 5. 测试通过后,合并到main
git checkout main
git merge release/v1.2.0
git tag v1.2.0
git push origin main --tags
# 6. 同时合并回develop
git checkout develop
git merge release/v1.2.0
优劣分析:
优点:
流程清晰,职责明确
适合大型团队
支持多版本并行开发
缺点:
分支复杂,学习成本高
合并冲突多
发布周期长
2. GitHub Flow
流程图:
main (生产) ──●─────────────●─────────────●────
│ │ │
│ │ │
feature └───●─────●───┘ │
│ │ │
开发 合并 │
│
hotfix └───●───
│
修复
工作流程:
# 1. 从main创建功能分支
git checkout main
git pull
git checkout -b feature/add-payment
# 2. 开发并推送
git add .
git commit -m "feat: add payment feature"
git push origin feature/add-payment
# 3. 创建Pull Request
# (触发CI/CD,自动部署到预览环境)
# 4. Code Review通过后,合并到main
# (自动部署到生产环境)
# 5. 删除功能分支
git branch -d feature/add-payment
优劣分析:
优点:
流程简单,易于理解
适合持续部署
减少分支管理成本
缺点:
不适合多版本并行
需要强大的CI/CD支持
要求代码质量高
3. Trunk-Based Development
流程图:
main (主干) ──●──●──●──●──●──●──●──●──●────
│ │ │ │ │ │ │ │ │
开发者直接提交到main
每次提交触发CI/CD
工作流程:
# 1. 拉取最新代码
git checkout main
git pull
# 2. 开发(使用Feature Flag控制未完成功能)
# main.go
func main() {
if featureFlag.IsEnabled("new_payment") {
// 新功能
} else {
// 旧功能
}
}
# 3. 提交到main
git add .
git commit -m "feat: add payment (behind feature flag)"
git push origin main
# 4. 自动触发CI/CD,部署到生产
优劣分析:
优点:
极简流程
持续集成
减少合并冲突
缺点:
需要Feature Flag
要求极高的代码质量
需要完善的自动化测试
分支策略选择
选择Git Flow:
大型团队(> 20人)
需要维护多个版本
发布周期较长(每月/每季度)
选择GitHub Flow:
中小型团队(< 20人)
持续部署
快速迭代
选择Trunk-Based:
极致DevOps团队
高度自动化
超快速迭代(每天多次部署)
构建工具
1. Go项目构建
Makefile:
# Makefile
APP_NAME := myapp
VERSION := $(shell git describe --tags --always --dirty)
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
COMMIT := $(shell git rev-parse --short HEAD)
LDFLAGS := -ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME) -X main.GitCommit=$(COMMIT)"
.PHONY: all build test clean docker
all: test build
# 构建
build:
@echo "Building $(APP_NAME)..."
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(APP_NAME) ./cmd/$(APP_NAME)
# 测试
test:
@echo "Running tests..."
go test -v -cover ./...
# 清理
clean:
@echo "Cleaning..."
rm -rf bin/
# 构建Docker镜像
docker:
@echo "Building Docker image..."
docker build -t $(APP_NAME):$(VERSION) .
# 本地运行
run:
@echo "Running $(APP_NAME)..."
go run ./cmd/$(APP_NAME)
# 依赖管理
deps:
go mod download
go mod tidy
使用:
# 构建
make build
# 测试
make test
# 构建Docker镜像
make docker
# 本地运行
make run
2. Java项目构建(Maven)
pom.xml:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.1.0</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
构建命令:
# 清理
mvn clean
# 编译
mvn compile
# 测试
mvn test
# 打包
mvn package
# 跳过测试打包
mvn package -DskipTests
# 安装到本地仓库
mvn install
# 部署到远程仓库
mvn deploy
3. Node.js项目构建(npm)
package.json:
{
"name": "myapp",
"version": "1.0.0",
"scripts": {
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint src/",
"start": "node dist/index.js",
"dev": "webpack-dev-server --mode development"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"webpack": "^5.88.0",
"jest": "^29.0.0",
"eslint": "^8.45.0"
}
}
构建命令:
# 安装依赖
npm install
# 构建
npm run build
# 测试
npm test
# 代码检查
npm run lint
# 本地运行
npm run dev
制品管理
1. 制品类型
二进制制品:
- JAR/WAR(Java)
- Executable(Go)
- Wheel(Python)
容器镜像:
- Docker镜像
- OCI镜像
前端制品:
- npm包
- 静态资源(HTML、CSS、JS)
2. 制品仓库选择
对比表:
| 仓库 | 类型 | 优势 | 适用场景 |
|---|---|---|---|
| Nexus | 通用 | 支持多种格式(Maven、npm、Docker) | 企业级 |
| Harbor | Docker | 安全扫描、签名验证 | 容器化 |
| JFrog Artifactory | 通用 | 功能最全 | 大型企业 |
| Docker Hub | Docker | 公共镜像 | 开源项目 |
3. Harbor私有镜像仓库
安装Harbor:
# 1. 下载Harbor
wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-offline-installer-v2.9.0.tgz
tar xzvf harbor-offline-installer-v2.9.0.tgz
cd harbor
# 2. 配置harbor.yml
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
# harbor.yml
hostname: harbor.example.com
http:
port: 80
https:
port: 443
certificate: /path/to/cert.crt
private_key: /path/to/cert.key
harbor_admin_password: Harbor12345
database:
password: root123
# 3. 安装
sudo ./install.sh
# 4. 访问
# https://harbor.example.com
# 用户名: admin
# 密码: Harbor12345
推送镜像到Harbor:
# 1. 登录Harbor
docker login harbor.example.com
Username: admin
Password: Harbor12345
# 2. 构建镜像
docker build -t myapp:v1.0.0 .
# 3. 打标签
docker tag myapp:v1.0.0 harbor.example.com/myproject/myapp:v1.0.0
# 4. 推送
docker push harbor.example.com/myproject/myapp:v1.0.0
4. 制品版本管理
语义化版本(Semantic Versioning):
格式:MAJOR.MINOR.PATCH
示例:
- 1.0.0:初始版本
- 1.0.1:Bug修复(PATCH)
- 1.1.0:新功能,向后兼容(MINOR)
- 2.0.0:破坏性变更(MAJOR)
Git Tag:
git tag v1.0.0
git push origin v1.0.0
Docker镜像版本管理:
# 多标签策略
docker build -t myapp:v1.2.3 .
docker tag myapp:v1.2.3 myapp:v1.2
docker tag myapp:v1.2.3 myapp:v1
docker tag myapp:v1.2.3 myapp:latest
# 推送所有标签
docker push myapp:v1.2.3
docker push myapp:v1.2
docker push myapp:v1
docker push myapp:latest
环境管理
1. 环境划分
┌──────────────┐
│ 开发环境 │ Development(dev)
│ 本地开发 │ - 开发者本地机器
└──────────────┘ - 快速迭代
↓
┌──────────────┐
│ 测试环境 │ Testing(test)
│ 功能测试 │ - 自动化测试
└──────────────┘ - 手动测试
↓
┌──────────────┐
│ 预发环境 │ Staging(staging)
│ 生产镜像 │ - 与生产环境一致
└──────────────┘ - 最终验证
↓
┌──────────────┐
│ 生产环境 │ Production(prod)
│ 真实用户 │ - 实际运行环境
└──────────────┘ - 高可用
2. 环境配置管理
方案1:环境变量:
# .env.dev
DATABASE_URL=postgres://localhost:5432/myapp_dev
REDIS_URL=redis://localhost:6379/0
LOG_LEVEL=debug
# .env.prod
DATABASE_URL=postgres://prod-db.example.com:5432/myapp
REDIS_URL=redis://prod-redis.example.com:6379/0
LOG_LEVEL=info
方案2:配置文件:
# config/dev.yaml
database:
host: localhost
port: 5432
name: myapp_dev
redis:
host: localhost
port: 6379
log:
level: debug
# config/prod.yaml
database:
host: prod-db.example.com
port: 5432
name: myapp
redis:
host: prod-redis.example.com
port: 6379
log:
level: info
方案3:Kubernetes ConfigMap:
# configmap-dev.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: dev
data:
DATABASE_URL: postgres://localhost:5432/myapp_dev
REDIS_URL: redis://localhost:6379/0
LOG_LEVEL: debug
---
# configmap-prod.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: production
data:
DATABASE_URL: postgres://prod-db.example.com:5432/myapp
REDIS_URL: redis://prod-redis.example.com:6379/0
LOG_LEVEL: info
3. 环境隔离
网络隔离:
┌──────────────────────────────────────┐
│ VPC (10.0.0.0/16) │
│ │
│ ┌────────────────────────────────┐ │
│ │ 开发环境 (10.0.1.0/24) │ │
│ │ - dev-k8s-cluster │ │
│ └────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────┐ │
│ │ 测试环境 (10.0.2.0/24) │ │
│ │ - test-k8s-cluster │ │
│ └────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────┐ │
│ │ 生产环境 (10.0.3.0/24) │ │
│ │ - prod-k8s-cluster │ │
│ └────────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
Kubernetes命名空间隔离:
# 创建命名空间
kubectl create namespace dev
kubectl create namespace test
kubectl create namespace staging
kubectl create namespace production
# 部署到不同环境
kubectl apply -f deployment.yaml -n dev
kubectl apply -f deployment.yaml -n production
面试问答
什么是CI/CD?有什么优势?
答案:
定义:
CI(持续集成):
频繁地将代码集成到主干,每次集成通过自动化构建和测试验证。
CD(持续交付):
确保代码始终处于可部署状态,可随时一键部署。
CD(持续部署):
自动将代码部署到生产环境,无需人工干预。
优势:
1. 快速反馈:
- 传统:2周后才发现问题
- CI/CD:30分钟内发现问题
2. 提高质量:
- 自动化测试覆盖
- 代码质量检查
- 减少人为错误
3. 加快交付:
- 部署频率:从每月→每天多次
- 减少部署时间:从半天→5分钟
4. 降低风险:
- 小批量发布
- 快速回滚
- 减少故障影响
Git Flow和GitHub Flow有什么区别?如何选择?
答案:
对比:
| 维度 | Git Flow | GitHub Flow |
|---|---|---|
| 分支 | 5种(main、develop、feature、release、hotfix) | 2种(main、feature) |
| 复杂度 | 高 | 低 |
| 发布周期 | 长(每月/每季度) | 短(每天/每周) |
| 适合团队 | 大型团队 | 中小型团队 |
| 适合场景 | 多版本并行 | 持续部署 |
选择建议:
选择Git Flow:
大型团队(> 20人)
需要维护多个版本(如SaaS多租户)
发布周期较长
严格的质量控制
选择GitHub Flow:
中小型团队(< 20人)
单一版本
快速迭代
强大的CI/CD支持
选择Trunk-Based:
极致DevOps团队
高度自动化
超快速迭代
如何设计一个完整的CI/CD流水线?
答案:
完整流水线设计:
# .gitlab-ci.yml
stages:
- build # 构建
- test # 测试
- security # 安全
- package # 打包
- deploy # 部署
# 1. 构建
build:
stage: build
script:
- go build -o bin/myapp
# 2. 单元测试
unit-test:
stage: test
script:
- go test -cover ./...
coverage: '/coverage: \d+.\d+%/'
# 3. 集成测试
integration-test:
stage: test
script:
- docker-compose up -d
- go test -tags=integration ./...
# 4. 代码质量
code-quality:
stage: security
script:
- sonar-scanner
# 5. 安全扫描
security-scan:
stage: security
script:
- trivy image myapp:latest
# 6. 构建镜像
docker-build:
stage: package
script:
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push myapp:$CI_COMMIT_SHA
# 7. 部署测试环境
deploy-test:
stage: deploy
script:
- kubectl set image deployment/myapp myapp=myapp:$CI_COMMIT_SHA -n test
# 8. 部署生产(手动触发)
deploy-prod:
stage: deploy
script:
- kubectl set image deployment/myapp myapp=myapp:$CI_COMMIT_SHA -n production
when: manual
关键要素:
1. 构建阶段:
编译代码
生成制品
2. 测试阶段:
单元测试
集成测试
代码覆盖率
3. 质量阶段:
代码质量检查(SonarQube)
安全扫描(Trivy)
依赖检查
4. 打包阶段:
构建Docker镜像
推送到镜像仓库
生成版本标签
5. 部署阶段:
自动部署到测试环境
手动部署到生产环境
健康检查
如何优化CI/CD流水线的速度?
答案:
优化策略:
1. 并行执行:
# 并行执行测试
stages:
- test
unit-test:
stage: test
script:
- go test ./internal/...
integration-test:
stage: test
script:
- go test -tags=integration ./...
e2e-test:
stage: test
script:
- npm run test:e2e
# 三个测试并行执行,节省时间
2. 缓存依赖:
build:
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/ # Go依赖
- node_modules/ # Node依赖
- .m2/ # Maven依赖
script:
- go build
3. 增量构建:
build:
script:
- |
if git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | grep -q "^frontend/"; then
echo "前端代码变更,构建前端"
npm run build
fi
- |
if git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | grep -q "^backend/"; then
echo "后端代码变更,构建后端"
go build
fi
4. Docker多阶段构建:
# 多阶段构建,减少镜像大小
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
FROM alpine:latest
COPY /app/myapp /myapp
CMD ["/myapp"]
5. 使用更快的Runner:
build:
tags:
- docker
- high-performance # 使用高性能Runner
效果对比:
优化前:
- 构建:10分钟
- 测试:15分钟
- 打包:5分钟
- 总计:30分钟
优化后:
- 并行构建+测试:8分钟(并行)
- 缓存依赖:节省3分钟
- 增量构建:节省5分钟
- 总计:10分钟
提速:3倍
如何保证生产环境部署的安全性?
答案:
安全措施:
1. 手动审批:
deploy-prod:
stage: deploy
script:
- kubectl apply -f deployment.yaml -n production
when: manual # 需要手动点击才会部署
only:
- main
2. 四眼原则(Four-Eyes Principle):
deploy-prod:
stage: deploy
script:
- kubectl apply -f deployment.yaml
environment:
name: production
action: prepare # 需要另一个人批准
only:
- main
3. 部署时间窗口:
deploy-prod:
stage: deploy
script:
- |
HOUR=$(date +%H)
if [ $HOUR -lt 10 ] || [ $HOUR -gt 18 ]; then
echo "只允许在10:00-18:00部署生产环境"
exit 1
fi
- kubectl apply -f deployment.yaml
4. 金丝雀部署:
# 先部署10%流量
deploy-canary:
script:
- kubectl apply -f canary-deployment.yaml # replicas: 1
- sleep 300 # 观察5分钟
# 观察无问题,全量部署
deploy-prod:
script:
- kubectl apply -f production-deployment.yaml # replicas: 10
when: manual
5. 自动回滚:
deploy-prod:
script:
- kubectl apply -f deployment.yaml
- kubectl rollout status deployment/myapp
- |
# 健康检查
for i in {1..30}; do
if curl -f http://myapp/health; then
echo "部署成功"
exit 0
fi
sleep 10
done
echo "健康检查失败,自动回滚"
kubectl rollout undo deployment/myapp
exit 1
6. 蓝绿部署:
# 1. 部署新版本(绿)
kubectl apply -f deployment-v2.yaml
# 2. 验证新版本
kubectl exec -it test-pod -- curl http://myapp-v2/health
# 3. 切换流量(修改Service selector)
kubectl patch service myapp -p '{"spec":{"selector":{"version":"v2"}}}'
# 4. 观察,如有问题立即切回
kubectl patch service myapp -p '{"spec":{"selector":{"version":"v1"}}}'