0%

CI/CD 流水线设计最佳实践:从 GitLab 到 K8s 的完整自动化

CI/CD 流水线设计最佳实践:从 GitLab 到 K8s 的完整自动化

写在前面:这篇文章源于 CrystalForge 项目的真实 CI/CD 建设经验。我们从零开始设计并实施了完整的自动化流水线,实现从代码提交到生产部署的全流程自动化。


一、背景及痛点分析

1.1 真实场景

没有 CI/CD 时的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
开发人员:
1. 本地开发代码
2. 手动构建 Docker 镜像
3. 手动推送到 Harbor
4. SSH 登录服务器
5. 手动 kubectl apply
6. 验证部署

测试人员:
1. 等待开发部署完成
2. 手动测试
3. 发现问题,打回重做

运维人员:
1. 接收部署请求
2. 手动执行部署脚本
3. 监控系统状态
4. 故障时手动回滚

❌ 部署一次需要 30-60 分钟
❌ 人为错误频繁
❌ 环境不一致
❌ 回滚困难

遇到的问题

  1. 效率低下

    • 手动操作多,耗时长
    • 重复劳动,容易出错
    • 部署窗口长,影响业务
  2. 质量不稳定

    • 缺少自动化测试
    • 环境不一致
    • 配置漂移
  3. 风险高

    • 人为失误
    • 回滚困难
    • 无审计日志
  4. 协作困难

    • 开发、测试、运维割裂
    • 信息不透明
    • 责任不清晰

1.2 CI/CD 目标

指标 优化前 目标 实际达成
部署时间 30-60 分钟 <5 分钟 3 分钟 ✅
部署频率 每周 1-2 次 每天多次 按需部署 ✅
部署失败率 20% <5% 3% ✅
回滚时间 30 分钟 <5 分钟 2 分钟 ✅
测试覆盖率 30% >80% 85% ✅

1.3 技术方案选型

工具 优势 劣势 我们的选择
GitLab CI 原生集成、配置简单 功能相对简单 ✅ 主要使用
Jenkins 功能强大、插件丰富 配置复杂、维护成本高 辅助使用
GitHub Actions 生态好、免费 国内访问慢 不考虑
ArgoCD GitOps、可视化好 学习曲线陡 ✅ 用于 K8s 部署

我们的 CI/CD 架构

1
2
3
4
5
GitLab CI(代码构建、测试、镜像打包)

Harbor(镜像存储)

ArgoCD(GitOps 部署到 K8s)

二、解决方案:三层流水线架构

2.1 架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────┐
│ CI/CD 流水线 │
├─────────────────────────────────────────┤
│ │
│ CI 层(持续集成) │
│ ┌─────────────────────────────────┐ │
│ │ 代码提交 → 构建 → 测试 → 镜像 │ │
│ └─────────────────────────────────┘ │
│ ↓ │
│ CD 层(持续交付) │
│ ┌─────────────────────────────────┐ │
│ │ 镜像推送 → 更新 Manifest → Git │ │
│ └─────────────────────────────────┘ │
│ ↓ │
│ CD 层(持续部署) │
│ ┌─────────────────────────────────┐ │
│ │ ArgoCD 同步 → K8s 部署 → 验证 │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘

2.2 流水线阶段

CI 流水线

  1. Checkout - 拉取代码
  2. Lint - 代码检查
  3. Test - 单元测试
  4. Build - 构建镜像
  5. Push - 推送镜像

CD 流水线

  1. Update Manifest - 更新 K8s YAML
  2. Commit - 提交到 Git
  3. Sync - ArgoCD 自动同步
  4. Verify - 健康检查

2.3 分支策略

1
2
3
4
5
6
7
main (生产)
↑ 保护分支,MR 合并
staging (测试)
↑ 自动部署
develop (开发)
↑ 功能分支合并
feature/xxx (功能)

三、最佳实践案例:CrystalForge 流水线

3.1 GitLab CI 配置

完整 .gitlab-ci.yml

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
# .gitlab-ci.yml
stages:
- lint
- test
- build
- push
- deploy

variables:
DOCKER_REGISTRY: hb.test
DOCKER_IMAGE: crystalforge/openclaw
K8S_NAMESPACE: openclaw

# 代码检查
lint:
stage: lint
image: node:18-alpine
script:
- npm ci
- npm run lint
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop"

# 单元测试
test:
stage: test
image: node:18-alpine
script:
- npm ci
- npm run test:coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop"

# 构建 Docker 镜像
build:
stage: build
image: docker:24-dind
services:
- docker:24-dind
variables:
DOCKER_BUILDKIT: 1
script:
- echo "🔨 构建 Docker 镜像..."
- docker build -t $DOCKER_REGISTRY/$DOCKER_IMAGE:$CI_COMMIT_SHA .
- docker tag $DOCKER_REGISTRY/$DOCKER_IMAGE:$CI_COMMIT_SHA $DOCKER_REGISTRY/$DOCKER_IMAGE:latest
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "staging"

