Memorizzazione nella cache della pipeline

La memorizzazione nella cache della pipeline consente di ridurre il tempo di compilazione consentendo il riutilizzo degli output o delle dipendenze scaricate da un'esecuzione nelle esecuzioni successive, riducendo o evitando così il costo di ricreare o ricaricare nuovamente gli stessi file. Caching particolarmente utile negli scenari in cui le stesse dipendenze vengono scaricate più volte all'inizio di ogni esecuzione. Si tratta spesso di un processo dispendioso in termini di tempo che interessa centinaia o migliaia di chiamate di rete.

Caching può essere efficace per migliorare il tempo di compilazione, purché il tempo di ripristino e salvataggio della cache sia inferiore al tempo necessario per produrre nuovamente l'output da zero. Per questo scopo, la memorizzazione nella cache potrebbe non essere efficace in tutti gli scenari e avere effettivamente un impatto negativo sul tempo di compilazione.

Caching è attualmente supportato nei processi di distribuzione e di distribuzione, ma non nei processi di versione classica.

Quando usare gli artefatti e la memorizzazione nella cache

La memorizzazione nella cache della pipeline e gli artefatti della pipeline eseguono funzioni simili, ma sono progettati per scenari diversi e non devono essere usati in modo intercambiabile. In generale:

  • Usare gli artefatti della pipeline quando è necessario prendere file specifici prodotti in un processo e condividerli con altri processi (e questi altri processi probabilmente avranno esito negativo senza di essi).

  • Usare la memorizzazione nella cache della pipeline quando si vuole migliorare il tempo di compilazione riutilizzando i file delle esecuzioni precedenti e non disponendo di questi file non influisce sulla possibilità di esecuzione del processo.

Nota

Caching è attualmente gratuito e le cache vengono archiviate nell'archivio BLOB di Azure.

Attività Usa cache

Caching viene aggiunto a una pipeline usando Cache l'attività della pipeline. Questa attività funziona come qualsiasi altra attività e viene aggiunta alla steps sezione di un processo.

Quando viene rilevato un passaggio della cache durante un'esecuzione, l'attività ripristina la cache in base ai dati di input specificati. Se non viene trovata alcuna cache, il passaggio viene completato e viene eseguito il passaggio successivo nel processo. Dopo l'esecuzione di tutti i passaggi del processo e l'esito positivo del processo, viene eseguito un passaggio speciale di "salvataggio cache" per ogni passaggio di "cache di ripristino" che non è stato ignorato. Questo passaggio è responsabile del salvataggio della cache.

Nota

Le cache non sono modificabili, vale a dire che, dopo la creazione di una cache, il relativo contenuto non può essere modificato.

Attività Configura cache

CacheL'attività ha due input obbligatori: e keypath .

Input del percorso

path percorso della cartella da memorizzare nella cache. Può essere un percorso assoluto o relativo. I percorsi relativi vengono risolti in base a $(System.DefaultWorkingDirectory) . È possibile usare variabili predefinite, ma i caratteri jolly non sono supportati.

Input chiave

