为什么选 GitLab CI?
公司用 GitLab,CI/CD 就直接用 GitLab CI 了。不用单独搭 Jenkins,配置都在代码仓库里,看起来很方便。
刚开始确实不错,写个 .gitlab-ci.yml 就能跑。但用久了,问题就来了。
第一个坑:构建慢
最明显的问题是构建慢。每次 push 代码,都要等 5-10 分钟才能看到结果。开发效率很低。
分析了一下,发现主要是这几个原因:
- 每次都要安装依赖,
npm install要 2-3 分钟 - 没有缓存,重复构建重复安装
- 任务串行执行,不能并行
- Runner 配置低,CPU 和内存不够
解决方案:缓存和并行
我们做了几个优化:
1. 缓存 node_modules
用 GitLab CI 的 cache 功能,缓存 node_modules。但问题来了,缓存经常失效。
原因是 package.json 改了,缓存 key 就变了,之前的缓存就用不了。我们改成用 package-lock.json 的 hash 做 key,才稳定一些。
2. 并行任务
把测试、构建、部署分成不同的 stage,可以并行执行。但 GitLab CI 的并行有限制,Runner 数量不够,还是慢。
后来我们用了 parallel 关键字,把测试任务分成多个并行任务,才快了一些。
3. 升级 Runner
最后升级了 Runner,从 2 核 4G 升级到 4 核 8G,构建时间从 10 分钟降到 5 分钟。
第二个坑:环境变量管理
GitLab CI 的环境变量管理很麻烦。不同环境要用不同的变量,但 GitLab 的变量管理界面不好用。
我们试过几个方案:
- 在 GitLab 界面手动配置:容易出错,不好管理
- 用
variables在 yml 里写:敏感信息不能写代码里 - 用外部密钥管理:配置复杂,维护成本高
最后用了 GitLab 的变量组(Variable Groups),按环境分组管理,才方便一些。
第三个坑:Docker 构建
我们的项目要构建 Docker 镜像,但 GitLab CI 的 Docker 构建很慢。
问题主要是:
- 每次都要拉基础镜像,很慢
- Docker layer 缓存不生效
- 构建上下文太大,上传慢
我们做了这些优化:
- 用本地 Docker registry,基础镜像不用每次都拉
- 用
--cache-from复用之前的构建缓存 - 用
.dockerignore减少构建上下文 - 用多阶段构建,减小镜像体积
第四个坑:测试不稳定
CI 里的测试经常挂,但不是因为代码有问题,而是因为:
- 网络问题,API 请求超时
- 时间问题,时区不对
- 并发问题,测试之间有冲突
- 环境问题,依赖版本不对
我们做了这些改进:
- 测试用 mock,不依赖外部服务
- 统一时区,用 UTC
- 测试隔离,每个测试独立运行
- 固定依赖版本,用 lockfile
第五个坑:部署回滚
GitLab CI 的部署回滚很麻烦。如果部署出问题,要手动回滚,很费时间。
我们做了自动化回滚:
- 部署前先做健康检查
- 部署后自动测试,失败就回滚
- 保留之前的版本,方便回滚
现在的配置
现在的 CI/CD 流程:
- 代码 push 后,自动触发 CI
- 并行执行:lint、测试、构建
- 构建成功后,构建 Docker 镜像
- 推送到镜像仓库
- 自动部署到测试环境
- 测试通过后,手动触发生产部署
整个流程大概 5-8 分钟,比以前快了很多。
如果重新开始
如果重新配置 CI/CD,我会:
- 一开始就考虑缓存和并行
- 环境变量统一管理,不要分散
- 测试要稳定,不要依赖外部服务
- 做好监控和告警,及时发现问题
总结
CI/CD 配置是个持续优化的过程。一开始可能很慢,但慢慢优化,总能找到问题所在。
如果你们的 CI/CD 也很慢,建议先看缓存,再看并行,最后看 Runner 配置。大部分问题都能在这几个地方找到。
另外,CI/CD 配置要文档化,不然时间长了,没人知道为什么这么配置。我们吃过这个亏,后来花了很多时间才理清楚。