# 推送镜像到 Harbor
push:
stage: push
image: docker:24-dind
services:
- docker:24-dind
script:
- echo "📤 推送镜像到 Harbor..."
- docker login -u $HARBOR_USERNAME -p $HARBOR_PASSWORD $DOCKER_REGISTRY
- docker push $DOCKER_REGISTRY/$DOCKER_IMAGE:$CI_COMMIT_SHA
- docker push $DOCKER_REGISTRY/$DOCKER_IMAGE:latest
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "staging"

# 更新 K8s Manifest
update-manifest:
stage: deploy
image: alpine/k8s
script:
- echo "🔄 更新 K8s Manifest..."
- git config --global user.email "ci-bot@example.com"
- git config --global user.name "CI Bot"
- git clone http://gitlab-ci-token:${GITLAB_TOKEN}@192.168.100.191:9910/crystalcreator/k8s-manifests.git
- cd k8s-manifests
- sed -i "s|image:.*|image: $DOCKER_REGISTRY/$DOCKER_IMAGE:$CI_COMMIT_SHA|" deployment.yaml
- git add deployment.yaml
- git commit -m "chore: 更新 OpenClaw 镜像到 $CI_COMMIT_SHA"
- git push origin main
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual # 生产环境手动触发
- if: $CI_COMMIT_BRANCH == "staging"
when: auto # 测试环境自动触发

3.2 环境变量配置

GitLab CI/CD Variables

1
2
3
4
5
# Settings → CI/CD → Variables
HARBOR_USERNAME=crystalcreator
HARBOR_PASSWORD=ZeNPCiUGW8Dgm2tlXg53
GITLAB_TOKEN=glpat-FxE8Ka5HaApPLHnAQDtz
KUBECONFIG_BASE64=<base64 编码的 kubeconfig>

3.3 ArgoCD 配置

安装 ArgoCD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建命名空间
kubectl create namespace argocd

# 部署 ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# 验证
kubectl get pods -n argocd

# 获取初始密码
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

# 访问 UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

配置 Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: openclaw
namespace: argocd
spec:
project: default
source:
repoURL: http://192.168.100.191:9910/crystalcreator/k8s-manifests.git
targetRevision: HEAD
path: .
destination:
server: https://kubernetes.default.svc
namespace: openclaw
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

四、深度思考体会

4.1 为什么选择 GitLab CI + ArgoCD?

GitLab CI 的优势

  • 原生集成,配置简单
  • 代码和 CI 配置在一起
  • MR 集成好

ArgoCD 的优势

  • GitOps 理念,声明式
  • 可视化好
  • 自动同步

我的理解

GitLab CI 负责构建和测试,ArgoCD 负责部署。分工明确,各司其职。

4.2 流水线设计原则

1. 快速失败

  • Lint 和 Test 放在前面
  • 尽早发现问题
  • 避免浪费资源

2. 并行执行

1
2
3
4
5
6
7
8
9
10
11
12
# 并行测试
test-unit:
stage: test
script: npm run test:unit

test-integration:
stage: test
script: npm run test:integration

test-e2e:
stage: test
script: npm run test:e2e

3. 缓存优化

1
2
3
4
5
# 缓存 node_modules
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/

4.3 安全考虑

1. 凭证管理

  • 使用 GitLab Variables
  • 不硬编码在 YAML 中
  • 定期轮换

2. 权限控制

  • 最小权限原则
  • 生产环境手动触发
  • 保护分支

3. 审计日志

  • GitLab 有完整日志
  • ArgoCD 有审计
  • 所有变更可追溯

五、踩坑记录

5.1 坑 1:Docker in Docker 权限问题

问题

1
ERROR: Cannot connect to the Docker daemon

解决

1
2
3
4
5
6
image: docker:24-dind
services:
- docker:24-dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"

5.2 坑 2:ArgoCD 同步失败

问题

1
Application is out of sync

原因

  • K8s Manifest 配置错误
  • 资源不存在

解决

1
2
3
4
5
# 查看 ArgoCD 日志
argocd app logs openclaw

# 手动同步
argocd app sync openclaw

5.3 坑 3:镜像拉取失败

问题

1
ImagePullBackOff: unauthorized

解决

1
2
3
4
5
6
7
8
9
10
# 创建 ImagePullSecret
kubectl create secret docker-registry harbor-secret \
--docker-server=hb.test \
--docker-username=crystalcreator \
--docker-password=xxx \
-n openclaw

# 在 Deployment 中引用
imagePullSecrets:
- name: harbor-secret

六、总结

6.1 核心要点

  1. GitLab CI - 代码构建、测试、镜像打包
  2. Harbor - 镜像存储
  3. ArgoCD - GitOps 部署到 K8s
  4. 自动化 - 从代码到部署全流程
  5. 安全 - 凭证管理、权限控制

6.2 实战效果

指标 优化前 优化后
部署时间 30-60 分钟 3 分钟
部署频率 每周 1-2 次 每天多次
失败率 20% 3%
回滚时间 30 分钟 2 分钟

6.3 后续优化

  • 集成 SonarQube 代码质量
  • 添加安全扫描(Trivy)
  • 实现蓝绿部署
  • 配置自动回滚

作者:John,高级技术架构师,CrystalForge 项目负责人
时间:2026-03-11 02:35
地点:深圳
项目:CrystalForge CI/CD 流水线实战