GitLab CI 缓存完全指南:加速构建的终极实践
2026/7/5 4:32:25 网站建设 项目流程

GitLab CI 缓存完全指南:加速构建的终极实践

    • 一、缓存的核心价值与工作原理
      • 1.1 🟢 什么是缓存?
      • 1.2 🔵 缓存 vs 制品:本质区别
    • 二、缓存配置基础语法
      • 2.1 🟡 cache 关键字基本结构
      • 2.2 🟠 缓存键(key)策略
      • 2.3 🔴 缓存策略(policy)
    • 三、多语言缓存实践
      • 3.1 🟣 JavaScript/Node.js 缓存
      • 3.2 🔵 Python 缓存
      • 3.3 🟡 Ruby 缓存
      • 3.4 🟠 Go 缓存
    • 四、高级缓存策略
      • 4.1 多缓存与回退键
      • 4.2 全局回退键
      • 4.3 禁用缓存或按作业覆盖
      • 4.4 分布式缓存
    • 五、完整实践示例
    • 六、总结

🌺The Begin🌺点点关注,收藏不迷路🌺

⬇ ⬇ 底部 ⬇ ⬇

⚡ 在 CI/CD 流程中,每次构建都重新下载依赖项是效率最低的行为之一。GitLab CI 的缓存机制通过复用之前下载的内容,能显著缩短流水线执行时间。本文将全面解析缓存的工作原理、配置方法及多语言实践方案。

一、缓存的核心价值与工作原理

1.1 🟢 什么是缓存?

在 GitLab CI 中,缓存是作业下载并保存的一个或多个文件。使用相同缓存的后续作业无需再次下载这些文件,因此运行速度更快 。

💡核心原则:缓存是为了加速你的作业,但它可能不存在,所以不要依赖它来传递必要的构建结果 。缓存是一个优化手段,而非必需的数据传递机制。

1.2 🔵 缓存 vs 制品:本质区别

理解缓存和制品的区别,是正确使用两者的前提:

对比维度🟡 缓存(Cache)🔴 制品(Artifacts)
用途存储从互联网下载的依赖项在阶段间传递中间构建结果
存储位置Runner 本地或分布式缓存(S3)GitLab 服务器
跨流水线✅ 后续流水线可复用❌ 同一流水线结束后通常过期
生命周期长期存在,按 key 复用默认 30 天过期
依赖关系可选,不存在时重新下载即可必需,后续阶段依赖其结果

一句话总结

缓存是为了"加快速度"但可以不存在,制品是为了"传递结果"所以必须存在。

流水线 #2

流水线 #1

生成

下载并保存

下载

命中

跨流水线复用

同一流水线传递

Job A

制品
存储于GitLab

缓存
存储于Runner

Job B

制品
后续阶段使用

缓存
加速构建

二、缓存配置基础语法

2.1 🟡 cache 关键字基本结构

.gitlab-ci.yml中,使用cache关键字定义缓存:

job:cache:key:"$CI_COMMIT_REF_SLUG"# 缓存的唯一标识paths:# 需要缓存的目录或文件-vendor/-node_modules/policy:pull-push# 缓存策略(拉取/推送)

核心字段说明

字段作用示例
key缓存的唯一标识,决定不同作业/分支是否共享缓存$CI_COMMIT_REF_SLUG
paths需要缓存的文件或目录路径(相对于项目根目录)- node_modules/
policy缓存策略:pull(只拉取)、push(只推送)、pull-push(默认)policy: pull

2.2 🟠 缓存键(key)策略

缓存键是决定缓存能否被复用的核心。不同的 key 会生成不同的缓存,互不影响。

按分支隔离(推荐)

cache:key:$CI_COMMIT_REF_SLUG# 每个分支独立缓存paths:-node_modules/

此配置可防止不同分支意外覆盖缓存 。

按作业和分支隔离

cache:key:"$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"# 每个作业+分支独立缓存paths:-vendor/

按依赖文件计算键(精准失效)

cache:key:files:# 根据文件内容生成哈希作为缓存键-package-lock.jsonpaths:-node_modules/

package-lock.json变化时,缓存自动失效,确保依赖变更时重新下载 。

分支间共享缓存(谨慎使用)

cache:key:"global-cache"# 所有分支共享同一个缓存paths:-shared/

2.3 🔴 缓存策略(policy)

policy控制作业如何与缓存交互:

策略行为适用场景
pull-push(默认)拉取已有缓存,执行后推送更新大多数场景
pull只拉取不推送只读作业(如测试),避免不必要的缓存写入
push不拉取,执行后推送负责生成缓存的专用作业

实用示例:条件化缓存策略

根据分支动态调整策略,减少默认分支的缓存写入开销 :

conditional-policy:rules:-if:$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHvariables:POLICY:pull-push-if:$CI_COMMIT_BRANCH!=$CI_DEFAULT_BRANCHvariables:POLICY:pullcache:key:gemspolicy:$POLICYpaths:-vendor/bundlescript:-echo "缓存策略根据分支动态变化"

三、多语言缓存实践

3.1 🟣 JavaScript/Node.js 缓存

