Cache de pipeline
Serviços de DevOps do Azure
O cache de pipeline pode ajudar a reduzir o tempo de compilação, permitindo que as saídas ou dependências baixadas de uma execução sejam reutilizadas em execuções posteriores, reduzindo ou evitando assim o custo de recriar ou baixar novamente os mesmos arquivos novamente. O cache é especialmente útil em cenários em que as mesmas dependências são baixadas repetidamente no início de cada execução. Este é muitas vezes um processo demorado que envolve centenas ou milhares de chamadas de rede.
O cache pode ser eficaz para melhorar o tempo de compilação, desde que o tempo para restaurar e salvar o cache seja menor do que o tempo para produzir a saída novamente do zero. Por isso, o cache pode não ser eficaz em todos os cenários e pode realmente ter um impacto negativo no tempo de compilação.
Nota
O cache de pipeline é suportado em trabalhos de pool de agentes para pipelines YAML e Classic. No entanto, ele não é suportado em pipelines de versão clássica.
Quando usar artefatos versus cache
O cache de pipeline e os artefatos de pipeline executam funções semelhantes, mas são projetados para cenários diferentes e não devem ser usados de forma intercambiável.
Use artefatos de pipeline quando precisar pegar arquivos específicos produzidos em um trabalho e compartilhá-los com outros trabalhos (e esses outros trabalhos provavelmente falharão sem eles).
Use o cache de pipeline quando quiser melhorar o tempo de compilação reutilizando arquivos de execuções anteriores (e não ter esses arquivos não afetará a capacidade de execução do trabalho).
Nota
O cache de pipeline e os artefatos de pipeline são gratuitos para todos os níveis (gratuitos e pagos). consulte Consumo de armazenamento de artefatos para obter mais detalhes.
Tarefa de cache: como funciona
O cache é adicionado a um pipeline usando a tarefa Cache. Esta tarefa funciona como qualquer outra tarefa e é adicionada steps
à seção de um trabalho.
Quando uma etapa de cache é encontrada durante uma execução, a tarefa restaura o cache com base nas entradas fornecidas. Se nenhum cache for encontrado, a etapa será concluída e a próxima etapa do trabalho será executada.
Depois que todas as etapas do trabalho tiverem sido executadas e assumindo um status de trabalho bem-sucedido , uma etapa especial "Pós-trabalho: Cache" é automaticamente adicionada e acionada para cada etapa de "restaurar cache" que não foi ignorada. Esta etapa é responsável por salvar o cache.
Nota
Os caches são imutáveis, o que significa que, uma vez que um cache é criado, seu conteúdo não pode ser alterado.
Configurar a tarefa Cache
A tarefa Cache tem dois argumentos necessários: chave e caminho:
- caminho: o caminho da pasta para armazenar em cache. Pode ser um caminho absoluto ou relativo. Os caminhos relativos são resolvidos em relação a
$(System.DefaultWorkingDirectory)
.
Nota
Você pode usar variáveis predefinidas para armazenar o caminho para a pasta que deseja armazenar em cache, no entanto, curingas não são suportados.
- chave: deve ser definido como o identificador do cache que você deseja restaurar ou salvar. As chaves são compostas por uma combinação de valores de cadeia de caracteres, caminhos de arquivo ou padrões de arquivo, onde cada segmento é separado por um
|
caractere.
Cordas:
Valor fixo (como o nome do cache ou o nome de uma ferramenta) ou retirado de uma variável de ambiente (como o SO atual ou o nome do trabalho atual)Caminhos de arquivo:
Caminho para um arquivo específico cujo conteúdo será colocado em hash. Esse arquivo deve existir no momento em que a tarefa é executada. Lembre-se de que qualquer segmento de chave que "se pareça com um caminho de arquivo" será tratado como um caminho de arquivo. Em particular, isto inclui segmentos que contêm um.
. Isso pode resultar na falha da tarefa quando esse "arquivo" não existe.Gorjeta
Para evitar que um segmento de cadeia de caracteres semelhante a um caminho seja tratado como um caminho de arquivo, envolva-o com aspas duplas, por exemplo:
"my.key" | $(Agent.OS) | key.file
Padrões de arquivo:
Lista separada por vírgulas do padrão curinga no estilo glob que deve corresponder a pelo menos um arquivo. Por exemplo:**/yarn.lock
: todos os arquivos yarn.lock sob o diretório de códigos-fonte*/asset.json, !bin/**
: todos os arquivos asset.json localizados em um diretório sob o diretório sources, exceto sob o diretório bin
O conteúdo de qualquer arquivo identificado por um caminho de arquivo ou padrão de arquivo é colocado em hash para produzir uma chave de cache dinâmica. Isso é útil quando seu projeto tem arquivos que identificam exclusivamente o que está sendo armazenado em cache. Por exemplo, arquivos como package-lock.json
, yarn.lock
, Gemfile.lock
, ou Pipfile.lock
são comumente referenciados em uma chave de cache, uma vez que todos eles representam um conjunto exclusivo de dependências.
Os caminhos de arquivo relativos ou padrões de arquivo são resolvidos em relação ao $(System.DefaultWorkingDirectory)
.
Exemplo:
Aqui está um exemplo mostrando como armazenar em cache dependências instaladas pelo 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
Neste exemplo, a chave de cache contém três partes: uma cadeia de caracteres estática ("yarn"), o sistema operacional em que o trabalho está sendo executado, uma vez que esse cache é exclusivo por sistema operacional, e o hash do arquivo que identifica exclusivamente o yarn.lock
conjunto de dependências no cache.
Na primeira execução após a tarefa ser adicionada, a etapa de cache relatará uma "falha de cache", uma vez que o cache identificado por essa chave não existe. Após a última etapa, um cache será criado a partir dos arquivos e $(Pipeline.Workspace)/s/.yarn
carregado. Na próxima execução, a etapa de cache relatará um "acerto de cache" e o conteúdo do cache será baixado e restaurado.
Ao usar checkout: self
o , é feito check-out do repositório no $(Pipeline.Workspace)/s
, e sua .yarn
pasta geralmente reside no próprio repositório.
Nota
Pipeline.Workspace
é o caminho local no agente que executa o pipeline onde todos os diretórios são criados. Esta variável tem o mesmo valor que Agent.BuildDirectory
.
Certifique-se de atualizar a variável YARN_CACHE_FOLDER
se estiver usando algo diferente de checkout: self
como isso deve apontar para o repositório onde .yarn
reside.
Restaurar chaves
restoreKeys
pode ser usado se alguém quiser consultar várias chaves exatas ou prefixos de chave. Isso é usado para cair de volta para outra chave no caso de um key
não produzir um acerto. Uma chave de restauração procura uma chave por prefixo e produz a entrada de cache mais recente criada como resultado. Isso é útil se o pipeline não conseguir encontrar uma correspondência exata, mas quiser usar um acerto de cache parcial. Para inserir várias chaves de restauração, delimite-as usando uma nova linha para indicar a chave de restauração (consulte o exemplo para obter mais detalhes). A ordem da qual as chaves de restauração serão julgadas será de cima para baixo.
Software necessário no agente auto-hospedado
Software de arquivo / Plataforma | Windows | Linux | Mac |
---|---|---|---|
GNU Tar | Obrigatório | Obrigatório | Não |
Alcatrão BSD | No | Não | Obrigatório |
7-Zip | Recomendado | No | Não |
Os executáveis acima precisam estar em uma pasta listada na variável de ambiente PATH. Tenha em mente que os agentes hospedados vêm com o software incluído, isso só é aplicável para agentes auto-hospedados.
Exemplo:
Aqui está um exemplo de como usar chaves de restauração pelo 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
Neste exemplo, a tarefa de cache tenta localizar se a chave existe no cache. Se a chave não existir no cache, ele tentará usar a primeira chave yarn | $(Agent.OS)
de restauração.
Isso tenta procurar todas as chaves que correspondem exatamente a essa chave ou que tem essa chave como um prefixo. Um acerto de prefixo pode acontecer se houver um segmento de hash diferente yarn.lock
.
Por exemplo, se a seguinte chave yarn | $(Agent.OS) | old-yarn.lock
estava no cache onde o old-yarn.lock
produziu um hash diferente do yarn.lock
, a chave de restauração produzirá um acerto parcial.
Se houver uma falha na primeira chave de restauração, ele usará a próxima chave yarn
de restauração que tentará encontrar qualquer chave que comece com yarn
. Para acertos de prefixo, o resultado produz a chave de cache criada mais recentemente como resultado.
Nota
Um pipeline pode ter uma ou mais tarefas de cache. Não há limite para a capacidade de armazenamento de cache, e trabalhos e tarefas do mesmo pipeline podem acessar e compartilhar o mesmo cache.
Isolamento e segurança do cache
Para garantir o isolamento entre caches de pipelines diferentes e ramificações diferentes, cada cache pertence a um contêiner lógico chamado escopo. Os escopos fornecem um limite de segurança que garante que um trabalho de um pipeline não possa acessar os caches de um pipeline diferente, e um trabalho que cria um PR tem acesso de leitura aos caches para a ramificação de destino do PR (para o mesmo pipeline), mas não pode gravar (criar) caches no escopo da ramificação de destino.
Quando uma etapa de cache é encontrada durante uma execução, o cache identificado pela chave é solicitado ao servidor. Em seguida, o servidor procura um cache com essa chave dos escopos visíveis para o trabalho e retorna o cache (se disponível). Ao salvar o cache (no final do trabalho), um cache é gravado no escopo que representa o pipeline e a ramificação. Consulte abaixo para mais detalhes
CI, execuções manuais e programadas
Âmbito | Lida | Escrita |
---|---|---|
Ramificação de origem | Sim | Sim |
main sucursal |
Sim | No |
master sucursal |
Sim | No |
A solicitação pull é executada
Âmbito | Lida | Escrita |
---|---|---|
Ramificação de origem | Sim | No |
Sucursal de destino | Sim | No |
Sucursal intermédia (como refs/pull/1/merge ) |
Sim | Sim |
main sucursal |
Sim | No |
master sucursal |
Sim | No |
A bifurcação de solicitação pull é executada
Filial | Lida | Escrita |
---|---|---|
Sucursal de destino | Sim | No |
Sucursal intermédia (como refs/pull/1/merge ) |
Sim | Sim |
main sucursal |
Sim | No |
master sucursal |
Sim | No |
Gorjeta
Como os caches já têm escopo para um projeto, pipeline e ramificação, não há necessidade de incluir nenhum projeto, pipeline ou identificadores de ramificação na chave de cache.
Condicionamento na restauração de cache
Em alguns cenários, a restauração bem-sucedida do cache deve fazer com que um conjunto diferente de etapas seja executado. Por exemplo, uma etapa que instala dependências pode ser ignorada se o cache foi restaurado. Isso é possível usando a entrada da cacheHitVar
tarefa. Definir essa entrada como o nome de uma variável de ambiente faz com que a variável seja definida como true
quando há um acerto de cache, inexact
em um acerto de cache de chave de restauração, caso contrário, ela é definida como false
. Essa variável pode então ser referenciada em uma condição de etapa ou de dentro de um script.
No exemplo a seguir, a install-deps.sh
etapa é ignorada quando o cache é restaurado:
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
Empacotador
Para projetos Ruby usando Bundler, substitua a BUNDLE_PATH
variável de ambiente usada por Bundler para definir o caminho em que o Bundler procura Gems.
Exemplo:
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 é um cache de compilador para C/C++. Para usar o Ccache em seu pipeline, verifique se Ccache
está instalado e, opcionalmente, adicionado ao seu PATH
(consulte Modos de execução do Ccache). Defina a CCACHE_DIR
variável de ambiente como um caminho sob $(Pipeline.Workspace)
e armazene em cache este diretório.
Exemplo:
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)"
Consulte Definições de configuração do Ccache para obter mais detalhes.
Imagens do Docker
O armazenamento em cache de imagens do Docker reduz drasticamente o tempo necessário para executar seu pipeline.
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'))
- key: (required) - um identificador exclusivo para o cache.
- path: (required) - caminho da pasta ou arquivo que você deseja armazenar em cache.
Golang
Para projetos Golang, você pode especificar os pacotes a serem baixados no arquivo go.mod . Se a GOCACHE
variável ainda não estiver definida, defina-a para onde você deseja que o cache seja baixado.
Exemplo:
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
Usar o suporte de cache integrado do Gradle pode ter um impacto significativo no tempo de compilação. Para habilitar o cache de compilação, defina a variável de GRADLE_USER_HOME
ambiente como um caminho abaixo $(Pipeline.Workspace)
e execute sua compilação com --build-cache
ou adicione org.gradle.caching=true
ao seu gradle.properties
arquivo.
Exemplo:
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: As chaves de fallback se a chave primária falhar (Opcional)
Nota
Os caches são imutáveis, uma vez que um cache com uma chave específica é criado para um escopo específico (ramificação), o cache não pode ser atualizado. Isso significa que, se a chave for um valor fixo, todas as compilações subsequentes para a mesma ramificação não poderão atualizar o cache, mesmo que o conteúdo do cache tenha sido alterado. Se quiser usar um valor de chave fixa, use o restoreKeys
argumento como uma opção de fallback.
Maven
O Maven tem um repositório local onde armazena downloads e artefatos construídos. Para habilitar, defina a maven.repo.local
opção como um caminho em $(Pipeline.Workspace)
e armazene em cache esta pasta.
Exemplo:
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
Se você estiver usando uma tarefa Maven, certifique-se de passar também a MAVEN_OPTS
variável porque ela será substituída caso contrário:
- task: Maven@4
inputs:
mavenPomFile: 'pom.xml'
mavenOptions: '-Xmx3072m $(MAVEN_OPTS)'
.NET/NuGet
Se você usar PackageReferences
para gerenciar dependências do NuGet diretamente em seu arquivo de projeto e tiver um packages.lock.json
arquivo, poderá habilitar o cache definindo a NUGET_PACKAGES
variável de ambiente como um caminho sob $(UserProfile)
e armazenando em cache esse diretório. Consulte Referência de pacote em arquivos de projeto para obter mais detalhes sobre como bloquear dependências.
Se você quiser usar vários packages.lock.json, ainda poderá usar o exemplo a seguir sem fazer alterações. O conteúdo de todos os arquivos packages.lock.json será colocado em hash e, se um dos arquivos for alterado, uma nova chave de cache será gerada.
Exemplo:
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
Node.js/npm
Há diferentes maneiras de habilitar o cache em um projeto Node.js, mas a maneira recomendada é armazenar em cache o diretório de cache compartilhado do npm. Este diretório é gerenciado pelo npm e contém uma versão em cache de todos os módulos baixados. Durante a instalação, o npm verifica esse diretório primeiro (por padrão) em busca de módulos que possam reduzir ou eliminar chamadas de rede para o registro npm público ou para um registro privado.
Como o caminho padrão para o diretório de cache compartilhado do npm não é o mesmo em todas as plataformas, é recomendável substituir a npm_config_cache
variável de ambiente por um caminho em $(Pipeline.Workspace)
. Isso também garante que o cache seja acessível a partir de trabalhos de contêiner e não contêiner.
Exemplo:
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
Se o seu projeto não tiver um package-lock.json
arquivo, faça referência ao package.json
arquivo na entrada da chave de cache.
Gorjeta
Como npm ci
exclui a node_modules
pasta para garantir que um conjunto consistente e repetível de módulos seja usado, você deve evitar o armazenamento em cache node_modules
ao chamar npm ci
.
Node.js/Fios
Como no npm, há diferentes maneiras de armazenar em cache pacotes instalados com o Yarn. A maneira recomendada é armazenar em cache a pasta de cache compartilhada do Yarn. Este diretório é gerenciado pelo Yarn e contém uma versão em cache de todos os pacotes baixados. Durante a instalação, o Yarn verifica esse diretório primeiro (por padrão) em busca de módulos, o que pode reduzir ou eliminar chamadas de rede para registros públicos ou privados.
Exemplo:
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
Configure seu cache de pipeline com ambientes Anaconda:
Exemplo
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/Compositor
Para projetos PHP usando o Composer, substitua a COMPOSER_CACHE_DIR
variável de ambiente usada pelo Composer.
Exemplo:
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
Problemas conhecidos e comentários
Se você estiver enfrentando problemas para configurar o cache para seu pipeline, verifique a lista de problemas abertos no microsoft/azure-pipelines-tasks repositório. Se não vir o problema listado, crie um novo problema e forneça as informações necessárias sobre o seu cenário.
Q&A
P: Posso limpar um cache?
R: No momento, não há suporte para limpar um cache. No entanto, você pode adicionar um literal de cadeia de caracteres (como version2
) à sua chave de cache existente para alterar a chave de uma forma que evite quaisquer acertos em caches existentes. Por exemplo, altere a seguinte chave de cache a partir disso:
key: 'yarn | "$(Agent.OS)" | yarn.lock'
Para isso:
key: 'version2 | yarn | "$(Agent.OS)" | yarn.lock'
P: Quando um cache expira?
R: Os caches expiram após sete dias sem atividade.
P: Quando é que a cache é carregada?
R: Após a última etapa do seu pipeline, um cache será criado a partir do seu cache path
e carregado. Veja o exemplo para obter mais detalhes.
P: Existe um limite para o tamanho de um cache?
R: Não há limite imposto para o tamanho de caches individuais ou o tamanho total de todos os caches em uma organização.