CI/CD完整流程:Docker + Jenkins + Kubernetes

CI/CD完整流程:Docker + Jenkins + Kubernetes

一、通俗易懂的比喻解释

1.1 用盖房子来比喻

Docker 镜像 = 房子设计图纸

  • 镜像就是一张图纸,上面写着:需要什么材料、怎么搭建
  • 比如 nginx:alpine 镜像 = “一个装了 Nginx 的 Linux 系统的设计图”
  • 镜像是只读的,不会变

Docker 容器 = 根据图纸盖出来的真实房子

  • 容器就是按照镜像(图纸)实际运行起来的程序
  • 一张图纸可以盖很多房子(一个镜像可以启动多个容器)
  • 容器是活的,可以进去操作
1
2
# 镜像 → 容器的过程
docker run nginx:alpine # 用 nginx 镜像启动一个容器(盖房子)

1.2 完整流程:从本地到线上

场景 1:你在本地玩 Docker + Jenkins

1
2
3
4
你的电脑(本地)
├── Docker Desktop(盖房子的工地)
│ └── Jenkins 容器(一个房子,里面住着 Jenkins)
│ └── 访问 http://localhost:8080 就能进这个房子

这时候发生了什么?

  1. 你下载了 Jenkins 镜像(图纸)
  2. Docker 根据图纸盖了一个 Jenkins 容器(房子)
  3. 这个房子开了一扇门:8080 端口
  4. 你通过 localhost:8080 进入这个房子
1
2
3
# 你本地执行的命令
docker run -p 8080:8080 jenkins/jenkins
# -p 8080:8080 = 把容器的 8080 端口映射到你电脑的 8080 端口

重点:这个 Jenkins 只在你电脑上,别人访问不了!

场景 2:真实线上环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
公司的服务器集群(线上)
├── 服务器 1
│ ├── Docker(工地)
│ │ └── Jenkins 容器(管家房子)
│ └── K8s Master(总指挥)

├── 服务器 2
│ ├── Docker(工地)
│ └── K8s Node(工人)
│ └── 你的前端容器 A(房子 A)

├── 服务器 3
│ ├── Docker(工地)
│ └── K8s Node(工人)
│ └── 你的前端容器 B(房子 B)

└── 服务器 4
├── Docker(工地)
└── K8s Node(工人)
└── 你的前端容器 C(房子 C)

1.3 完整部署流程(用你的项目举例)

第 1 步:你提交代码

1
2
3
git add .
git commit -m "修复了登录 bug"
git push origin main

第 2 步:Jenkins 自动开始干活(在服务器 1)

Jenkins 就像一个管家,住在服务器 1 的 Docker 容器里。

1
2
3
4
5
6
7
# Jenkins 容器里执行的操作
1. 拉取你的代码
2. npm install(安装依赖)
3. npm run build(打包,生成 dist 文件夹)
4. docker build -t uhr-form:v1.0.5 . # 制作新图纸
5. docker push uhr-form:v1.0.5 # 把图纸上传到图纸仓库
6. kubectl apply -f k8s.yaml # 通知 K8s:用新图纸盖房子

第 3 步:K8s 开始调度(总指挥)

K8s Master(总指挥)看到新图纸来了:

1
2
3
4
5
6
7
8
# k8s.yaml 说:我要 3 个房子(replicas: 3)
spec:
replicas: 3
template:
spec:
containers:
- name: uhr-form
image: uhr-form:v1.0.5 # 用这个新图纸

K8s 总指挥开始分配任务:

  • “服务器 2,你去盖一个房子”
  • “服务器 3,你也盖一个”
  • “服务器 4,你也盖一个”

第 4 步:各个服务器开始盖房子

1
2
3
4
5
6
服务器 2:
1. docker pull uhr-form:v1.0.5 # 下载图纸
2. docker run uhr-form:v1.0.5 # 盖房子(启动容器)
3. 房子盖好了,开门营业(监听 80 端口)

服务器 3、4 同样操作

第 5 步:用户访问

