深入理解持续集成工具
从 Jenkins 到 GitLab CI/CD:我的持续集成工具进阶之路

在我刚入行那会儿,公司的项目还是靠手动部署的。每次上线都像打仗一样紧张,谁也不敢轻易改代码,生怕一个不小心就搞出个线上故障。后来公司开始引入持续集成(CI)的概念,我也有机会一步步接触到各种 CI 工具。这篇文章想和你聊聊这几年我对 CI 工具的理解、踩过的坑,以及从 Jenkins 转向 GitLab CI/CD 的真实经历。
项目背景:业务发展推动 CI 演进
我们是一个 ToB 的 SaaS 公司,产品包括一个 Web 后台系统和多个 SDK 组件。起初团队规模不大,项目结构也简单,用的是一套基于 Jenkins 的 CI 流程:开发者本地提交代码到 GitLab,触发 Jenkins 自动拉取代码、构建 Docker 镜像并推送到镜像仓库。那时候,Jenkins 是“唯一选择”,一切运行得还算顺利。
但随着业务发展,我们的痛点逐渐暴露出来:
- 配置复杂:Jenkinsfile 写得越来越臃肿,多分支逻辑混乱。
- 环境不一致:不同项目的依赖版本管理难统一。
- 维护成本高:插件更新频繁,有时候升级一个插件就能让整个流水线跑不起来。
- 协作困难:非 DevOps 成员想要修改 CI 配置必须去 Jenkins 系统操作,不够透明。
这些问题最终促使我们重新思考 CI 工具的选择。
技术选型:为什么是 GitLab CI/README?
当时市面上流行的工具有 Jenkins、GitLab CI/CD、CircleCI、GitHub Actions。我们做了个简单的对比:
| 工具 | 优势 | 劣势 |
|---|---|---|
| Jenkins | 插件丰富,生态庞大 | 安装配置复杂,可维护性差 |
| GitLab CI/CD | 与 GitLab 深度集成,易上手 | 对非 GitLab 用户支持较弱 |
| GitHub Actions | 社区活跃,集成方便 | 私有化部署成本高 |
| CircleCI | 性能不错,界面简洁 | 价格较高,自定义程度一般 |
我们最终选择了 GitLab CI/CD,原因主要有两个:
- 我们已经在使用 GitLab 进行代码管理,无需额外切换平台;
- GitLab Runner 支持私有部署,便于我们在内部集群跑任务。
此外,YAML 形式的 .gitlab-ci.yml 让流程更透明、版本可控,也更容易做 Code Review。这对我们这种需要多人协作的团队来说非常重要。
实践过程:一次完整的迁移案例
在某个老项目中,我们决定先做一个试点迁移。原 Jenkins 流水线大概流程如下:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make build'
}
}
stage('Test') {
steps {
sh 'make test'
}
}
stage('Push Image') {
steps {
sh 'docker login registry -u user -p pass'
sh 'docker build -t myapp:latest .'
sh 'docker push myapp:latest'
}
}
}
}
转换成 GitLab CI 很直观:
image: docker:24.0
services:
- docker:dind
stages:
- build
- test
- deploy
build:
script:
- make build
test:
script:
- make test
deploy:
script:
- docker login registry -u $REGISTRY_USER -p $REGISTRY_PASS
- docker build -t myapp:latest .
- docker push myapp:latest
这里有几个小细节需要注意:
docker:dind服务用于在 GitLab Runner 中运行 Docker 命令;- 使用了内置的变量机制
$REGISTRY_USER和$REGISTRY_PASS,避免敏感信息硬编码; - 所有配置直接写在代码仓库中,可以随代码一起进行版本控制。
一开始我们也遇到一些问题:
问题1:权限错误导致 Docker 构建失败
刚开始执行 docker build 总是提示权限被拒绝。查了一圈发现是因为 GitLab Runner 默认运行在 gitlab-runner 用户下,对 /var/run/docker.sock 没有访问权限。
解决方式:
- 将
gitlab-runner用户添加到docker用户组:sudo usermod -aG docker gitlab-runner - 重启 GitLab Runner 服务以应用新权限。
这个看似基础的问题其实耽误了不少时间,也提醒我们不要忽视环境搭建的基础检查。
问题2:缓存命中率低
我们在构建前端项目时发现,每次 CI 都要重新安装 npm 包,非常慢。后来尝试引入缓存:
cache:
key: "$CI_COMMIT_REF_SLUG-node_modules"
paths:
- node_modules/
这一项改进直接把构建时间从平均 5 分钟缩短到 90 秒以内。
效果与收益
迁移完成后,整个项目的 CI 体验有了明显提升:
- 流水线更清晰:每个阶段都在
.gitlab-ci.yml中一目了然; - 易于维护:任何开发人员都可以通过 Pull Request 修改 CI 配置;
- 提升构建效率:通过合理缓存和并发控制,整体构建时间下降约 40%;
- 更好的安全控制:凭据集中管理,减少了硬编码泄露风险;
- 更少的运维负担:不用再频繁处理 Jenkins 插件冲突或性能瓶颈。
最让我感到惊喜的一点是:团队成员开始主动优化自己的 CI 步骤。以前他们只管写代码,现在会去看自己提交的变更对构建耗时有什么影响,甚至有人提出用 Linter 在 PR 阶段拦截格式问题。
几点经验分享
如果你也在考虑 CI 工具的选型,或者准备做迁移,我可以结合自己的经历给出几点建议:
1. 先从小范围试点开始
CI 工具一旦上线就会影响所有人的工作流,所以别上来就在全公司推广。我们是从一个小项目做起,验证可行性、收集反馈、逐步完善模板后再铺开。
2. 尽量保持流程透明、可读性强
GitLab CI 的一大优势就是把 CI 配置放到代码仓库里,这样不仅可以走 Review,还可以做版本回溯。这一点比 Jenkins 只保存在服务器磁盘里好太多了。
3. 重视缓存机制
构建效率直接影响开发体验。合理设置缓存目录和 Key,能大大减少重复下载和构建时间。
4. 安全问题不能忽视
- CI 中不要明文写密码,用环境变量;
- 权限最小化原则,例如 Runner 不要用 root 身份运行;
- 如果涉及敏感步骤(比如生产发布),可以考虑加上人工审批环节。
5. 多阶段 + 并行执行是个好习惯
将测试、打包、构建分开阶段,并尽可能利用并行 job,不仅提高执行效率,也方便排查问题。
写在最后
CI 工具不是万能的,但它确实能让我们的开发流程变得更高效、更规范。从 Jenkins 到 GitLab CI/CD,对我而言不只是技术栈的转变,更是对开发协作流程理解的加深。
中间也走了不少弯路,但正是这些“踩坑”让我对 CI 有了更深的认识。现在的我,看到一个新的项目,第一件事就是去检查有没有 .gitlab-ci.yml 或对应的 CI 文件 —— 因为我知道,它不仅是一个自动化脚本,更是一个项目能否快速迭代、稳定交付的基础设施。
希望这篇文章对你有所帮助。如果你正在面临类似的 CI 工具选型问题,欢迎留言交流,我很乐意听听你的故事!

评论 0