Поделиться через


Кэширование конвейера

Azure DevOps Services

Кэширование конвейера может помочь сократить время сборки, позволяя выходным данным или скачанным зависимостям из одного запуска повторно использоваться в последующих запусках, тем самым уменьшая или избегая затрат повторного создания или повторной загрузки одних и таких же файлов. Кэширование особенно полезно в сценариях, когда одни и те же зависимости скачиваются снова и снова в начале каждого запуска. Это часто занимает много времени, включая сотни или тысячи сетевых вызовов.

Кэширование может быть эффективным при улучшении времени сборки, если время восстановления и сохранения кэша меньше времени для повторного создания выходных данных с нуля. Из-за этого кэширование может оказаться не эффективным во всех сценариях и на самом деле может оказать негативное влияние на время сборки.

Заметка

Кэширование конвейеров не поддерживается в классических конвейерах выпуска.

Когда использовать артефакты или кэширование

Кэширование конвейера и артефакты конвейера выполнять аналогичные функции, но предназначены для различных сценариев и не должны использоваться взаимозаменяемо.

  • Используйте артефакты конвейера, когда необходимо взять определенные файлы, созданные в одном задании, и предоставить их для других заданий (и эти другие задания, скорее всего, могут завершиться сбоем без них).

  • Используйте кэширование конвейера, если требуется улучшить время сборки, повторно используя файлы из предыдущих запусков (и отсутствие этих файлов не повлияет на возможность выполнения задания).

Заметка

Кэширование конвейера и артефакты конвейера бесплатны для всех уровней (бесплатные и платные). Дополнительные сведения см. в разделе потребления хранилища артефактов.

Задача кэша: как она работает

Кэширование добавляется в конвейер с помощью задачи кэша . Эта задача работает как любая другая задача и добавляется в раздел steps задания.

При обнаружении шага кэша во время выполнения задача восстанавливает кэш на основе предоставленных входных данных. Если кэш не найден, шаг завершается и выполняется следующий шаг задания.

После выполнения всех шагов задания и при условии успешного состояния задания специальный "Post-job: Cache" шаг автоматически добавляется и активируется для каждого "кэш восстановления" шаг, который не был пропущен. Этот шаг отвечает за сохранение кэша.

Заметка

Кэши неизменяемы, то есть после создания кэша его содержимое неизменяемо.

Настройка задачи кэша

Задача кэша имеет два обязательных аргумента: ключ и путь :

  • путь: путь папки для кэширования. Может быть абсолютным или относительным путем. Относительные пути разрешаются относительно $(System.DefaultWorkingDirectory).

Заметка