1
2
3
4
5
6
7
8
9
10
用户访问 http://your-domain.com

K8s Service(前台接待)

负载均衡(随机选一个房子)

┌─────┼─────┐
↓ ↓ ↓
房子A 房子B 房子C
(容器A)(容器B)(容器C)

1.4 关键理解

Docker 的角色

Docker 就是”盖房子的工具”,在每台服务器上都有。

  • 本地:你电脑上的 Docker Desktop
  • 线上:每台服务器上都装了 Docker

Jenkins 的角色

Jenkins 是”自动化管家”,负责:

  • 监听代码变化
  • 自动打包
  • 制作 Docker 镜像(图纸)
  • 通知 K8s 部署

Jenkins 本身也是一个 Docker 容器,住在某台服务器上。

K8s 的角色

K8s 是”总指挥 + 物业管理”,负责:

  • 决定在哪台服务器盖房子
  • 监控房子是否正常(容器健康检查)
  • 房子塌了自动重建(自动重启)
  • 流量分配(负载均衡)

为什么本地和线上不一样?

本地 线上
只有你一台电脑 有很多台服务器
手动 docker run K8s 自动调度
只有你能访问 全世界都能访问
容器挂了要手动重启 K8s 自动重启

1.5 总结一句话

  • Docker:盖房子的工具(本地和线上都用)
  • Jenkins:自动化管家(监听代码变化,自动打包部署)
  • K8s:物业公司(管理多台服务器上的多个容器)

本地:你自己玩,手动盖房子
线上:Jenkins 自动打包 → K8s 自动在多台服务器盖房子 → 用户访问

重要:Docker 容器跑在 K8s 上,K8s 调度和管理 Docker 容器。

二、三者的关系与定位

2.1 整体架构图

1
2
3
4
5
6
7
8
9
开发阶段          构建阶段              部署阶段
↓ ↓ ↓
代码提交 → Jenkins (CI/CD) → Docker镜像 → K8s集群
| | |
|-- 拉取代码 |-- 构建镜像 |-- 容器编排
|-- 运行测试 |-- 推送仓库 |-- 自动扩缩容
|-- 构建应用 | |-- 服务发现
|-- 自动部署 |-- 负载均衡
|-- 滚动更新

2.2 各自的定位

Docker - 容器化技术

  • 打包应用及依赖,保证环境一致性
  • 解决”在我机器上能跑”的问题
  • 层级:应用层

Jenkins - CI/CD 工具

  • 自动化构建、测试、部署流程
  • 串联整个开发到部署的流程
  • 层级:流程编排层

Kubernetes - 容器编排平台

  • 管理和调度大规模容器集群
  • 提供高可用、自动扩缩容等能力
  • 层级:基础设施层

三、完整的 CI/CD 流程

3.1 流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. 开发提交代码到 Git

2. Git Webhook 触发 Jenkins

3. Jenkins 拉取最新代码

4. Jenkins 安装依赖并运行测试

5. Jenkins 构建应用

6. Jenkins 构建 Docker 镜像

7. Jenkins 推送镜像到镜像仓库

8. Jenkins 更新 K8s Deployment

9. K8s 执行滚动更新

10. K8s 健康检查

11. 部署完成,发送通知

