Cachelagring av pipeline

Azure DevOps Services

Cachelagring av pipeline kan minska byggtiden genom att tillåta att utdata eller nedladdade beroenden från en körning återanvänds i senare körningar, vilket minskar eller undviker kostnaden för att återskapa eller ladda ned samma filer igen. Cachelagring är särskilt användbart i scenarier där samma beroenden laddas ned om och om för varje körning. Det här är ofta en tidskrävande process som omfattar hundratals eller tusentals nätverksanrop.

Cachelagring kan vara effektivt för att förbättra byggtiden, förutsatt att tiden för att återställa och spara cachen är mindre än tiden för att producera utdata igen från grunden. På grund av detta kanske cachelagring inte är effektivt i alla scenarier och kan faktiskt ha en negativ inverkan på byggtiden.

Cachelagring stöds för närvarande i CI- och distributionsjobb, men inte klassiska versionsjobb.

När artefakter ska användas jämfört med cachelagring

Cachelagring av pipeline och pipelineartefakter utför liknande funktioner men är utformade för olika scenarier och bör inte användas på ett utbytbart sätt.

  • Använd pipelineartefakter när du behöver ta specifika filer som skapats i ett jobb och dela dem med andra jobb (och dessa andra jobb misslyckas förmodligen utan dem).

  • Använd pipelinecachelagring när du vill förbättra byggtiden genom att återanvända filer från tidigare körningar (och om du inte har dessa filer påverkas inte jobbets möjlighet att köras).

Kommentar

Cachelagring av pipeline och pipelineartefakter är kostnadsfria för alla nivåer (kostnadsfri och betald). Mer information finns i Lagringsförbrukning för artefakter.

Cacheuppgift: så här fungerar den

Cachelagring läggs till i en pipeline med hjälp av Cacheaktivitet. Den här uppgiften fungerar som alla andra aktiviteter och läggs till i steps avsnittet i ett jobb.

När ett cachesteg påträffas under en körning återställer uppgiften cachen baserat på de angivna indata. Om ingen cache hittas slutförs steget och nästa steg i jobbet körs.

När alla steg i jobbet har körts och ett lyckat jobb har status, läggs ett särskilt "Post-job: Cache" -steg automatiskt till och utlöses för varje "återställningscache" -steg som inte hoppades över. Det här steget ansvarar för att spara cacheminnet.

Kommentar

Cacheminnen är oföränderliga, vilket innebär att när ett cacheminne har skapats kan dess innehåll inte ändras.

Konfigurera cacheaktiviteten

Cacheaktivitetenhar två obligatoriska argument: nyckel och sökväg:

  • path: sökvägen till mappen som ska cachelagrats. Kan vara en absolut eller relativ sökväg. Relativa sökvägar matchas mot $(System.DefaultWorkingDirectory).

Kommentar

