管道缓存

Azure DevOps Services

管道缓存可以通过重用以前运行中下载的依赖项来帮助缩短构建时间,而无需重新创建或重新下载相同的文件。 在每次运行开始时重复下载相同依赖项的情况下,这特别有用。 这通常是一个耗时的过程,涉及数百或数千个网络调用。

当还原和保存缓存所需的时间少于重新生成文件所需的时间时,缓存最有效。 但是,在某些情况下,缓存可能无法提供性能优势,甚至可能对构建时间产生负面影响。 请务必评估您的特定方案以确定缓存是否是正确的方法。

注意

经典发布管道不支持管道缓存。

何时使用管道项目与管道缓存

管道缓存和 管道项目 执行类似的功能,但适用于不同的场景,不应互换使用。

  • 使用管道构件:当您需要获取一个作业生成的特定文件并与其他作业共享它们时(如果没有它们,其他作业可能会失败)。

  • 使用管道缓存:当您想通过重用以前运行中的文件来缩短构建时间时(并且没有这些文件不会影响作业的运行能力)。

注意

管道缓存和管道构件适用于所有层级(免费和付费),均免费提供。 有关更多详细信息,请参阅 Artifacts 存储消耗。

自承载代理要求

以下可执行文件必须位于环境变量中列出的 PATH 文件夹中。 请注意,这些要求仅适用于自承载代理,因为托管代理预装了必要的软件。

存档软件/平台 Windows操作系统 Linux的 Mac
GNU Tar 必需 必需
BSD 焦油 必需
7 拉链 建议

缓存任务:工作原理

通过将 Cache 任务steps添加到作业的部分,将缓存添加到管道中。

在管道执行期间,当遇到缓存步骤时,任务会尝试根据提供的输入恢复缓存。 如果未找到缓存,则步骤完成,并执行作业中的下一步。

作业中的所有步骤都成功运行后,将自动添加一个特殊的 “作业后:缓存” 步骤,并为每个未跳过的 “还原缓存” 步骤触发。 此步骤负责保存缓存。

注意

缓存是不可变的。 创建缓存后,无法修改其内容。

配置缓存任务

缓存任务有两个必需的参数:pathkey

  1. path:要缓存的文件夹的路径。 这可以是绝对路径或相对路径。 针对 $(System.DefaultWorkingDirectory) 解析相对路径。

    提示

    您可以使用 预定义变量 来存储要缓存的文件夹的路径。 但是,不支持通配符。

  2. key:定义要还原或保存的缓存的标识符。 该键由字符串值、文件路径或文件模式的组合组成,每个段由一个 | 字符分隔。

    • 字符串
      固定值(如缓存名称或工具名称),或取自环境变量(如当前作系统或作业名称)。

    • 文件路径
      将对其内容进行哈希处理的特定文件的路径。 该文件在任务运行时必须存在。 任何类似于文件路径的段都被视为此类,因此请谨慎,尤其是在使用包含 .的段时,因为这可能会导致“文件不存在”失败。

      提示

      要避免将类似路径的字符串段视为文件路径,请用双引号将其括起来,例如:"my.key" | $(Agent.OS) | key.file

    • 文件模式
      必须与至少一个文件匹配的 glob 样式通配符模式的逗号分隔列表。 例子:

      • **/yarn.lock:sources 目录下的所有 yarn.lock 文件。
      • */asset.json, !bin/**:所有 asset.json 文件都位于 sources 目录下的目录中, 但 bin 目录中的文件除外。

由文件路径或文件模式标识的任何文件的内容都经过哈希处理,以生成动态缓存键。 当您的项目具有唯一标识要缓存的内容的文件时,这非常有用。 例如,像 、 yarn.lockGemfile.lockPipfile.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   

已知问题和反馈

如果您在管道中设置缓存时遇到问题,请查看存储库中microsoft/azure-pipelines-tasks未解决的问题列表。 如果未看到问题列出,请创建一个新问题,并提供有关你的场景的必要信息。

问答

问:是否可以清除缓存?

答:不支持清除缓存。 但是,您可以通过向缓存键添加字符串文字(例如 version2)来避免对现有缓存的命中。 例如,从中更改以下缓存密钥:

key: 'yarn | "$(Agent.OS)" | yarn.lock'

更改为:

key: 'version2 | yarn | "$(Agent.OS)" | yarn.lock'

问:缓存何时过期?

答:如果缓存七天无活动,则会过期。

问:何时会上传缓存?

答:缓存是根据您指定的 path ,并在作业的最后一步后上传。 有关更多详细信息,请参阅示例

问:缓存的大小是否有限制?

答:对于组织内的单个缓存大小或总缓存大小,没有强制限制。