3.2 Jenkins Pipeline 完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
pipeline {
agent any

environment {
// Docker 相关配置
DOCKER_REGISTRY = 'registry.example.com'
DOCKER_CREDENTIALS = 'docker-hub-credentials'
IMAGE_NAME = 'myapp'
IMAGE_TAG = "${env.BUILD_NUMBER}"

// K8s 相关配置
K8S_NAMESPACE = 'production'
K8S_DEPLOYMENT = 'myapp-deployment'
KUBECONFIG_CREDENTIALS = 'kubeconfig'
}

stages {
stage('Checkout') {
steps {
echo '📥 拉取代码...'
checkout scm
}
}

stage('Install Dependencies') {
steps {
echo '📦 安装依赖...'
sh 'npm ci'
}
}

stage('Lint & Test') {
parallel {
stage('Lint') {
steps {
echo '🔍 代码检查...'
sh 'npm run lint'
}
}
stage('Unit Test') {
steps {
echo '🧪 单元测试...'
sh 'npm run test:unit'
}
}
stage('E2E Test') {
steps {
echo '🎯 E2E测试...'
sh 'npm run test:e2e'
}
}
}
}

stage('Build Application') {
steps {
echo '🔨 构建应用...'
sh 'npm run build'
}
}

stage('Build Docker Image') {
steps {
echo '🐳 构建 Docker 镜像...'
script {
def fullImageName = "${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
def latestImageName = "${DOCKER_REGISTRY}/${IMAGE_NAME}:latest"

sh "docker build -t ${fullImageName} ."
sh "docker tag ${fullImageName} ${latestImageName}"
}
}
}

stage('Push Docker Image') {
steps {
echo '📤 推送 Docker 镜像...'
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", DOCKER_CREDENTIALS) {
sh "docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
sh "docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest"
}
}
}
}

stage('Deploy to K8s') {
steps {
echo '🚀 部署到 Kubernetes...'
script {
withKubeConfig([credentialsId: KUBECONFIG_CREDENTIALS]) {
// 更新镜像
sh """
kubectl set image deployment/${K8S_DEPLOYMENT} \
${IMAGE_NAME}=${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \
-n ${K8S_NAMESPACE} \
--record
"""

// 等待滚动更新完成
sh """
kubectl rollout status deployment/${K8S_DEPLOYMENT} \
-n ${K8S_NAMESPACE} \
--timeout=5m
"""
}
}
}
}

stage('Verify Deployment') {
steps {
echo '✅ 验证部署...'
script {
withKubeConfig([credentialsId: KUBECONFIG_CREDENTIALS]) {
// 检查 Pod 状态
sh """
kubectl get pods -n ${K8S_NAMESPACE} \
-l app=${IMAGE_NAME} \
-o wide
"""

// 健康检查
sleep 10
sh 'curl -f http://myapp.example.com/health || exit 1'
}
}
}
}
}

post {
success {
echo '🎉 部署成功!'
dingtalk(
robot: 'jenkins-bot',
type: 'MARKDOWN',
title: '部署成功',
text: [
"### 🎉 部署成功",
"- **项目**: ${env.JOB_NAME}",
"- **版本**: ${IMAGE_TAG}",
"- **环境**: ${K8S_NAMESPACE}",
"- **镜像**: ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}",
"- [查看详情](${env.BUILD_URL})"
]
)
}

failure {
echo '❌ 部署失败!'
// 自动回滚
script {
withKubeConfig([credentialsId: KUBECONFIG_CREDENTIALS]) {
sh """
kubectl rollout undo deployment/${K8S_DEPLOYMENT} \
-n ${K8S_NAMESPACE}
"""
}
}

dingtalk(
robot: 'jenkins-bot',
type: 'MARKDOWN',
title: '部署失败',
text: [
"### ❌ 部署失败(已自动回滚)",
"- **项目**: ${env.JOB_NAME}",
"- **版本**: ${IMAGE_TAG}",
"- [查看日志](${env.BUILD_URL}console)"
]
)
}

always {
// 清理 Docker 镜像
sh "docker rmi ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} || true"
sh "docker rmi ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest || true"

// 清理工作空间
cleanWs()
}
}
}

四、Kubernetes 配置文件

4.1 Deployment 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
namespace: production
labels:
app: myapp
version: v1
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多出1个Pod
maxUnavailable: 0 # 最多0个Pod不可用
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
version: v1
spec:
# 镜像拉取密钥
imagePullSecrets:
- name: registry-secret

# 初始化容器
initContainers:
- name: init-db
image: busybox:1.28
command: ['sh', '-c', 'until nc -z db-service 3306; do echo waiting for db; sleep 2; done;']