Можно использовать предопределенные переменные для хранения пути к папке, которую вы хотите кэшировать, однако подстановочные знаки не поддерживаются.

  • ключ: необходимо задать идентификатор кэша, который требуется восстановить или сохранить. Ключи состоят из сочетания строковых значений, путей к файлам или шаблонов файлов, где каждый сегмент разделен символом |.
  • строки:
    Фиксированное значение (например, имя кэша или имени средства) или взятое из переменной среды (например, текущее имя ОС или текущего задания).

  • пути к файлам:
    Путь к определенному файлу, содержимое которого будет хэшировано. Этот файл должен существовать во время выполнения задачи. Помните, что любой ключевой сегмент, который будет рассматриваться как путь к файлу. В частности, это включает сегменты, содержащие .. Это может привести к сбою задачи, если этот файл не существует.

    Совет

    Чтобы избежать обработки сегмента строки типа пути, как путь к файлу, обтекайте его двойными кавычками, например: "my.key" | $(Agent.OS) | key.file

  • Шаблоны файлов:
    Список шаблонов подстановочных знаков в стиле glob, разделенный запятыми, который должен соответствовать по крайней мере одному файлу. Например:

    • **/yarn.lock: все файлы yarn.lock в каталоге источников
    • */asset.json, !bin/**: все файлы asset.json, расположенные в каталоге источников, за исключением каталога bin

Содержимое любого файла, определяемого путем к файлу или шаблоном файла, хэшируется для создания ключа динамического кэша. Это полезно, если в проекте есть файлы, которые однозначно определяют, что кэшируется. Например, файлы, такие как package-lock.json, yarn.lock, Gemfile.lockили Pipfile.lock, обычно ссылаются на ключ кэша, так как все они представляют уникальный набор зависимостей.

Относительные пути к файлам или шаблоны файлов определяются относительно $(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.

Убедитесь, что обновите переменную YARN_CACHE_FOLDER, если используется что-либо, отличное от checkout: self, так как она должна указывать на репозиторий, где расположен .yarn.

Восстановление ключей

restoreKeys можно использовать, если требуется запрашивать несколько точных ключей или префиксов ключей. Это используется для переключения на другой ключ в случае, если key не дает результата. Ключ восстановления выполняет поиск ключа по префиксу и дает последнюю созданную запись кэша в результате. Это полезно, если конвейер не может найти точное совпадение, но предпочитает использовать частичное совпадение кэша. Чтобы вставить несколько ключей восстановления, разделите их с помощью новой строки, чтобы указать ключ восстановления (см. пример для получения дополнительных сведений). Порядок, в котором будут пытаться восстановить ключи, будет сверху вниз.

Обязательное программное обеспечение для локального агента

Архивное программное обеспечение / платформа Виндоус Линукс Mac
GNU Tar Обязательно Обязательно Нет
BSD Tar Нет Нет Обязательно
7-Zip Рекомендуется Нет Нет

Приведенные выше исполняемые файлы должны находиться в папке, указанной в переменной среды PATH. Имейте в виду, что облачные агенты поставляются с предустановленным программным обеспечением; это применимо только для самостоятельно размещенных агентов.

пример:

Ниже приведен пример использования ключей восстановления 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. При совпадении префикса результатом является последний созданный ключ кэша.

Заметка

Конвейер может иметь одну или несколько задач кэширования. Нет ограничений на емкость хранилища кэширования, а задания и задачи из одного конвейера могут получить доступ к одному и тому же кэшу.

Изоляция кэша и безопасность

Чтобы обеспечить изоляцию между кэшами из разных конвейеров и разных ветвей, каждый кэш принадлежит логическому контейнеру, называемому областью. Области создают границу безопасности, которая обеспечивает:

  1. Задание из одного потока не может обратиться к кэшам из другого потока.
  2. Задание на создание ПР имеет право на чтение кешей целевой ветви ПР (для того же конвейера), но не может записывать кэши в её области.

При обнаружении шага кэша во время выполнения, кэш, определенный ключом, запрашивается с сервера. Затем сервер ищет кэш с этим ключом из областей, видимых для задания, и возвращает кэш (если он доступен). При сохранении кэша (в конце работы) кэш записывается в область, обозначающую конвейер и ветку. Дополнительные сведения см. ниже.

Непрерывная интеграция (CI), вручные и запланированные запуски

Размах Читать Писать
Исходная ветвь Да Да
ветвь main Да Нет
ветвь master Да Нет

Запуски пул-реквестов

Размах Читать Писать
Исходная ветвь Да Нет
Целевая ветвь Да Нет
Промежуточная ветвь (например, refs/pull/1/merge) Да Да
ветвь main Да Нет
ветвь master Да Нет

Запуски запросов на вытягивание

Ветка Читать Писать
Целевая ветвь Да Нет
Промежуточная ветвь (например, refs/pull/1/merge) Да Да
ветвь main Да Нет
master ветвь Да Нет

Совет

Так как кэши уже ограничены проектом, конвейером и ветвью, в ключ кэша не требуется включать какие-либо идентификаторы проекта, конвейера или ветви.

Кондиционирование при восстановлении кэша

В некоторых сценариях успешное восстановление кэша должно привести к выполнению другого набора шагов. Например, шаг установки зависимостей можно пропустить, если кэш был восстановлен. Это возможно с помощью входных данных для задачи 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

Бандлер

Для проектов Ruby, использующих Bundler, переопределите переменную среды BUNDLE_PATH, которую Bundler использует для задания пути , по которому Bundler ищет Gems.

пример:

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 — это кэш компилятора для 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

Кэширование образов 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'
      path: $(Pipeline.Workspace)/docker
      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'))
  • ключ: (обязательно) — уникальный идентификатор для кэша.
  • путь: (обязательно) — путь к папке или файлу, который нужно кэшировать.

Голанг

Для проектов 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 может оказать значительное влияние на время сборки. Чтобы включить кэш сборки, задайте для переменной среды 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: |
      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: резервные ключи, если основной ключ отказал (необязательно)

Заметка

Кэши неизменяемы, когда кэш с определенным ключом создается для определенной области (ветви), кэш нельзя обновить. Это означает, что если ключ является фиксированным значением, все последующие сборки для одной ветви не смогут обновить кэш, даже если содержимое кэша изменилось. Если вы хотите использовать фиксированное значение ключа, необходимо использовать аргумент restoreKeys в качестве резервного варианта.

Maven

Maven имеет локальный репозиторий, в котором хранятся загружаемые и собранные артефакты. Чтобы включить, задайте для параметра maven.repo.local путь в разделе $(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)'

.NET/NuGet

Если вы используете 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

Этот подход также действителен для проектов .NET Core, если проект использует packages.lock.json для блокировки версий пакетов. Это можно включить, задав RestorePackagesWithLockFileTrue в файле csproj или выполнив следующую команду: dotnet restore --use-lock-file.

Node.js/npm

Существуют различные способы включить кэширование в проекте 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

- script: npm ci

Если в проекте нет файла package-lock.json, используйте файл package.json в качестве входных данных ключа кэша.

Совет

Так как npm ci удаляет папку node_modules, чтобы обеспечить использование согласованного повторяемого набора модулей, следует избегать кэширования node_modules при вызове npm ci.

Node.js/Пряжа

Как и в 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

Python/Anaconda

Настройте кэширование конвейера с помощью сред 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')
    

PHP/Composer

Для PHP-проектов, использующих Composer, переопределите переменную среды COMPOSER_CACHE_DIR, используемую Composer.

пример:

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. Если вашу проблему не удалось найти в списке, создайте новую и предоставьте необходимую информацию о сценарии.

Q&A

Вопрос. Можно ли очистить кэш?

Ответ. Очистка кэша в настоящее время не поддерживается. Однако можно добавить строковый литерал (например, version2) в существующий ключ кэша, чтобы изменить ключ таким образом, чтобы избежать попаданий в существующие кэши. Например, измените следующий ключ кэша из этого:

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

Для этого выполните указанные ниже действия.

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

Вопрос. Когда истекает срок действия кэша?

Ответ. Срок действия кэша истекает через семь дней без действия.

Вопрос: Когда кэш загружается?

После последнего шага вашего конвейера будет создан кэш из кэша path и отправлен. Дополнительные сведения см. в примере .

Вопрос. Существует ли ограничение на размер кэша?

Ответ. Нет принудительного ограничения на размер отдельных кэшей или общий размер всех кэшей в организации.