Partilhar via


Cache de pipeline

Serviços de DevOps do Azure

O cache de pipeline pode ajudar a reduzir o tempo de compilação reutilizando dependências baixadas de execuções anteriores, evitando a necessidade de recriar ou baixar novamente os mesmos arquivos. Isso é particularmente ú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 é mais eficaz quando o tempo necessário para restaurar e salvar o cache é menor do que o tempo necessário para regenerar os arquivos. No entanto, em alguns casos, o cache pode não fornecer benefícios de desempenho e pode até mesmo afetar negativamente o tempo de compilação. É importante avaliar seu cenário específico para determinar se o cache é a abordagem correta.

Observação

O cache de pipeline não é suportado para pipelines de versão clássica.

Quando usar artefatos de pipeline versus cache do pipeline

O cache de pipeline e os artefatos de pipeline executam funções semelhantes, mas destinam-se a cenários diferentes e não devem ser usados de forma intercambiável.

  • Use artefatos de pipeline: quando você precisa pegar arquivos específicos produzidos por um trabalho e compartilhá-los com outros trabalhos (e esses outros trabalhos provavelmente falhariam sem eles).

  • Use o cache de pipeline: quando você 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 estão disponíveis sem custo para todos os níveis (grátis e pagos). Consulte o consumo de armazenamento de artefatos para obter mais detalhes.

Requisitos do agente auto-hospedado

Os seguintes executáveis devem estar localizados em uma pasta listada na variável de PATH ambiente. Observe que esses requisitos se aplicam apenas a agentes auto-hospedados, pois os agentes hospedados vêm pré-instalados com o software necessário.

Software de arquivo / Plataforma Mac OS Aplicações Linux Mac
GNU Tar (um utilitário de armazenamento e recuperação de arquivos) Necessário Necessário Não
Alcatrão BSD Não Não Necessário
7-Zip Recomendado Não Não

Tarefa de cache: como funciona

A cache é adicionada a um pipeline ao adicionar a tarefa Cache à seção steps de um trabalho.

Durante a execução do pipeline, quando uma etapa de cache é encontrada, a tarefa tenta restaurar 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 forem executadas com êxito, uma etapa especial "Pós-trabalho: Cache" será automaticamente adicionada e acionada para cada etapa de "restaurar cache" que não foi ignorada. Esta etapa é responsável por salvar o cache.

Observação

Os caches são imutáveis. Depois que um cache é criado, seu conteúdo não pode ser modificado.

Configurar a tarefa de cache

