首页技术专题博客目录我的收藏关于与联系

CI/CD 的那些痛点:GitLab CI 踩坑记

用 GitLab CI 做持续集成,配置写了大半年,踩了不少坑。从构建慢到缓存失效,从环境变量到并行任务,这篇文章记录一下遇到的问题和解决方案。

为什么选 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 流程:

  1. 代码 push 后,自动触发 CI
  2. 并行执行:lint、测试、构建
  3. 构建成功后,构建 Docker 镜像
  4. 推送到镜像仓库
  5. 自动部署到测试环境
  6. 测试通过后,手动触发生产部署

整个流程大概 5-8 分钟,比以前快了很多。

如果重新开始

如果重新配置 CI/CD,我会:

  • 一开始就考虑缓存和并行
  • 环境变量统一管理,不要分散
  • 测试要稳定,不要依赖外部服务
  • 做好监控和告警,及时发现问题

总结

CI/CD 配置是个持续优化的过程。一开始可能很慢,但慢慢优化,总能找到问题所在。

如果你们的 CI/CD 也很慢,建议先看缓存,再看并行,最后看 Runner 配置。大部分问题都能在这几个地方找到。

另外,CI/CD 配置要文档化,不然时间长了,没人知道为什么这么配置。我们吃过这个亏,后来花了很多时间才理清楚。