# 应用容器
containers:
- name: myapp
image: registry.example.com/myapp:latest
imagePullPolicy: Always
ports:
- containerPort: 3000
name: http
protocol: TCP

# 环境变量
env:
- name: NODE_ENV
value: "production"
- name: PORT
value: "3000"
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: myapp-config
key: DATABASE_URL
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secret
key: password

# 资源限制
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

# 存活探针
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3

# 就绪探针
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3

# 挂载卷
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
- name: logs
mountPath: /app/logs

# 卷定义
volumes:
- name: config
configMap:
name: myapp-config
- name: logs
persistentVolumeClaim:
claimName: myapp-logs-pvc

4.2 Service 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: production
spec:
type: ClusterIP
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
sessionAffinity: ClientIP # 会话保持

4.3 Ingress 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: production
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80

4.4 ConfigMap 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: production
data:
DATABASE_URL: "mysql://db-service:3306/myapp"
REDIS_URL: "redis://redis-service:6379"
LOG_LEVEL: "info"
app.conf: |
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
}
}

4.5 Secret 配置

1
2
3
4
5
6
7
8
9
10
11
12
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myapp-secret
namespace: production
type: Opaque
data:
# base64 编码后的值
username: YWRtaW4=
password: cGFzc3dvcmQ=
db-password: ZGJwYXNzd29yZA==

4.6 HPA 自动扩缩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 15
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15

五、Dockerfile 最佳实践

5.1 多阶段构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 构建阶段
FROM node:16 AS builder

WORKDIR /app

# 复制依赖文件
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production

# 复制源码
COPY . .

# 构建应用
RUN npm run build

# 运行阶段
FROM node:16-alpine

WORKDIR /app

# 只复制必要文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001

USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD node healthcheck.js || exit 1

# 启动命令
CMD ["node", "dist/main.js"]

5.2 .dockerignore 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
.DS_Store
*.md
.vscode
.idea
coverage
.nyc_output
dist
build
*.log

六、常用 K8s 命令

6.1 Pod 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查看 Pod
kubectl get pods -n production
kubectl get pods -n production -o wide
kubectl get pods -n production -w # 实时监控

# 查看 Pod 详情
kubectl describe pod myapp-pod -n production

# 查看日志
kubectl logs myapp-pod -n production
kubectl logs -f myapp-pod -n production # 实时日志
kubectl logs myapp-pod -n production --previous # 查看上一个容器的日志

# 进入容器
kubectl exec -it myapp-pod -n production -- bash
kubectl exec -it myapp-pod -n production -- sh

# 删除 Pod
kubectl delete pod myapp-pod -n production

6.2 Deployment 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 查看 Deployment
kubectl get deployments -n production
kubectl describe deployment myapp-deployment -n production

# 扩缩容
kubectl scale deployment myapp-deployment --replicas=5 -n production

# 更新镜像
kubectl set image deployment/myapp-deployment \
myapp=myapp:v2 -n production --record

# 查看滚动更新状态
kubectl rollout status deployment/myapp-deployment -n production

# 查看历史版本
kubectl rollout history deployment/myapp-deployment -n production

# 回滚到上一个版本
kubectl rollout undo deployment/myapp-deployment -n production

# 回滚到指定版本
kubectl rollout undo deployment/myapp-deployment \
--to-revision=2 -n production

# 暂停/恢复滚动更新
kubectl rollout pause deployment/myapp-deployment -n production
kubectl rollout resume deployment/myapp-deployment -n production

6.3 Service 操作

1
2
3
4
5
6
7
# 查看 Service
kubectl get services -n production
kubectl get svc -n production
kubectl describe service myapp-service -n production

# 查看 Endpoints
kubectl get endpoints -n production

6.4 配置操作

1
2
3
4
5
6
7
8
9
10
# ConfigMap
kubectl get configmap -n production
kubectl describe configmap myapp-config -n production
kubectl create configmap myapp-config --from-file=config.yaml -n production

# Secret
kubectl get secret -n production
kubectl describe secret myapp-secret -n production
kubectl create secret generic myapp-secret \
--from-literal=password=mypassword -n production

