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.
Observação
A cache de pipeline não é suportada em pipelines de lançamento clássicos.
Quando usar artefatos versus armazenamento em cache
Cache de pipeline e artefatos de pipeline executam funções semelhantes, mas são projetados para cenários distintos 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).
Observação
O cache de pipeline e os artefatos de pipeline são gratuitos para todos os níveis (gratuitos e pagos). Consulte o 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 à secção steps
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
Observação
Os caches são imutáveis, o que significa que, uma vez que um cache é criado, seu conteúdo é imutável.
Configurar a tarefa Cache
A tarefa Cache tem dois argumentos necessários: chave e o caminho :
-
caminho: o caminho da pasta para armazenamento em cache. Pode ser um caminho absoluto ou relativo. Os caminhos relativos são resolvidos em relação a
$(System.DefaultWorkingDirectory)
.
Observação
Você pode usar variáveis predefinidas para armazenar o caminho para a pasta que deseja guardar em cache; no entanto, curingas não são suportados.
-
chave: deve ser definida 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 ficheiro específico cujo conteúdo será submetido a 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.Dica
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 sources -
*/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, pois todos representam um conjunto exclusivo de dependências.
Caminhos de arquivo relativos ou padrões de arquivo são resolvidos em relação a $(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 yarn.lock
que identifica exclusivamente o 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 em $(Pipeline.Workspace)/s/.yarn
e 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 repositório é colocado em $(Pipeline.Workspace)/s
, e a sua pasta .yarn
geralmente reside no próprio repositório.
Observação
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
, pois 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 recorrer a outra chave caso um key
não resulte num sucesso. Uma chave de restauração procura uma chave por prefixo e produz a entrada de cache mais recente criada como resultado. Isto é útil se o pipeline não conseguir encontrar uma correspondência exata, mas optar por utilizar um resultado parcial de cache. 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 em que as chaves de restauração serão tentadas será de cima para baixo.
Software necessário no agente auto-hospedado
Software de arquivo / Plataforma | Windows | Linux | Mac |
---|---|---|---|
GNU Tar | Necessário | Necessário | Não |
Alcatrão BSD | Não | Não | Necessário |
7-Zip | Recomendado | Não | 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:
Eis um exemplo de como usar chaves de restauração com o 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 de restauração yarn | $(Agent.OS)
.
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 de yarn.lock
diferente.
Por exemplo, se a seguinte chave yarn | $(Agent.OS) | old-yarn.lock
estivesse no cache onde o old-yarn.lock
produziu um hash diferente do yarn.lock
, a chave de restauração produziria um acerto parcial.
Se houver uma falha na primeira chave de restauração, ele usará a próxima chave de restauração yarn
que tentará encontrar qualquer chave que comece com yarn
. Nos acertos de prefixo, o resultado é a chave de cache criada mais recentemente.
Observação
Um pipeline pode ter uma ou mais tarefas de cache. Não há limite para a capacidade de armazenamento em 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 diferentes pipelines e diferentes ramificações, cada cache pertence a um contentor lógico chamado escopo. Os escopos fornecem um limite de segurança que garante:
- Um trabalho de um pipeline não pode acessar os caches de um pipeline diferente, e
- Um trabalho de criação de uma RP tem acesso de leitura aos caches da ramificação de destino da RP (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 âmbitos visíveis para a tarefa 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. Veja abaixo mais detalhes.
CI, execuções manuais e programadas
Âmbito de aplicação | Ler | Escrever |
---|---|---|
Ramificação de origem | Sim | Sim |
main filial |
Sim | Não |
master sucursal |
Sim | Não |
A solicitação pull é executada
Âmbito de aplicação | Ler | Escrever |
---|---|---|
Ramificação de origem | Sim | Não |
Sucursal de destino | Sim | Não |
Ramo intermediário (como refs/pull/1/merge ) |
Sim | Sim |
main sucursal |
Sim | Não |
master filial |
Sim | Não |
A bifurcação de solicitação pull é executada
Sucursal | Ler | Escrever |
---|---|---|
Sucursal de destino | Sim | Não |
Ramo intermediário (como refs/pull/1/merge ) |
Sim | Sim |
main sucursal |
Sim | Não |
master filial |
Sim | Não |
Dica
Como os caches já estão associados a um projeto, pipeline e branch, não é necessário incluir nenhum identificador de projeto, pipeline ou branch na chave de cache.
Condicionamento na recuperaçã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 de tarefa cacheHitVar
. 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
. Esta variável pode então ser referenciada numa condição de etapa ou a partir de um script.
No exemplo a seguir, a etapa install-deps.sh
é 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 variável de ambiente BUNDLE_PATH
usada pelo Bundler para definir o caminho de 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 variável de ambiente CCACHE_DIR
como um caminho em $(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 as 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'))
- chave: (obrigatório) - um identificador exclusivo para o cache.
- caminho: (obrigatório) - 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 GOCACHE
ainda não estiver definida, defina-a para onde 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
O uso do suporte de cache integrado da Gradle pode ter um impacto significativo no tempo de compilação. Para habilitar o cache de compilação, defina a variável de ambiente GRADLE_USER_HOME
como um caminho em $(Pipeline.Workspace)
e execute sua compilação com --build-cache
ou adicione org.gradle.caching=true
ao seu arquivo gradle.properties
.
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 reserva se a chave primária falhar (Opcional)
Observação
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 argumento restoreKeys
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 opção maven.repo.local
como um caminho em $(Pipeline.Workspace)
e faça o cache desta 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 do Maven, certifique-se de passar também a variável MAVEN_OPTS
porque ela será substituída de outra forma:
- task: Maven@4
inputs:
mavenPomFile: 'pom.xml'
mavenOptions: '-Xmx3072m $(MAVEN_OPTS)'
.NET/NuGet
Se você usar o PackageReferences
para gerenciar dependências do NuGet diretamente em seu arquivo de projeto e tiver um arquivo packages.lock.json
, poderá habilitar o cache definindo a variável de ambiente NUGET_PACKAGES
como um caminho em $(UserProfile)
e armazenando em cache esse diretório. Consulte a referência de pacote nos ficheiros 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
Essa abordagem também é válida para projetos .NET Core se seu projeto usa packages.lock.json para bloquear versões de pacote. Você pode habilitar isso definindo
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 variável de ambiente npm_config_cache
por um caminho em $(Pipeline.Workspace)
. Isso também garante que o cache seja acessível a partir de tarefas em contêineres e fora deles.
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 arquivo package-lock.json
, faça referência ao arquivo package.json
na entrada da chave de cache.
Dica
Como npm ci
exclui a pasta node_modules
para garantir que um conjunto consistente e repetível de módulos seja usado, você deve evitar armazenar node_modules
em cache ao chamar npm ci
.
Node.js/Fio
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 variável de ambiente COMPOSER_CACHE_DIR
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 estiver a enfrentar problemas ao configurar o armazenamento em cache para o seu pipeline, verifique a lista de problemas abertos no repositório microsoft/azure-pipelines-tasks. Caso não veja o problema listado, crie um novo 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 forma a evitar 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 o último passo do seu pipeline, um cache será criado a partir do cache path
e enviado. Consulte 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.