Azure DevOps Services
管道缓存可以通过重用以前运行中下载的依赖项来帮助缩短构建时间,而无需重新创建或重新下载相同的文件。 在每次运行开始时重复下载相同依赖项的情况下,这特别有用。 这通常是一个耗时的过程,涉及数百或数千个网络调用。
当还原和保存缓存所需的时间少于重新生成文件所需的时间时,缓存最有效。 但是,在某些情况下,缓存可能无法提供性能优势,甚至可能对构建时间产生负面影响。 请务必评估您的特定方案以确定缓存是否是正确的方法。
何时使用管道项目与管道缓存
管道缓存和 管道项目 执行类似的功能,但适用于不同的场景,不应互换使用。
注意
管道缓存和管道构件适用于所有层级(免费和付费),均免费提供。 有关更多详细信息,请参阅 Artifacts 存储消耗。
自承载代理要求
以下可执行文件必须位于环境变量中列出的 PATH
文件夹中。 请注意,这些要求仅适用于自承载代理,因为托管代理预装了必要的软件。
存档软件/平台 |
Windows操作系统 |
Linux的 |
Mac |
GNU Tar |
必需 |
必需 |
否 |
BSD 焦油 |
否 |
否 |
必需 |
7 拉链 |
建议 |
否 |
否 |
缓存任务:工作原理
通过将 Cache 任务steps
添加到作业的部分,将缓存添加到管道中。
在管道执行期间,当遇到缓存步骤时,任务会尝试根据提供的输入恢复缓存。 如果未找到缓存,则步骤完成,并执行作业中的下一步。
作业中的所有步骤都成功运行后,将自动添加一个特殊的 “作业后:缓存” 步骤,并为每个未跳过的 “还原缓存” 步骤触发。 此步骤负责保存缓存。
注意
缓存是不可变的。 创建缓存后,无法修改其内容。
缓存任务有两个必需的参数:path 和 key:
path:要缓存的文件夹的路径。 这可以是绝对路径或相对路径。 针对 $(System.DefaultWorkingDirectory)
解析相对路径。
提示
您可以使用 预定义变量 来存储要缓存的文件夹的路径。 但是,不支持通配符。
key:定义要还原或保存的缓存的标识符。 该键由字符串值、文件路径或文件模式的组合组成,每个段由一个 |
字符分隔。
字符串:
固定值(如缓存名称或工具名称),或取自环境变量(如当前作系统或作业名称)。
文件路径:
将对其内容进行哈希处理的特定文件的路径。 该文件在任务运行时必须存在。 任何类似于文件路径的段都被视为此类,因此请谨慎,尤其是在使用包含 .
的段时,因为这可能会导致“文件不存在”失败。
提示
要避免将类似路径的字符串段视为文件路径,请用双引号将其括起来,例如:"my.key" | $(Agent.OS) | key.file
文件模式:
必须与至少一个文件匹配的 glob 样式通配符模式的逗号分隔列表。 例子:
-
**/yarn.lock
:sources 目录下的所有 yarn.lock 文件。
-
*/asset.json, !bin/**
:所有 asset.json 文件都位于 sources 目录下的目录中, 但 bin 目录中的文件除外。
由文件路径或文件模式标识的任何文件的内容都经过哈希处理,以生成动态缓存键。 当您的项目具有唯一标识要缓存的内容的文件时,这非常有用。 例如,像 、 yarn.lock
、 Gemfile.lock
或 Pipfile.lock
这样的package-lock.json
文件通常在缓存键中引用,因为它们表示一组唯一的依赖项。 相对文件路径或模式根据 $(System.DefaultWorkingDirectory)
进行解析。
以下示例展示了如何缓存 Yarn 包:
variables:
YARN_CACHE_FOLDER: $(Pipeline.Workspace)/s/.yarn
steps:
- task: Cache@2
inputs:
key: '"yarn" | "$(Agent.OS)" | yarn.lock'
restoreKeys: |
"yarn" | "$(Agent.OS)"
"yarn"
path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- script: yarn --frozen-lockfile
在此示例中,缓存键由三部分组成:静态字符串 (“yarn”)、运行作业的作系统(因为缓存对于每个作系统都是唯一的)和文件的哈希 yarn.lock
值(唯一标识依赖项)。
在添加任务后的第一次运行时,缓存步骤将报告“缓存未命中”,因为此键标识的缓存不存在。 完成最后一个步骤后,将通过 $(Pipeline.Workspace)/s/.yarn
中的文件创建缓存并上传缓存。 下次运行时,缓存步骤将报告“缓存命中”,缓存的内容将下载并还原。
使用 checkout: self
时,存储库将签出到 $(Pipeline.Workspace)/s
,并且您的 .yarn
文件夹可能位于存储库本身中。
注意
Pipeline.Workspace
是运行管道的代理上的本地路径,在其中创建了所有目录。 此变量的值与 Agent.BuildDirectory
相同。
如果您未使用 checkout: self
,请确保更新 YARN_CACHE_FOLDER
变量以指向存储库中的位置 .yarn
。
使用还原键
restoreKeys
允许您查询多个 exact key 或 key prefix。 当指定的 key
值未产生命中时,它用作回退。 还原键按前缀搜索键,并返回最近创建的缓存条目。 当管道找不到完全匹配项但仍希望使用部分缓存命中时,这非常有用。
要指定多个还原键,请在单独的行中列出它们。 尝试还原键的顺序是从上到下。
以下是如何使用恢复键缓存 Yarn 包的示例:
variables:
YARN_CACHE_FOLDER: $(Pipeline.Workspace)/.yarn
steps:
- task: Cache@2
inputs:
key: '"yarn" | "$(Agent.OS)" | yarn.lock'
restoreKeys: |
yarn | "$(Agent.OS)"
yarn
path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- script: yarn --frozen-lockfile
在此示例中,缓存任务首先尝试恢复指定的密钥。 如果缓存中不存在该键,则它会尝试第一个还原键: yarn | $(Agent.OS)
。 这将搜索与此前缀完全匹配或以此前缀开头的任何缓存键。
如果文件的哈希值已更改, yarn.lock
则可能会发生前缀匹配。 例如,如果缓存包含键 yarn | $(Agent.OS) | old-yarn.lock
(其中 old-yarn.lock
的哈希值与当前 yarn.lock
值不同),则此还原键将导致部分缓存命中。
如果第一个还原键未产生匹配项,则下一个还原键 (yarn
) 这将搜索任何以 yarn
. 对于前缀匹配,还原过程将返回最近创建的缓存条目。
注意
一个管道可以包含多个缓存任务,并且缓存没有存储限制。 同一管道中的作业和任务可以访问和共享同一缓存。
使用还原条件
在某些情况下,您可能希望根据缓存是否成功还原来有条件地执行步骤。 例如,如果缓存已还原,则可以跳过安装依赖项的步骤。 这可以使用 argument cacheHitVar
来实现。
将此输入设置为环境变量的名称会导致变量设置为 true
在出现缓存命中、 inexact
还原键产生部分缓存命中以及 false
未找到缓存时。 然后,您可以在 步骤条件 或脚本中引用此变量。
下面是一个示例,其中在还原缓存时跳过该 install-deps.sh
步骤:
steps:
- task: Cache@2
inputs:
key: mykey | mylockfile
restoreKeys: mykey
path: $(Pipeline.Workspace)/mycache
cacheHitVar: CACHE_RESTORED
- script: install-deps.sh
condition: ne(variables.CACHE_RESTORED, 'true')
- script: build.sh
缓存隔离和安全性
为了确保来自不同管道和不同分支的缓存之间的隔离,每个缓存都存储在称为 范围的逻辑容器中。 范围充当安全边界,可保证:
在运行期间遇到缓存步骤时,将从服务器请求由键标识的缓存。 然后,服务器从作业可见的作用域中寻找具有此键的缓存,并返回缓存(如果可用)。 在缓存保存时(作业结束时),缓存被写入表示管道和分支的作用域。
CI、手动和计划运行
作用域 |
读取 |
写入 |
源分支 |
是 |
是 |
main 分支 |
是 |
否 |
main 分支 |
是 |
否 |
拉取请求运行
作用域 |
读取 |
写入 |
源分支 |
是 |
否 |
目标分支 |
是 |
否 |
中间分支(如 refs/pull/1/merge ) |
是 |
是 |
main 分支 |
是 |
否 |
main 分支 |
是 |
否 |
拉取请求分叉运行
分支 |
读取 |
写入 |
目标分支 |
是 |
否 |
中间分支(如 refs/pull/1/merge ) |
是 |
是 |
main 分支 |
是 |
否 |
main 分支 |
是 |
否 |
提示
由于缓存已限定为项目、管道和分支,因此无需在缓存密钥中包含任何项目、管道或分支标识符。
例子
对于使用 Bundler 的 Ruby 项目,请覆盖环境变量以 BUNDLE_PATH
设置 Bundler 查找 Gem 的 路径 。
示例:
variables:
BUNDLE_PATH: $(Pipeline.Workspace)/.bundle
steps:
- task: Cache@2
displayName: Bundler caching
inputs:
key: 'gems | "$(Agent.OS)" | Gemfile.lock'
path: $(BUNDLE_PATH)
restoreKeys: |
gems | "$(Agent.OS)"
gems
Ccache 是 C/C++ 的编译器缓存。 要在管道中使用 Ccache,请确保已安装 Ccache
并选择性地添加到 PATH
(请参阅 Ccache 运行模式)。 将 CCACHE_DIR
环境变量设置为 $(Pipeline.Workspace)
下面的路径,并缓存此目录。
示例:
variables:
CCACHE_DIR: $(Pipeline.Workspace)/ccache
steps:
- bash: |
sudo apt-get install ccache -y
echo "##vso[task.prependpath]/usr/lib/ccache"
displayName: Install ccache and update PATH to use linked versions of gcc, cc, etc
- task: Cache@2
displayName: Ccache caching
inputs:
key: 'ccache | "$(Agent.OS)" | $(Build.SourceVersion)'
path: $(CCACHE_DIR)
restoreKeys: |
ccache | "$(Agent.OS)"
有关更多详细信息,请参阅 Ccache 配置设置。
缓存 Docker 镜像可以显著减少运行管道所需的时间。
variables:
repository: 'myDockerImage'
dockerfilePath: '$(Build.SourcesDirectory)/app/Dockerfile'
tag: '$(Build.BuildId)'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Cache@2
displayName: Cache task
inputs:
key: 'docker | "$(Agent.OS)" | cache' ## A unique identifier for the cache
path: $(Pipeline.Workspace)/docker ## Path of the folder or file that you want to cache
cacheHitVar: CACHE_RESTORED ## Variable to set to 'true' when the cache is restored
- script: |
docker load -i $(Pipeline.Workspace)/docker/cache.tar
displayName: Docker restore
condition: and(not(canceled()), eq(variables.CACHE_RESTORED, 'true'))
- task: Docker@2
displayName: 'Build Docker'
inputs:
command: 'build'
repository: '$(repository)'
dockerfile: '$(dockerfilePath)'
tags: |
'$(tag)'
- script: |
mkdir -p $(Pipeline.Workspace)/docker
docker save -o $(Pipeline.Workspace)/docker/cache.tar $(repository):$(tag)
displayName: Docker save
condition: and(not(canceled()), not(failed()), ne(variables.CACHE_RESTORED, 'true'))
使用 docker buildx 的示例:
steps:
- task: Cache@2
displayName: Cache Docker
inputs:
key: 'docker | "$(Agent.OS)" | mydockerimage | ./Dockerfile'
path: $(Pipeline.Workspace)/docker_image_cache
restoreKeys: |
docker | "$(Agent.OS)" | mydockerimage
- script: |
docker buildx create --name builder --driver docker-container --use
docker buildx build \
--cache-from=type=local,src=$(Pipeline.Workspace)/docker_image_cache \
--cache-to=type=local,dest=$(Pipeline.Workspace)/docker_image_cache,mode=max \
--file ./Dockerfile \
--output=type=docker,name=mydockerimage \
.
displayName: docker buildx
env:
DOCKER_BUILDKIT: 1
对于 Golang 项目,可以指定要在 go.mod 文件中下载的包。 如果尚未设置 GOCACHE
变量,请将其设置为要下载缓存的位置。
示例:
variables:
GO_CACHE_DIR: $(Pipeline.Workspace)/.cache/go-build/
steps:
- task: Cache@2
inputs:
key: 'go | "$(Agent.OS)" | go.mod'
restoreKeys: |
go | "$(Agent.OS)"
path: $(GO_CACHE_DIR)
displayName: Cache GO packages
使用 Gradle 的 内置缓存支持可能会对生成时间产生重大影响。 要启用生成缓存,请将 GRADLE_USER_HOME
环境变量设置为 $(Pipeline.Workspace)
下的路径,并使用 --build-cache
运行生成或将 org.gradle.caching=true
添加到 gradle.properties
文件。
示例:
variables:
GRADLE_USER_HOME: $(Pipeline.Workspace)/.gradle
steps:
- task: Cache@2
inputs:
key: 'gradle | "$(Agent.OS)" | **/build.gradle.kts' # Swap build.gradle.kts for build.gradle when using Groovy
restoreKeys: | # The fallback keys if the primary key fails (Optional)
gradle | "$(Agent.OS)"
gradle
path: $(GRADLE_USER_HOME)
displayName: Configure gradle caching
- task: Gradle@2
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'build'
options: '--build-cache'
displayName: Build
- script: |
# stop the Gradle daemon to ensure no files are left open (impacting the save cache operation later)
./gradlew --stop
displayName: Gradlew stop
注意
缓存是不可变的。 为特定范围 ((如分支) )创建具有特定键的缓存后,将无法更新该缓存。 这意味着,如果您使用固定的键值,则同一分支的所有后续构建将无法更新缓存,即使缓存内容已更改。 如果您使用固定键,请务必将 restoreKeys
input 指定为 fallback 选项。
Maven 使用本地存储库来存储下载的依赖项和构建的工件。 要启用缓存,请将 maven.repo.local
选项设置为 and cache this folder 下的 $(Pipeline.Workspace)
路径。
示例:
variables:
MAVEN_CACHE_FOLDER: $(Pipeline.Workspace)/.m2/repository
MAVEN_OPTS: '-Dmaven.repo.local=$(MAVEN_CACHE_FOLDER)'
steps:
- task: Cache@2
inputs:
key: 'maven | "$(Agent.OS)" | **/pom.xml'
restoreKeys: |
maven | "$(Agent.OS)"
maven
path: $(MAVEN_CACHE_FOLDER)
displayName: Cache Maven local repo
- script: mvn install -B -e
如果使用 Maven 任务,请确保同时传递 MAVEN_OPTS
变量,如若不然,此变量会被覆盖:
- task: Maven@4
inputs:
mavenPomFile: 'pom.xml'
mavenOptions: '-Xmx3072m $(MAVEN_OPTS)'
如果使用 PackageReferences
直接在项目文件中管理 NuGet 依赖项并且具有 packages.lock.json
文件,则可以按如下方式来启用缓存:将 NUGET_PACKAGES
环境变量设置为 $(UserProfile)
下面的路径并缓存此目录。 有关如何锁定依赖项的更多详细信息,请参阅项目文件中的包引用。
如果要使用多个 packages.lock.json,您仍然可以使用以下示例,而无需进行任何更改。 所有 packages.lock.json 文件的内容都将被哈希处理,如果其中一个文件发生更改,则会生成新的缓存键。
示例:
variables:
NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
steps:
- task: Cache@2
inputs:
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/**/packages.lock.json'
restoreKeys: |
nuget | "$(Agent.OS)"
nuget
path: $(NUGET_PACKAGES)
displayName: Cache NuGet packages
如果项目使用 packages.lock.json 锁定包版本,此方法也适用于 .NET Core 项目。 通过在 RestorePackagesWithLockFile
文件中将 True
设置为 ,或者使用以下命令来启用此功能:dotnet restore --use-lock-file
。
在 Node.js 项目中启用缓存的方法有多种,但推荐的方法是缓存 npm 的 共享缓存目录。 此目录由 npm 管理,并包含所有已下载模块的缓存版本。 在安装过程中,npm 首先检查此目录(默认情况下),这可以减少或消除对公共 npm 注册表或私有注册表的网络调用。
由于 npm 共享缓存目录的默认路径因平台而异,因此建议覆盖npm_config_cache
环境变量并将其设置为 .$(Pipeline.Workspace)
这还可以确保可以从容器和非容器作业访问缓存。
示例:
variables:
npm_config_cache: $(Pipeline.Workspace)/.npm
steps:
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(npm_config_cache)
displayName: Cache npm
如果项目没有 package-lock.json
文件,请改为在缓存键输入中引用 package.json
文件。
提示
由于会node_modules
删除文件夹以确保模块集的一致性和可重复性,因此npm ci
请在node_modules
使用 npm ci
.
与 npm 一样,Yarn 提供了多种缓存已安装包的方法。 推荐的方法是缓存 Yarn 的 共享缓存文件夹。 此目录由 Yarn 管理,并存储所有下载的包的缓存版本。 在安装过程中,Yarn 首先检查此目录(默认情况下),这可以减少甚至消除对公共或私有注册表的网络调用。
示例:
variables:
YARN_CACHE_FOLDER: $(Pipeline.Workspace)/.yarn
steps:
- task: Cache@2
inputs:
key: 'yarn | "$(Agent.OS)" | yarn.lock'
restoreKeys: |
yarn | "$(Agent.OS)"
yarn
path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- script: yarn --frozen-lockfile
使用 Anaconda 环境设置管道缓存:
示例
variables:
CONDA_CACHE_DIR: /usr/share/miniconda/envs
# Add conda to system path
steps:
- script: echo "##vso[task.prependpath]$CONDA/bin"
displayName: Add conda to PATH
- bash: |
sudo chown -R $(whoami):$(id -ng) $(CONDA_CACHE_DIR)
displayName: Fix CONDA_CACHE_DIR directory permissions
- task: Cache@2
displayName: Use cached Anaconda environment
inputs:
key: 'conda | "$(Agent.OS)" | environment.yml'
restoreKeys: |
python | "$(Agent.OS)"
python
path: $(CONDA_CACHE_DIR)
cacheHitVar: CONDA_CACHE_RESTORED
- script: conda env create --quiet --file environment.yml
displayName: Create Anaconda environment
condition: eq(variables.CONDA_CACHE_RESTORED, 'false')
Windows
- task: Cache@2
displayName: Cache Anaconda
inputs:
key: 'conda | "$(Agent.OS)" | environment.yml'
restoreKeys: |
python | "$(Agent.OS)"
python
path: $(CONDA)/envs
cacheHitVar: CONDA_CACHE_RESTORED
- script: conda env create --quiet --file environment.yml
displayName: Create environment
condition: eq(variables.CONDA_CACHE_RESTORED, 'false')
对于使用 Composer 的 PHP 项目,请替代 Composer 使用的 COMPOSER_CACHE_DIR
环境变量。
示例:
variables:
COMPOSER_CACHE_DIR: $(Pipeline.Workspace)/.composer
steps:
- task: Cache@2
inputs:
key: 'composer | "$(Agent.OS)" | composer.lock'
restoreKeys: |
composer | "$(Agent.OS)"
composer
path: $(COMPOSER_CACHE_DIR)
displayName: Cache composer
- script: composer install
已知问题和反馈
如果您在管道中设置缓存时遇到问题,请查看存储库中microsoft/azure-pipelines-tasks未解决的问题列表。 如果未看到问题列出,请创建一个新问题,并提供有关你的场景的必要信息。
问答
问:是否可以清除缓存?
答:不支持清除缓存。 但是,您可以通过向缓存键添加字符串文字(例如 version2
)来避免对现有缓存的命中。 例如,从中更改以下缓存密钥:
key: 'yarn | "$(Agent.OS)" | yarn.lock'
更改为:
key: 'version2 | yarn | "$(Agent.OS)" | yarn.lock'
问:缓存何时过期?
答:如果缓存七天无活动,则会过期。
问:何时会上传缓存?
答:缓存是根据您指定的 path
,并在作业的最后一步后上传。 有关更多详细信息,请参阅示例。
问:缓存的大小是否有限制?
答:对于组织内的单个缓存大小或总缓存大小,没有强制限制。
相关内容