6.5 应用配置

1
2
3
4
5
6
7
8
9
10
11
# 应用配置文件
kubectl apply -f k8s/
kubectl apply -f k8s/deployment.yaml

# 删除资源
kubectl delete -f k8s/
kubectl delete deployment myapp-deployment -n production

# 查看资源定义
kubectl get deployment myapp-deployment -n production -o yaml
kubectl get deployment myapp-deployment -n production -o json

6.6 调试命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 端口转发(本地调试)
kubectl port-forward pod/myapp-pod 8080:3000 -n production
kubectl port-forward service/myapp-service 8080:80 -n production

# 查看事件
kubectl get events -n production --sort-by='.lastTimestamp'

# 查看资源使用
kubectl top pods -n production
kubectl top nodes

# 查看集群信息
kubectl cluster-info
kubectl get nodes
kubectl describe node node-name

七、面试高频问题

7.1 Docker、Jenkins、K8s 三者如何配合?

答:

  1. Docker 负责应用容器化,保证环境一致性
  2. Jenkins 负责 CI/CD 流程自动化,串联整个流程
  3. K8s 负责容器编排和管理,实现高可用和自动扩缩容

工作流程:

  • 开发提交代码 → Git
  • Jenkins 自动触发构建
  • Jenkins 使用 Docker 构建镜像
  • Jenkins 推送镜像到仓库
  • Jenkins 通知 K8s 更新部署
  • K8s 拉取新镜像并滚动更新

7.2 K8s 滚动更新原理?

答:

  1. 创建新版本 Pod
  2. 等待新 Pod 就绪(readinessProbe)
  3. 将流量切换到新 Pod
  4. 删除旧版本 Pod
  5. 重复以上步骤,直到所有 Pod 更新完成

配置参数:

  • maxSurge: 最多可以多出几个 Pod(默认 25%)
  • maxUnavailable: 最多几个 Pod 不可用(默认 25%)

7.3 如何保证 K8s 应用的高可用?

答:

  1. 多副本部署:replicas >= 3
  2. 健康检查:配置 livenessProbe 和 readinessProbe
  3. 资源限制:设置 requests 和 limits
  4. 反亲和性:Pod 分散到不同节点
  5. HPA 自动扩缩容:应对流量波动
  6. PDB(Pod Disruption Budget):限制同时不可用的 Pod 数量
  7. 滚动更新:零停机部署

7.4 CI/CD 流程中如何保证质量?

答:

  1. 代码检查:ESLint、Prettier 等
  2. 单元测试:Jest、Mocha 等
  3. 集成测试:API 测试、E2E 测试
  4. 代码覆盖率:要求达到一定比例
  5. 安全扫描:镜像漏洞扫描
  6. 性能测试:压力测试、负载测试
  7. 灰度发布:先发布到部分用户
  8. 自动回滚:失败时自动回滚到上一版本

7.5 如何优化 CI/CD 流程?

答:

  1. 并行构建:测试、检查等并行执行
  2. 缓存依赖:npm cache、Docker layer cache
  3. 增量构建:只构建变更的部分
  4. 多阶段构建:减小镜像体积
  5. 使用更快的 Agent:SSD、更多 CPU
  6. 清理旧资源:定期清理旧镜像、旧构建
  7. 分支策略:不同分支不同流程

八、总结

完整的 CI/CD 流程需要 Docker、Jenkins、Kubernetes 三者紧密配合:

  1. Docker 提供容器化能力,保证环境一致性
  2. Jenkins 提供自动化能力,串联整个流程
  3. Kubernetes 提供编排能力,实现高可用和弹性伸缩

通过这套体系,可以实现:

  • ✅ 代码提交后自动构建、测试、部署
  • ✅ 零停机滚动更新
  • ✅ 自动扩缩容应对流量波动
  • ✅ 失败自动回滚
  • ✅ 完整的监控和日志

这是现代化应用部署的标准实践。