从零搭建CI集成流水线的那些坑我都帮你踩过了

Des.怡瑶 工具 阅读 2,778
赞 16 收藏
二维码
手机扫码查看
反馈

优化前:CI流水线慢得要命

最近把项目迁移到新的CI/CD流程,结果发现构建时间从原来的3分钟拖到了快8分钟,每次提交代码都得等半天,简直要命。最恶心的是测试阶段,光跑单元测试就要花4分多钟,感觉整个流程都在浪费时间。

从零搭建CI集成流水线的那些坑我都帮你踩过了

我查了下之前的构建日志,发现几个明显的问题:依赖安装太慢、缓存没利用好、还有些测试其实可以在并行执行却串行跑了。最让我崩溃的是Docker镜像层没有合理利用,每次构建都是重新拉取所有依赖。

找到瓶颈了!

用GitHub Actions的详细日志看了下各步骤耗时,问题基本集中在几个地方:

  • npm install 用了快2分钟
  • 单元测试执行时间过长
  • Docker build 时间能压缩但没处理
  • 多个环境部署串行执行

我用 npm ls --depth=0npm audit 先确认了依赖树没有循环引用,然后重点看缓存策略。确实,之前的 .npmrc 配置有问题,而且 Actions 中的缓存key设置也不够精细。

核心优化方案

先说最重要的依赖安装优化。原来的做法是每次都全量安装,现在改成按package-lock.json的hash值缓存node_modules:

- name: Get npm cache directory
  id: npm-cache
  run: |
    echo "::set-output name=dir::$(npm config get cache)"

- name: Cache node modules
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

- name: Install dependencies
  run: npm ci --prefer-offline --no-audit

这样改完后依赖安装时间从2分钟降到了30秒左右,效果很明显。npm ci本身就比npm install快不少,加上离线模式更是减少了网络请求。

接下来是测试环节的并行化。之前一个测试文件一个测试文件地跑,现在我把测试分成4个组,并行执行:

# 定义矩阵策略
strategy:
  matrix:
    shard: [1, 2, 3, 4]

# 测试命令改为分片执行
run: npm run test -- --shard=${{ matrix.shard }}/4

这样原本需要4分多钟的测试,现在1分钟出头就能搞定。不过要注意测试数据隔离,避免多个实例同时操作数据库产生冲突。

还有就是Docker镜像优化,这部分改动最大。原来的Dockerfile每层都没考虑到缓存复用:

# 优化前:所有东西一股脑放进去
COPY . .
RUN npm install
RUN npm run build

# 优化后:分层缓存
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

COPY . .
RUN npm run build

这样做的好处是只有package.json变化时才会重新install依赖,大部分时候直接用缓存。构建时间从原来的3分钟降到了1分半。

缓存策略深度优化

除了node_modules,我还针对构建产物做了缓存。特别是TypeScript编译的增量缓存:

- name: Cache TypeScript build
  uses: actions/cache@v3
  with:
    path: |
      dist/**
      .tsbuildinfo
    key: ${{ runner.os }}-typescript-build-${{ hashFiles('src/**', 'tsconfig.json') }}

配合tsconfig.json启用incremental编译:

{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": ".tsbuildinfo"
  }
}

这个改动让后续的构建速度提升了不少,特别是只改了一个小文件的情况下,编译时间从30秒降到了5秒以内。

CI配置精简

之前有些不必要的步骤,比如每次都重新初始化环境变量、重复的权限检查等。我把这些冗余操作都删掉了,同时把一些可以合并的步骤合并起来。

还有一个容易忽略的地方是文件同步,原来每步都用cp命令复制文件,现在统一用actions/upload-artifact和actions/download-artifact,减少I/O操作。

- name: Upload build artifacts
  uses: actions/upload-artifact@v3
  with:
    name: build-output
    path: dist/
    retention-days: 7

性能数据对比

优化前后对比数据:

  • 总构建时间:7分45秒 → 2分18秒
  • 依赖安装:2分15秒 → 32秒
  • 单元测试:4分20秒 → 1分15秒
  • Docker构建:3分05秒 → 1分25秒

整体效率提升了约70%,这个提升还是很明显的。最重要的是开发体验变好了,提交代码后很快就能看到结果,不用干等着。

不过优化过程中也踩过几个坑。比如并行测试时数据库连接池不够用导致测试失败,后来加了数据库连接池的配置才解决。还有缓存key的设计,一开始用的太宽泛导致缓存命中率不高。

持续监控很重要

光优化完还不够,我还加了监控告警,一旦构建时间超过3分钟就发通知。用GitHub的workflow_run事件来监听构建状态:

on:
  workflow_run:
    workflows: ["CI Build"]
    types: [completed]

jobs:
  notify:
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - name: Check build time
        run: |
          duration=$(curl -s -H "Authorization: token $GITHUB_TOKEN" 
            "${{ github.event.workflow_run.artifacts_url }}" | jq -r '.artifacts[0].created_at')
          # 添加超时检查逻辑

这样可以及时发现性能退化,防止后续提交影响构建效率。

还有提升空间

目前的优化主要集中在构建环节,后续还打算做几个改进:

  • 进一步细分测试模块,提高并行度
  • 考虑用更快的包管理器,比如yarn 2.0+
  • 引入更智能的缓存策略,根据文件变化频率动态调整

不过现有的改动已经满足当前需求了,整个CI流程跑起来顺畅多了。

以上是我个人对CI集成性能优化的完整讲解,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论