Du kan använda fördefinierade variabler för att lagra sökvägen till den mapp som du vill cachelagrar, men jokertecken stöds inte.

  • nyckel: ska anges till identifieraren för cacheminnet som du vill återställa eller spara. Nycklarna består av en kombination av strängvärden, filsökvägar eller filmönster, där varje segment avgränsas med ett | tecken.
  • Strängar:
    Fast värde (till exempel namnet på cachen eller ett verktygsnamn) eller hämtat från en miljövariabel (till exempel det aktuella operativsystemet eller det aktuella jobbnamnet)

  • Filsökvägar:
    Sökväg till en specifik fil vars innehåll ska hashas. Den här filen måste finnas när aktiviteten körs. Tänk på att alla nyckelsegment som "ser ut som en filsökväg" behandlas som en filsökväg. I synnerhet omfattar detta segment som innehåller en .. Detta kan leda till att aktiviteten misslyckas när den här "filen" inte finns.

    Dricks

    Om du vill undvika att ett sökvägsliknande strängsegment behandlas som en filsökväg, omsluter du det med dubbla citattecken, till exempel: "my.key" | $(Agent.OS) | key.file

  • Filmönster:
    Kommaavgränsad lista över jokerteckenmönster i globformat som måste matcha minst en fil. Till exempel:

    • **/yarn.lock: alla yarn.lock-filer under källkatalogen
    • */asset.json, !bin/**: alla asset.json filer som finns i en katalog under källkatalogen, förutom under bin-katalogen

Innehållet i en fil som identifieras av en filsökväg eller ett filmönster hashas för att skapa en dynamisk cachenyckel. Det här är användbart när projektet har filer som unikt identifierar vad som cachelagras. Till exempel refereras filer som package-lock.json, yarn.lock, Gemfile.lockeller Pipfile.lock ofta i en cachenyckel eftersom de alla representerar en unik uppsättning beroenden.

Relativa filsökvägar eller filmönster matchas mot $(System.DefaultWorkingDirectory).

Exempel:

Här är ett exempel som visar hur du cachelagrar beroenden som installerats av 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

I det här exemplet innehåller cachenyckeln tre delar: en statisk sträng ("yarn"), operativsystemet som jobbet körs på eftersom cacheminnet är unikt per operativsystem och hashen för yarn.lock filen som unikt identifierar uppsättningen beroenden i cacheminnet.

Vid den första körningen efter att aktiviteten har lagts till rapporterar cachesteget en "cachemiss" eftersom cacheminnet som identifieras av den här nyckeln inte finns. Efter det sista steget skapas en cache från filerna i $(Pipeline.Workspace)/s/.yarn och laddas upp. Vid nästa körning rapporterar cachesteget en "cacheträff" och innehållet i cachen laddas ned och återställs.

När du använder checkout: selfcheckas lagringsplatsen ut till $(Pipeline.Workspace)/soch mappen .yarn finns vanligtvis på själva lagringsplatsen.

Kommentar

Pipeline.Workspace är den lokala sökvägen på agenten som kör din pipeline där alla kataloger skapas. Den här variabeln har samma värde som Agent.BuildDirectory.

Se till att du uppdaterar variabeln YARN_CACHE_FOLDER om du använder något annat än checkout: self detta bör peka på lagringsplatsen där .yarn den finns.

Återställa nycklar

restoreKeys kan användas om man vill köra frågor mot flera exakta nycklar eller nyckelprefix. Detta används för att återgå till en annan nyckel om en key inte ger en träff. En återställningsnyckel söker efter en nyckel med prefix och ger därför den senaste skapade cacheposten. Detta är användbart om pipelinen inte kan hitta en exakt matchning men vill använda en partiell cacheträff i stället. Om du vill infoga flera återställningsnycklar avgränsar du dem genom att använda en ny rad för att ange återställningsnyckeln (se exemplet för mer information). Ordningen som återställningsnycklarna ska provas mot kommer att vara uppifrån och ned.

Nödvändig programvara på en lokalt installerad agent

Arkivera programvara/plattform Windows Linux Mac
GNU Tar Obligatoriskt Obligatoriskt Nej
BSD-tjära Nej Nej Obligatoriskt
7-Zip Rekommenderat Nej Nej

Ovanstående körbara filer måste finnas i en mapp som anges i PATH-miljövariabeln. Tänk på att de värdbaserade agenterna kommer med den programvara som ingår, detta gäller endast för lokalt installerade agenter.

Exempel:

Här är ett exempel på hur du använder återställningsnycklar av 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

I det här exemplet försöker cacheuppgiften ta reda på om nyckeln finns i cacheminnet. Om nyckeln inte finns i cacheminnet försöker den använda den första återställningsnyckeln yarn | $(Agent.OS). Detta kommer att försöka söka efter alla nycklar som antingen exakt matchar den nyckeln eller har den nyckeln som ett prefix. En prefixträff kan inträffa om det fanns ett annat yarn.lock hash-segment. Om följande nyckel yarn | $(Agent.OS) | old-yarn.lock till exempel fanns i cachen där den old-yarn.lock gav en annan hash än yarn.lockger återställningsnyckeln en partiell träff. Om det finns en miss på den första återställningsnyckeln använder den sedan nästa återställningsnyckel yarn som försöker hitta alla nycklar som börjar med yarn. För prefixträffar ger resultatet den senast skapade cachenyckeln som resultat.

Kommentar

En pipeline kan ha en eller flera cachelagringsaktiviteter. Det finns ingen gräns för cachelagringskapaciteten, och jobb och uppgifter från samma pipeline kan komma åt och dela samma cache.

Cacheisolering och säkerhet

För att säkerställa isolering mellan cacheminnen från olika pipelines och olika grenar tillhör varje cache en logisk container som kallas för ett omfång. Omfång ger en säkerhetsgräns som säkerställer att ett jobb från en pipeline inte kan komma åt cacheminnena från en annan pipeline, och ett jobb som skapar en PR har läsåtkomst till cacheminnena för PR:s målgren (för samma pipeline), men kan inte skriva (skapa) cacheminnen i målgrenens omfång.

När ett cachesteg påträffas under en körning begärs cachen som identifieras av nyckeln från servern. Servern letar sedan efter en cache med den här nyckeln från de omfång som är synliga för jobbet och returnerar cacheminnet (om tillgängligt). Vid cachesparning (i slutet av jobbet) skrivs en cache till omfånget som representerar pipelinen och grenen. Se nedan för mer information.

CI, manuella och schemalagda körningar

Omfattning Lästa Skriv
Källgren Ja Ja
main branch (standardgren) Ja Nej

Pull-begäranden körs

Omfattning Lästa Skriv
Källgren Ja Nej
Målgren Ja Nej
Mellanliggande gren (till exempel refs/pull/1/merge) Ja Ja
main branch (standardgren) Ja Nej

Pull-begärandeförgreningskörningar

Filial Lästa Skriv
Målgren Ja Nej
Mellanliggande gren (till exempel refs/pull/1/merge) Ja Ja
main branch (standardgren) Ja Nej

Dricks

Eftersom cacheminnen redan är begränsade till ett projekt, en pipeline och en gren behöver du inte inkludera några projekt-, pipeline- eller grenidentifierare i cachenyckeln.

Konditionering vid cacheåterställning

I vissa scenarier bör en lyckad återställning av cachen leda till att en annan uppsättning steg körs. Ett steg som installerar beroenden kan till exempel hoppas över om cacheminnet återställdes. Detta är möjligt med hjälp av uppgiftsindata cacheHitVar . Om du anger den här indatan till namnet på en miljövariabel kommer variabeln att ställas in på true när det finns en cacheträff, inexact vid en återställningsnyckelcacheträff, annars anges den till false. Den här variabeln kan sedan refereras i ett stegvillkor eller inifrån ett skript.

I följande exempel install-deps.sh hoppas steget över när cacheminnet återställs:

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

För Ruby-projekt som använder Bundler åsidosätter du miljövariabeln BUNDLE_PATH som används av Bundler för att ange sökvägen Bundler ska leta efter Gems i.

Exempel:

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 är en kompilatorcache för C/C++. Om du vill använda Ccache i pipelinen kontrollerar du att Ccache den är installerad och eventuellt tillagd i dina PATH (se Ccache-körningslägen). CCACHE_DIR Ange miljövariabeln till en sökväg under $(Pipeline.Workspace) och cachelagras i den här katalogen.

Exempel:

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

Mer information finns i Konfigurationsinställningar för Ccache.

Docker-avbildningar

Cachelagring Docker-avbildningar minskar avsevärt den tid det tar att köra pipelinen.

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'))
  • nyckel: (krävs) – en unik identifierare för cacheminnet.
  • path: (krävs) – sökvägen till den mapp eller fil som du vill cachelagras.

Golang

För Golang-projekt kan du ange vilka paket som ska laddas ned i go.mod-filen . Om variabeln GOCACHE inte redan har angetts anger du den till den där du vill att cachen ska laddas ned.

Exempel:

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

Att använda Gradles inbyggda cachelagringsstöd kan ha en betydande inverkan på byggtiden. Om du vill aktivera byggcachen anger du GRADLE_USER_HOME miljövariabeln till en sökväg under $(Pipeline.Workspace) och kör antingen din version med --build-cache eller lägger till org.gradle.caching=true i gradle.properties filen.

Exempel:

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: Återställningsnycklarna om den primära nyckeln misslyckas (valfritt)

Kommentar

Cacheminnen är oföränderliga när en cache med en viss nyckel har skapats för ett specifikt omfång (gren) och cacheminnet inte kan uppdateras. Det innebär att om nyckeln är ett fast värde kommer alla efterföljande versioner för samma gren inte att kunna uppdatera cacheminnet även om cachens innehåll har ändrats. Om du vill använda ett fast nyckelvärde måste du använda restoreKeys argumentet som reservalternativ.

Maven

Maven har en lokal lagringsplats där den lagrar nedladdningar och skapade artefakter. Om du vill aktivera anger du maven.repo.local alternativet till en sökväg under $(Pipeline.Workspace) och cachelagr den här mappen.

Exempel:

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

Om du använder en Maven-uppgift ser du till att även skicka variabeln eftersom den MAVEN_OPTS skrivs över annars:

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

.NET/NuGet

Om du använder PackageReferences för att hantera NuGet-beroenden direkt i projektfilen och har en packages.lock.json fil kan du aktivera cachelagring genom att ange NUGET_PACKAGES miljövariabeln till en sökväg under $(UserProfile) och cachelagra den här katalogen. Mer information om hur du låser beroenden finns i Paketreferens i projektfiler . Om du vill använda flera packages.lock.json kan du fortfarande använda följande exempel utan att göra några ändringar. Innehållet i alla packages.lock.json filer hashas och om en av filerna ändras genereras en ny cachenyckel.

Exempel:

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

Det finns olika sätt att aktivera cachelagring i ett Node.js projekt, men det rekommenderade sättet är att cachelagra npm:s delade cachekatalog. Den här katalogen hanteras av npm och innehåller en cachelagrad version av alla nedladdade moduler. Under installationen kontrollerar npm den här katalogen först (som standard) efter moduler som kan minska eller eliminera nätverksanrop till det offentliga npm-registret eller till ett privat register.

Eftersom standardsökvägen till npm:s delade cachekatalog inte är densamma på alla plattformar rekommenderar vi att du åsidosätter npm_config_cache miljövariabeln till en sökväg under $(Pipeline.Workspace). Detta säkerställer också att cachen är tillgänglig från container- och icke-containerjobb.

Exempel:

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

Om projektet inte har någon package-lock.json fil refererar du till package.json filen i cachenyckelns indata i stället.

Dricks

Eftersom npm ci tar bort node_modules mappen för att säkerställa att en konsekvent, repeterbar uppsättning moduler används bör du undvika cachelagring node_modules när du anropar npm ci.

Node.js/Yarn

Precis som med npm finns det olika sätt att cachelagrat paket som installerats med Yarn. Det rekommenderade sättet är att cachelagrat Yarns delade cachemapp. Den här katalogen hanteras av Yarn och innehåller en cachelagrad version av alla nedladdade paket. Under installationen kontrollerar Yarn den här katalogen först (som standard) för moduler, vilket kan minska eller eliminera nätverksanrop till offentliga eller privata register.

Exempel:

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

Konfigurera din pipelinecachelagring med Anaconda-miljöer:

Exempel

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/Composer

För PHP-projekt som använder Composer åsidosätter du miljövariabeln COMPOSER_CACHE_DIR som används av Composer.

Exempel:

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

Kända problem och feedback

Om du har problem med att konfigurera cachelagring för din pipeline kontrollerar du listan över öppna problem på lagringsplatsen microsoft/azure-pipelines-tasks . Om du inte ser problemet i listan skapar du ett nytt ärende och anger nödvändig information om ditt scenario.

Q&A

F: Kan jag rensa en cache?

S: Det finns för närvarande inte stöd för att rensa en cache. Du kan dock lägga till en strängliteral (till exempel version2) i din befintliga cachenyckel för att ändra nyckeln på ett sätt som undviker träffar på befintliga cacheminnen. Ändra till exempel följande cachenyckel från detta:

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

till denna:

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

F: När upphör en cache att gälla?

S: Cacheminnen upphör att gälla efter sju dagar utan aktivitet.

F: När laddas cachen upp?

S: Efter det sista steget i pipelinen skapas en cache från cacheminnet path och laddas upp. Mer information finns i exemplet.

F: Finns det en gräns för storleken på en cache?

S: Det finns ingen framtvingad gräns för storleken på enskilda cacheminnen eller den totala storleken på alla cacheminnen i en organisation.