A tarefa Cache tem dois argumentos necessários: caminho e chave:

  1. path: O caminho para a pasta que você deseja armazenar em cache. Este pode ser um caminho absoluto ou relativo. Os caminhos relativos são resolvidos em relação a $(System.DefaultWorkingDirectory).

    Dica

    Você pode usar variáveis predefinidas para armazenar o caminho para a pasta que deseja armazenar em cache. No entanto, não há suporte para curingas.

  2. key: define o identificador do cache que você deseja restaurar ou salvar. A chave é composta por uma combinação de valores de cadeia de caracteres, caminhos de arquivo ou padrões de arquivo, com cada segmento separado por um | caractere.

    • Cordas:
      Um 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).

    • Caminhos de arquivo:
      O caminho para um ficheiro específico cujo conteúdo será hashado. O arquivo deve existir no momento em que a tarefa é executada. Qualquer segmento que se assemelhe a um caminho de arquivo é tratado como tal, portanto, seja cauteloso, especialmente ao usar segmentos contendo ., pois isso pode levar a falhas de "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:
      Uma lista separada por vírgulas de padrões de curinga no estilo glob que devem corresponder a pelo menos um ficheiro. Exemplos:

      • **/yarn.lock: todos os arquivos yarn.lock no diretório sources.
      • */asset.json, !bin/**: todos os arquivos asset.json localizados em um diretório sob o diretório sources, exceto aqueles no diretório bin .

O conteúdo de qualquer arquivo identificado por um caminho de arquivo ou padrão de arquivo é colocado em hash para gerar 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 frequentemente referenciados em uma chave de cache, pois representam um conjunto exclusivo de dependências. Os caminhos ou padrões de arquivo relativos são resolvidos relativamente ao $(System.DefaultWorkingDirectory).

  • Exemplo:

O exemplo a seguir mostra como armazenar em cache pacotes 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 consiste em três partes: uma cadeia de caracteres estática ("yarn"), o sistema operacional em que o trabalho está sendo executado (já que o cache é exclusivo por sistema operacional) e o hash do arquivo (que identifica exclusivamente as yarn.lock dependências).

Na primeira execução após a tarefa ser adicionada, a etapa de cache relatará uma "falha de cache" porque 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, é feito check-out do repositório para $(Pipeline.Workspace)/s, e a sua pasta .yarn provavelmente residirá no próprio repositório.

Observação

Pipeline.Workspace é o caminho local no agente que executa o pipeline e onde são criados todos os diretórios. Esta variável tem o mesmo valor que Agent.BuildDirectory. Se tu não estiveres a usar checkout: self, certifica-te de atualizar a variável YARN_CACHE_FOLDER para apontar para o local de .yarn no teu repositório.

Usar chaves de restauração

restoreKeys Permite consultar várias chaves exatas ou prefixos de chave. É usado como um recurso alternativo quando o especificado key não gera um resultado. Uma chave de restauração procura uma chave por prefixo e retorna a entrada de cache criada mais recentemente. Isso é útil quando o pipeline não consegue encontrar uma correspondência exata, mas ainda deseja usar uma colisão de cache parcial.

Para especificar várias chaves de restauração, liste-as em linhas separadas. A ordem em que as chaves de restauração são tentadas é de cima para baixo.

  • Exemplo:

Aqui está um exemplo de como usar chaves de restauração para armazenar em cache pacotes 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 primeiro tenta restaurar a chave especificada. Se a chave não existir no cache, ela tentará a primeira chave de restauração: yarn | $(Agent.OS). Isso procura por quaisquer chaves de cache que correspondam exatamente ou comecem com esse prefixo.

Uma correspondência de prefixo yarn.lock pode ocorrer se o hash do arquivo tiver sido alterado. Por exemplo, se a memória cache contiver a chave yarn | $(Agent.OS) | old-yarn.lock (onde old-yarn.lock tem um hash diferente do atual yarn.lock), essa chave de restauração resultará em um acesso parcial à memória cache.

Se a primeira chave de restauração não produzir uma correspondência, a próxima chave de restauração (yarn) procurará qualquer chave de cache que comece com yarn. Para correspondências de prefixo, o processo de restauração retorna a entrada de cache criada mais recentemente.

Observação

Um pipeline pode incluir várias tarefas de cache e não há limite de armazenamento para cache. Trabalhos e tarefas dentro do mesmo pipeline podem acessar e compartilhar o mesmo cache.

Usar condição de restauração

Em alguns cenários, convém executar condicionalmente as etapas com base no fato de o cache ter sido restaurado com êxito. Por exemplo, você pode ignorar uma etapa que instala dependências se o cache foi restaurado. Isto pode ser conseguido usando o cacheHitVar argumento.

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 se uma chave de restauração produz um acerto de cache parcial e false se nenhum cache é encontrado. Em seguida, você pode fazer referência a essa variável em uma condição de etapa ou dentro de um script.

Veja um exemplo em que a etapa é ignorada install-deps.sh 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

Isolamento e segurança do cache

Para garantir o isolamento entre caches de diferentes pipelines e ramificações, cada cache é armazenado num contentor lógico chamado escopo. Os âmbitos atuam como uma fronteira de segurança que assegura:

  • Os trabalhos de um pipeline não podem acessar caches de um pipeline diferente.

  • Os trabalhos que criam solicitações pull podem ler caches da ramificação de destino (para o mesmo pipeline), mas não podem escrever (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 da tarefa), um cache é gravado no escopo que representa o pipeline e a ramificação.

CI, execuções manuais e programadas

Âmbito de aplicação Ler Escrever
Ramo de origem Sim Sim
main filial Sim Não
master filial Sim Não

A solicitação pull é executada

Âmbito de aplicação Ler Escrever
Ramo de origem Sim Não
Sucursal de destino Sim Não
Ramo intermediário (como refs/pull/1/merge) Sim Sim
main filial Sim Não
master filial Sim Não

Execuções de pull request de repositório bifurcado

Sucursal Ler Escrever
Sucursal de destino Sim Não
Ramo intermediário (como refs/pull/1/merge) Sim Sim
main filial 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.

Exemplos

Para projetos Ruby usando Bundler, substitua a BUNDLE_PATH variável de ambiente para definir o caminho onde 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   

Problemas conhecidos e comentários

Se você estiver tendo problemas para configurar o cache em seu pipeline, verifique a lista de problemas abertos no microsoft/azure-pipelines-tasks repositório. Caso não veja o problema listado, crie um novo e forneça as informações necessárias sobre o seu cenário.

Perguntas e Respostas

P: Posso limpar um cache?

R: Não há suporte para limpar um cache. No entanto, pode-se evitar acessos a caches existentes adicionando um texto literal (como version2) à sua chave de cache. 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: Um cache é criado a partir do seu especificado path e carregado após a última etapa do trabalho. 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 do cache dentro de uma organização.