npm 项目:将 npm 缓存目录放在项目内,按分支缓存 :

default:image:node:latestcache:key:$CI_COMMIT_REF_SLUGpaths:-.npm/before_script:-npm ci--cache .npm--prefer-offline# 使用缓存离线安装

基于锁文件计算键(推荐):

cache:key:files:-package-lock.jsonpaths:-node_modules/

Yarn 项目:使用yarn-offline-mirror缓存压缩包 :

cache:key:files:-yarn.lockpaths:-.yarn-cache/

3.2 🔵 Python 缓存

pip 项目:设置PIP_CACHE_DIR到项目内目录 :

variables:PIP_CACHE_DIR:"$CI_PROJECT_DIR/.cache/pip"cache:key:files:-requirements.txtpaths:-.cache/pip-venv/

3.3 🟡 Ruby 缓存

Bundler 项目:将 gem 安装到项目内目录并缓存 :

cache:key:files:-Gemfile.lockprefix:$CI_JOB_NAME# 不同作业使用不同缓存前缀paths:-vendor/rubybefore_script:-bundle config set--local path 'vendor/ruby'-bundle install-j $(nproc)

3.4 🟠 Go 缓存

Go Modules:设置GOPATH到项目内目录 :

variables:GOPATH:$CI_PROJECT_DIR/.gocache:paths:-.go/pkg/mod/# 缓存 Go 模块

四、高级缓存策略

4.1 多缓存与回退键

使用多个缓存(每个作业最多4个):

test-job:cache:-key:files:-Gemfile.lockpaths:-vendor/ruby-key:files:-yarn.lockpaths:-.yarn-cache/

回退缓存键:当找不到指定缓存时,尝试使用回退缓存 :

test-job:cache:-key:cache-$CI_COMMIT_REF_SLUGfallback_keys:-cache-$CI_DEFAULT_BRANCH# 主分支缓存作为回退-cache-default# 最终回退paths:-vendor/ruby

这确保了新分支能复用主分支的缓存,加速首次构建。

4.2 全局回退键

使用CACHE_FALLBACK_KEY变量指定全局回退缓存 :

variables:CACHE_FALLBACK_KEY:global-fallbackjob:cache:key:"$CI_COMMIT_REF_SLUG"paths:-binaries/

4.3 禁用缓存或按作业覆盖

完全禁用缓存

job:cache:[]# 空列表表示该作业不使用缓存

继承全局配置并覆盖特定设置(使用 YAML 锚点):

default:cache:&global_cachekey:$CI_COMMIT_REF_SLUGpaths:-node_modules/policy:pull-pushjob:cache:<<:*global_cache# 继承所有设置policy:pull# 仅覆盖 policy

4.4 分布式缓存

如果使用多个 Runner,需要启用分布式缓存以确保在不同 Runner 间共享缓存 :

config.toml中配置 S3 作为共享缓存后端:

[[runners]] [runners.cache] Type = "s3" Path = "bucket/path/prefix" Shared = true [runners.cache.s3] BucketName = "foobar" AccessKey = "<changeme>" SecretKey = "<changeme>"

GitLab.com 的共享 Runner 即采用分布式缓存方式 。

五、完整实践示例

以下是一个综合配置示例,结合了多语言缓存和最佳实践:

stages:-deps-test-builddefault:image:node:latestvariables:PIP_CACHE_DIR:"$CI_PROJECT_DIR/.cache/pip"GOPATH:$CI_PROJECT_DIR/.go# 安装依赖的专用作业(推送缓存)install deps:stage:depsscript:-npm install jest-junitcache:-key:$CI_COMMIT_REF_SLUGpaths:-node_modules/-key:files:-requirements.txtpaths:-.cache/pippolicy:push# 测试作业(只拉取缓存)test:stage:testbefore_script:-npm install-g jestscript:-jest binarysearch.test.jscache:key:$CI_COMMIT_REF_SLUGpaths:-node_modules/policy:pull# Python 测试(独立缓存)python-test:stage:testimage:python:latestcache:key:files:-requirements.txtpaths:-.cache/pipbefore_script:-pip install-r requirements.txt--cache-dir .cache/pipscript:-pytest .

六、总结

实践要点说明
🟢缓存定位缓存是为了加速构建,不是必需的传递机制
🔵键设计使用$CI_COMMIT_REF_SLUG按分支隔离,或用files基于依赖文件生成键
🟡多缓存每个作业最多4个缓存,可分别缓存不同依赖
🟠策略选择pull策略避免不必要的缓存写入
🔴分布式多 Runner 环境下启用 S3 分布式缓存
🟣回退键通过fallback_keys让新分支复用主分支缓存

🔑核心启示:GitLab CI 缓存的本质是用空间换时间——通过复用依赖文件换取更快的构建速度。合理的缓存键设计是成功的关键:过于宽泛会导致缓存污染,过于精细则命中率低。推荐使用files结合锁文件的方式,在依赖变更时自动失效,在依赖不变时稳定命中。


🌺The End🌺点点关注,收藏不迷路🌺

⬆ ⬆ 顶部 ⬆ ⬆

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询