Compartilhar via


Cache de pipeline

Serviços do Azure DevOps

O cache de pipeline pode ajudar a reduzir o tempo de build reutilizando dependências baixadas de execuções anteriores, evitando a necessidade de recriar ou recarregar 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. Geralmente, esse é 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é afetar negativamente o tempo de build. É importante avaliar seu cenário específico para determinar se o cache é a abordagem certa.

Observação

O armazenamento em cache de pipelines não tem suporte para pipelines de lançamento clássicos.

Quando utilizar artefatos de pipeline em comparação ao cache de 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 obter arquivos específicos produzidos por uma tarefa e compartilhá-los com outras tarefas (e as demais provavelmente falharão sem eles).

  • Use o cache de pipeline: quando você deseja melhorar o tempo de compilação reutilizando arquivos de execuções anteriores (e não ter esses arquivos não afetará a capacidade de executar a tarefa).

Observação

O cache de pipeline e os artefatos de pipeline estão disponíveis sem custo para todas as camadas (gratuitos e pagos). Consulte o consumo de armazenamento de artefatos para obter mais detalhes.

Requisitos de agente auto-hospedado

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

Plataforma/software de arquivo morto Windows Linux Mac
GNU Tar Obrigatório Obrigatório Não
BSD alcatrão Não Não Obrigatório
7 Zip Recomendadas Não Não

Tarefa cache: como ela funciona

O cache é adicionado a um pipeline adicionando a tarefa Cache na steps seção 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 tiverem sido executadas com êxito, uma etapa especial "Pós-trabalho: Cache" será adicionada e disparada automaticamente 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 Cache

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

  1. caminho: o caminho para a pasta que você deseja armazenar em cache. Esse pode ser um caminho absoluto ou relativo. 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. chave: isso define o identificador para o 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.

    • Cadeias de caracteres:
      Um valor fixo (como o nome do cache ou o nome de uma ferramenta), ou extraído de uma variável de ambiente (como o SO atual ou o nome da tarefa).

    • Caminhos de arquivo:
      O caminho para um arquivo específico cujo conteúdo será convertido em hash. O arquivo deve existir no momento em que a tarefa é executada. Qualquer segmento semelhante a um caminho de arquivo é tratado como tal, portanto, tenha cuidado, especialmente ao usar segmentos que .contêm, 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, coloque-o entre 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 do estilo glob que devem corresponder a pelo menos um arquivo. Exemplos:

      • **/yarn.lock: todos os arquivos yarn.lock no diretório de fontes.
      • */asset.json, !bin/**: todos os arquivosasset.json localizados em algum diretório dentro do diretório de fontes, exceto aqueles no diretório bin.

O conteúdo de qualquer arquivo identificado por um caminho ou padrão de arquivo é processado 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 comopackage-lock.json, yarn.lockou Gemfile.lockPipfile.lock geralmente são referenciados em uma chave de cache, pois representam um conjunto exclusivo de dependências. Caminhos ou padrões de arquivo relativos são resolvidos em relação 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 yarn.lock arquivo (que identifica exclusivamente as dependências).

Na primeira execução após a adição da tarefa, 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 com base nos arquivos em $(Pipeline.Workspace)/s/.yarn e carregado. Na próxima execução, a etapa de cache relatará uma "ocorrência no cache" e o conteúdo do cache será baixado e restaurado.

Ao usar checkout: self, o repositório será verificado em $(Pipeline.Workspace)/s, e seu diretório .yarn provavelmente residirá no próprio repositório.

Observação

Pipeline.Workspace é o caminho local no agente que executa o pipeline em que todos os diretórios são criados. Essa variável tem o mesmo valor que Agent.BuildDirectory. Se você não estiver usando checkout: self, certifique-se de atualizar a variável YARN_CACHE_FOLDER para apontar para o local de .yarn no seu repositório.

Usar chaves de restauração

restoreKeys permite que você consulte várias chaves exatas ou prefixos de chave. Ele é usado como um fallback quando o especificado key não produz um hit. Uma chave de restauração pesquisa 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 um resultado parcial de cache.

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

  • Exemplo:

Aqui está um exemplo de como usar chaves de recuperação para cachear 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 chaves de cache que correspondam ou comecem exatamente com esse prefixo.

Uma correspondência de prefixo poderá ocorrer se o hash do yarn.lock arquivo tiver sido alterado. Por exemplo, se o 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 acerto parcial de cache.

Se a primeira chave de restauração não produzir uma correspondência, a próxima chave de restauração (yarn) isso 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 a condição de restauração

Em alguns cenários, talvez você queira executar condicionalmente as etapas com base em se o cache foi restaurado com êxito. Por exemplo, você pode ignorar uma etapa que instala dependências se o cache foi restaurado. Isso pode ser obtido usando o cacheHitVar argumento.

Definir essa entrada como o nome de uma variável de ambiente faz com que a variável seja configurada como true quando houver um acerto de cache, inexact se uma chave de restauração resultar em um acerto parcial de cache, e false se nenhum cache for encontrado. Em seguida, você pode referenciar essa variável em uma condição de etapa ou dentro de um script.

Aqui está um exemplo em que 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

Isolamento e segurança do cache

Para garantir o isolamento entre caches de diferentes pipelines e diferentes ramificações, cada cache é armazenado em um contêiner lógico chamado escopo. Os escopos atuam como um limite de segurança que garante:

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

  • Trabalhos que criam solicitações de pull podem ler caches do branch de destino (para o mesmo pipeline), mas não podem gravar (criar) caches no escopo do branch de destino.

Quando uma etapa de cache é encontrada durante uma execução, o cache identificado pela chave é solicitado do servidor. Em seguida, o servidor procura um cache com essa chave nos escopos visíveis para o trabalho e retorna o cache (se disponível). Ao salvar em cache (no final do trabalho), um cache é gravado no escopo que representa o pipeline e o branch.

execuções agendadas, manuais e de CI

Escopo Ler Gravar
Branch de origem Sim Sim
Ramificação main Sim Não
Ramificação master Sim Não

Execuções de solicitação de pull

Escopo Ler Gravar
Branch de origem Sim Não
Branch de destino Sim Não
Branch intermediário (como refs/pull/1/merge) Sim Sim
Ramificação main Sim Não
Ramificação master Sim Não

Execuções de fork de solicitação de pull

Ramo Ler Gravar
Branch de destino Sim Não
Branch intermediário (como refs/pull/1/merge) Sim Sim
Ramificação main Sim Não
Ramificação master Sim Não

Dica

Como os caches já têm escopo para um projeto, pipeline e ramificação, não há necessidade de incluir nenhum identificador de projeto, pipeline ou ramificação na chave do cache.

Exemplos

Para projetos ruby usando o Bundler, substitua a BUNDLE_PATH variável de ambiente 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   

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. Se você não vir seu problema listado, crie um novo e forneça as informações necessárias sobre seu cenário.

P e R

P: Posso limpar um cache?

R: Não há suporte para limpar um cache. No entanto, você pode evitar acessos a caches existentes adicionando um literal de string (como version2) à sua chave de cache. Por exemplo, altere a seguinte chave de cache disto:

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 o cache é carregado?

Um cache é criado a partir do que você especificou path e carregado após a última etapa do trabalho. Confira o exemplo para obter mais detalhes.

P: Há um limite para o tamanho de um cache?

R: Não há limite imposto sobre o tamanho de caches individuais ou o tamanho total do cache em uma organização.