key deve essere impostato sull'identificatore per la cache da ripristinare o salvare. Le chiavi sono costituite da una combinazione di valori stringa, percorsi di file o modelli di file, in cui ogni segmento è separato da un | carattere.

  • Stringhe:
    valore fisso (ad esempio il nome della cache o un nome di strumento) o tratto da una variabile di ambiente (ad esempio il nome del sistema operativo corrente o del processo corrente)

  • Percorsi dei file:
    percorso di un file specifico di cui verrà eseguito l'hashing del contenuto. Questo file deve esistere al momento dell'esecuzione dell'attività. Tenere presente che qualsiasi segmento chiave che "ha un aspetto simile a un percorso di file" verrà trattato come un percorso di file. In particolare, sono inclusi i segmenti contenenti un . oggetto . Ciò potrebbe causare l'esito negativo dell'attività quando questo "file" non esiste.

    Suggerimento

    Per evitare che un segmento di stringa simile a un percorso venga trattato come un percorso di file, racchiuderlo tra virgolette doppie, ad esempio: "my.key" | $(Agent.OS) | key.file

  • Modelli di file:
    Elenco delimitato da virgole del modello con caratteri jolly di tipo glob che deve corrispondere ad almeno un file. Ad esempio:

    • **/yarn.lock: tutti i file yarn.lock nella directory sources
    • */asset.json, !bin/**: tutti i file asset.json che si trovano in una directory nella directory sources, ad eccezione della directory bin

Viene eseguito l'hashing del contenuto di qualsiasi file identificato da un percorso di file o da un modello di file per produrre una chiave di cache dinamica. Ciò è utile quando il progetto contiene file che identificano in modo univoco ciò che viene memorizzato nella cache. Ad esempio, a file come , , o viene fatto comunemente riferimento in una chiave della cache perché rappresentano package-lock.jsonyarn.lock tutti un set Gemfile.lockPipfile.lock univoco di dipendenze.

I percorsi di file relativi o i modelli di file vengono risolti in base a $(System.DefaultWorkingDirectory) .

Esempio:

Di seguito è riportato un esempio che illustra come memorizzare nella cache le dipendenze installate da 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

In questo esempio la chiave della cache contiene tre parti: una stringa statica ("yarn"), il sistema operativo in cui è in esecuzione il processo perché questa cache è univoca per ogni sistema operativo e l'hash del file che identifica in modo univoco il set di dipendenze nella yarn.lock cache.

Alla prima esecuzione dopo l'aggiunta dell'attività, il passaggio della cache segnala una mancata memorizzazione nella cache perché la cache identificata da questa chiave non esiste. Dopo l'ultimo passaggio, verrà creata una cache dai file in $(Pipeline.Workspace)/.yarn e caricati. All'esecuzione successiva, il passaggio della cache segnala un "riscontri nella cache" e il contenuto della cache verrà scaricato e ripristinato.

Ripristinare le chiavi

restoreKeys può essere usato se si vuole eseguire query su più chiavi o prefissi di chiave esatti. Viene usato per eseguire il fallback a un'altra chiave nel caso in cui un oggetto key non restituisce un hit. Una chiave di ripristino cerca una chiave in base al prefisso e restituisce la voce della cache creata più recente di conseguenza. Ciò è utile se la pipeline non è in grado di trovare una corrispondenza esatta, ma vuole usare invece un hit della cache parziale. Per inserire più chiavi di ripristino, è sufficiente delimitarle usando una nuova riga per indicare la chiave di ripristino (vedere l'esempio per altri dettagli). L'ordine in base al quale verranno tentate le chiavi di ripristino sarà dall'alto verso il basso.

Software necessario nell'agente self-hosted

Archiviare software/piattaforma Windows Linux Mac
GNU Tar Obbligatoria Obbligatoria No
BSD Tar No No Necessario
7-Zip Consigliato No No

I file eseguibili precedenti devono essere in una cartella elencata nella variabile di ambiente PATH. Si noti che gli agenti ospitati includono il software incluso, applicabile solo agli agenti self-hosted.

Esempio:

Ecco un esempio su come usare le chiavi di ripristino di 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

In questo esempio l'attività cache tenterà di individuare se la chiave esiste nella cache. Se la chiave non esiste nella cache, tenterà di usare la prima chiave di yarn | $(Agent.OS) ripristino. In questo modo si tenterà di cercare tutte le chiavi che corrispondono esattamente a tale chiave o che hanno tale chiave come prefisso. Un hit del prefisso può verificarsi se è presente un segmento yarn.lock hash diverso. Ad esempio, se la chiave seguente si trova nella cache in cui il precedente ha generato un hash diverso da , la chiave di ripristino yarn | $(Agent.OS) | old-yarn.lockyarn.lock restituisce un hit yarn.lock parziale. In caso di mancata esecuzione della prima chiave di ripristino, verrà utilizzata la chiave di ripristino successiva che tenterà di trovare qualsiasi chiave yarn che inizia con yarn . Per i riscontri di prefisso, il risultato restituisce la chiave della cache creata più di recente come risultato.

Nota

Una pipeline può avere una o più attività di memorizzazione nella cache. Non esiste alcun limite per la capacità di archiviazione nella cache e i processi e le attività della stessa pipeline possono accedere e condividere la stessa cache.

Isolamento e sicurezza della cache

Per garantire l'isolamento tra cache da pipeline diverse e rami diversi, ogni cache appartiene a un contenitore logico denominato ambito. Gli ambiti forniscono un limite di sicurezza che garantisce che un processo da una pipeline non possa accedere alle cache da una pipeline diversa e che un processo di creazione di una richiesta pull abbia accesso in lettura alle cache per il ramo di destinazione della richiesta pull (per la stessa pipeline), ma non possa scrivere cache (creare) nell'ambito del ramo di destinazione.

Quando viene rilevato un passaggio della cache durante un'esecuzione, la cache identificata dalla chiave viene richiesta dal server. Il server cerca quindi una cache con questa chiave dagli ambiti visibili al processo e restituisce la cache (se disponibile). Al salvataggio della cache (alla fine del processo), viene scritta una cache nell'ambito che rappresenta la pipeline e il ramo. Vedere di seguito per altri dettagli.

Ci, manuali e esecuzioni pianificate

Scope Lettura Scrittura
Ramo di origine
Ramo main No

Esecuzioni di richieste pull

Scope Lettura Scrittura
Ramo di origine No
Ramo di destinazione No
Ramo intermedio (ad esempio refs/pull/1/merge )
Ramo main No

Esecuzioni del fork delle richieste pull

Ramo Lettura Scrittura
Ramo di destinazione No
Ramo intermedio (ad esempio refs/pull/1/merge )
Ramo main No

Suggerimento

Poiché l'ambito delle cache è già quello di un progetto, una pipeline e un ramo, non è necessario includere identificatori di progetto, pipeline o ramo nella chiave della cache.

Condizionamento al ripristino della cache

In alcuni scenari, il corretto ripristino della cache dovrebbe causare l'esecuzione di un set diverso di passaggi. Ad esempio, un passaggio che installa le dipendenze può essere ignorato se la cache è stata ripristinata. Ciò è possibile usando cacheHitVar l'input dell'attività. Se si imposta questo input sul nome di una variabile di ambiente, la variabile verrà impostata su in caso di riscontri nella cache, in caso contrario verrà impostata trueinexact su false . È quindi possibile fare riferimento a questa variabile in una condizione del passaggio o all'interno di uno script.

Nell'esempio seguente il install-deps.sh passaggio viene ignorato quando viene ripristinata la cache:

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

Bundler

Per i progetti Ruby che usano Bundler, eseguire l'override della variabile di ambiente usata da Bundler per impostare il percorso in cui BUNDLE_PATHBUNDLE_PATH cerca le gemme.

Esempio:

variables:
  BUNDLE_PATH: $(Pipeline.Workspace)/.bundle

steps:
- task: Cache@2
  inputs:
    key: 'gems | "$(Agent.OS)" | my.gemspec'
    restoreKeys: | 
      gems | "$(Agent.OS)"
      gems
    path: $(BUNDLE_PATH)
  displayName: Cache gems

- script: bundle install

Ccache (C/C++)

Ccache è una cache del compilatore per C/C++. Per usare Ccache nella pipeline, assicurarsi che sia installato e, facoltativamente, aggiunto Ccache a PATH (vedere Ccache). Impostare la CCACHE_DIR variabile di ambiente su un percorso in e $(Pipeline.Workspace) memorizzare nella cache questa directory.

Esempio:

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
  inputs:
    key: 'ccache | "$(Agent.OS)"'
    path: $(CCACHE_DIR)
    restoreKeys: | 
      ccache | "$(Agent.OS)"
  displayName: ccache

Per altri dettagli, vedere Impostazioni di configurazione di Ccache.

Immagini Docker

Caching immagini Docker riduce notevolmente il tempo necessario per eseguire la pipeline.

pool:
  vmImage: 'Ubuntu-18.04'
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'))

  - script: |
      mkdir -p $(Pipeline.Workspace)/docker
      docker save -o $(Pipeline.Workspace)/docker/cache.tar cache
    displayName: Docker save
    condition: and(not(canceled()), or(failed(), ne(variables.CACHE_RESTORED, 'true')))
  • key: (obbligatorio): identificatore univoco per la cache.
  • path: (obbligatorio): percorso della cartella o del file da memorizzare nella cache.

Golang

Per i progetti Golang, è possibile specificare i pacchetti da scaricare nel file go.mod. Se la GOCACHE variabile non è già impostata, impostarla sul percorso in cui si vuole scaricare la cache.

Esempio:

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

L'uso del supporto della memorizzazione nella cache incorporato di Gradle può avere un impatto significativo sul tempo di compilazione. Per abilitare la cache di compilazione, impostare la variabile di ambiente su un percorso in ed eseguire GRADLE_USER_HOME la compilazione con o aggiungere al $(Pipeline.Workspace)--build-cacheorg.gradle.caching=truegradle.properties file.

Esempio:

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: Build
  • restoreKeys:chiavi di fallback se la chiave primaria ha esito negativo (facoltativo)

Nota

Le cache non sono modificabili, una volta creata una cache con una chiave specifica per un ambito specifico (ramo), la cache non può essere aggiornata. Ciò significa che se la chiave è un valore fisso, tutte le compilazioni successive per lo stesso ramo non saranno in grado di aggiornare la cache anche se il contenuto della cache è stato modificato. Se si vuole usare un valore di chiave fisso, è necessario usare restoreKeys l'argomento come opzione di fallback.

Maven

Maven ha un repository locale in cui archivia i download e gli artefatti compilati. Per abilitare, impostare maven.repo.local l'opzione su un percorso in e $(Pipeline.Workspace) memorizzare nella cache questa cartella.

Esempio:

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 si usa un'attività Maven,assicurarsi di passare anche la variabile perché viene sovrascritta in caso contrario:

- task: Maven@3
  inputs:
    mavenPomFile: 'pom.xml'
    mavenOptions: '-Xmx3072m $(MAVEN_OPTS)'

.NET/NuGet

Se si usa per gestire le NuGet direttamente all'interno del file di progetto e si dispone di un file, è possibile abilitare la memorizzazione nella cache impostando la variabile di ambiente su un percorso in e memorizzando nella cache questa PackageReferencespackages.lock.jsonNUGET_PACKAGES$(UserProfile) directory. Per altre informazioni su come bloccare le dipendenze, vedere Informazioni di riferimento sui pacchetti nei file di progetto.

Esempio:

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

Esistono diversi modi per abilitare la memorizzazione nella cache in un progetto Node.js, ma il modo consigliato è memorizzare nella cache la directory della cache condivisa dinpm. Questa directory è gestita da npm e contiene una versione memorizzata nella cache di tutti i moduli scaricati. Durante l'installazione, npm controlla prima questa directory (per impostazione predefinita) per i moduli che possono ridurre o eliminare le chiamate di rete al registro pubblico registro npm o a un registro privato.

Poiché il percorso predefinito della directory della cache condivisa di npm non è lo stessoin tutte le piattaforme, è consigliabile eseguire l'override della variabile di ambiente in un percorso in $(Pipeline.Workspace) . Ciò garantisce anche che la cache sia accessibile dai processi contenitore e non contenitore.

Esempio:

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 il progetto non ha un package-lock.json file, fare riferimento al package.json file nell'input della chiave della cache.

Suggerimento

Poiché elimina la cartella per garantire l'uso di un set coerente e ripetibile di moduli, è consigliabile evitare la memorizzazione nella npm ci cache quando si chiama node_modulesnode_modulesnpm ci .

Node.js/Yarn

Come con npm, esistono diversi modi per memorizzare nella cache i pacchetti installati con Yarn. Il modo consigliato è memorizzare nella cache la cartella della cache condivisa di Yarn. Questa directory è gestita da Yarn e contiene una versione memorizzata nella cache di tutti i pacchetti scaricati. Durante l'installazione, Yarn controlla prima questa directory (per impostazione predefinita) i moduli, riducendo o eliminando le chiamate di rete a registri pubblici o privati.

Esempio:

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

Configurare la memorizzazione nella cache della pipeline con ambienti Anaconda

Esempio

variables:
  CONDA_CACHE_DIR: $(Pipeline.Workspace)/.condarc

# Add conda to system path
steps:
- script: echo "##vso[task.prependpath]$CONDA/bin"
  displayName: Add conda to PATH

- 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')

PHP/Composer

Per i progetti PHP che usano Composer, eseguire l'override della COMPOSER_CACHE_DIRCOMPOSER_CACHE_DIR usata da Composer.

Esempio:

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

Problemi noti e feedback

Se si verificano problemi durante la configurazione della memorizzazione nella cache per la pipeline, controllare l'elenco dei problemi aperti nel repo. Se il problema non è elencato, crearne uno nuovo e fornire le informazioni necessarie sullo scenario.

Domande e risposte

D: È possibile cancellare una cache?

A: La cancellazione di una cache non è attualmente supportata. È tuttavia possibile aggiungere un valore letterale stringa (ad esempio ) alla chiave della cache esistente per modificare la chiave in modo da evitare riscontri version2 nelle cache esistenti. Ad esempio, modificare la chiave di cache seguente:

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

con questo:

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

D: Quando scade una cache?

A: Le cache scadono dopo sette giorni di assenza di attività.

D: È previsto un limite per le dimensioni di una cache?

A: Non è previsto alcun limite per le dimensioni delle singole cache o per le dimensioni totali di tutte le cache in un'organizzazione.