Processi di distribuzione

Azure Pipelines | Azure DevOps Server 2020

Importante

  • I nomi dei processi e delle fasi non possono contenere parole chiave (ad esempio: deployment ).
  • Ogni processo in una fase deve avere un nome univoco.

Nelle pipeline YAML è consigliabile inserire i passaggi di distribuzione in un tipo speciale di processo denominato processo di distribuzione. Un processo di distribuzione è una raccolta di passaggi che vengono eseguiti in sequenza nell'ambiente. Un processo di distribuzione e un processo tradizionale possono esistere nella stessa fase.

I processi di distribuzione offrono i vantaggi seguenti:

  • Cronologia distribuzione:si ottiene la cronologia di distribuzione tra le pipeline, fino a una risorsa e a uno stato specifici delle distribuzioni per il controllo.

  • Applicare la strategia di distribuzione:si definisce la modalità di implementazione dell'applicazione.

    Nota

    Attualmente sono supportate solo le strategie runOnce, rollinge canary.

Un processo di distribuzione non clona automaticamente il repo di origine. È possibile eseguire il checkout del repo di origine all'interno del processo con checkout: self . I processi di distribuzione supportano un solo passaggio di checkout.

SCHEMA

Ecco la sintassi completa per specificare un processo di distribuzione:

jobs:
- deployment: string   # name of the deployment job, A-Z, a-z, 0-9, and underscore. The word "deploy" is a keyword and is unsupported as the deployment name.
  displayName: string  # friendly name to display in the UI
  pool:                # not required for virtual machine resources
    name: string       # Use only global level variables for defining a pool name. Stage/job level variables are not supported to define pool name.
    demands: string | [ string ]
  workspace:
    clean: outputs | resources | all # what to clean up before the job runs
  dependsOn: string
  condition: string
  continueOnError: boolean                # 'true' if future jobs should run even if this job fails; defaults to 'false'
  container: containerReference # container to run this job inside
  services: { string: string | container } # container resources to run as a service container
  timeoutInMinutes: nonEmptyString        # how long to run the job before automatically cancelling
  cancelTimeoutInMinutes: nonEmptyString  # how much time to give 'run always even if cancelled tasks' before killing them
  variables: # several syntaxes, see specific section
  environment: string  # target environment name and optionally a resource name to record the deployment history; format: <environment-name>.<resource-name>
  strategy:
    runOnce:    #rolling, canary are the other strategies that are supported
      deploy:
        steps: [ script | bash | pwsh | powershell | checkout | task | templateReference ]

È disponibile una sintassi alternativa più dettagliata che è possibile usare anche per la environment proprietà .

environment:
    name: string # Name of environment.
    resourceName: string # Name of resource.
    resourceId: string # Id of resource.
    resourceType: string # Type of environment resource.
    tags: string # List of tag filters.

Per le macchine virtuali non è necessario definire un pool. Tutti i passaggi definiti in un processo di distribuzione con una risorsa macchina virtuale verranno eseguiti su tale macchina virtuale e non sull'agente nel pool. Per altri tipi di risorse, ad esempio Kubernetes, è necessario definire un pool in modo che le attività possano essere eseguite in tale computer.

Strategie di distribuzione

Quando si distribuiscono gli aggiornamenti dell'applicazione, è importante che la tecnica utilizzata per distribuire l'aggiornamento:

  • Abilitare l'inizializzazione.
  • Distribuire l'aggiornamento.
  • Indirizzare il traffico alla versione aggiornata.
  • Testare la versione aggiornata dopo il routing del traffico.
  • In caso di errore, eseguire i passaggi per ripristinare l'ultima versione corretta nota.

A tale scopo, si usano hook del ciclo di vita che possono eseguire passaggi durante la distribuzione. Ogni hook del ciclo di vita si risolve in un processo dell'agente o in un processo server (o in un contenitore o in un processo di convalida in futuro), a seconda dell'attributo . Per impostazione predefinita, gli hook del ciclo di vita erediteranno pool l'oggetto specificato dal deployment processo.

I processi di distribuzione usano la $(Pipeline.Workspace) variabile di sistema.

Descrizioni degli hook del ciclo di vita

preDeploy: consente di eseguire passaggi che inizializzano le risorse prima dell'avvio della distribuzione dell'applicazione.

deploy: usato per eseguire i passaggi che distribuiscono l'applicazione. L'attività Scarica artefatto verrà inserita automaticamente solo deploy nell'hook per i processi di distribuzione. Per arrestare il download degli elementi, usare o scegliere elementi specifici da scaricare specificando - download: none- download: none.

routeTraffic: usato per eseguire i passaggi che servono il traffico alla versione aggiornata.

postRouteTraffic: usato per eseguire i passaggi dopo che il traffico è stato instradato. In genere, queste attività monitorano l'integrità della versione aggiornata per l'intervallo definito.

on: failure o on: success : consente di eseguire i passaggi per le azioni di rollback o la pulizia.

Strategia di distribuzione RunOnce

runOnceè la strategia di distribuzione più semplice in cui tutti gli hook del ciclo di vita, ad esempio preDeploydeploy , e , routeTraffic vengono postRouteTraffic eseguiti una sola volta. Viene quindi eseguito on:success o on:failure .

strategy: 
    runOnce:
      preDeploy:        
        pool: [ server | pool ] # See pool schema.        
        steps:
        - script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
      deploy:          
        pool: [ server | pool ] # See pool schema.        
        steps:
        ...
      routeTraffic:         
        pool: [ server | pool ]         
        steps:
        ...        
      postRouteTraffic:          
        pool: [ server | pool ]        
        steps:
        ...
      on:
        failure:         
          pool: [ server | pool ]           
          steps:
          ...
        success:          
          pool: [ server | pool ]           
          steps:
          ...

Se si usano agenti self-hosted, è possibile usare le opzioni di pulizia dell'area di lavoro per pulire l'area di lavoro di distribuzione.

  jobs:
  - deployment: deploy
    pool:
      vmImage: 'ubuntu-latest'
      workspace:
        clean: all
    environment: staging

Strategia di distribuzione in sequenza

Una distribuzione in sequenza sostituisce le istanze della versione precedente di un'applicazione con le istanze della nuova versione dell'applicazione in un set fisso di macchine virtuali (set in sequenza) in ogni iterazione.

Attualmente è possibile supportare solo la strategia in sequenza per le risorse della macchina virtuale.

Ad esempio, una distribuzione in sequenza in genere attende il completamento delle distribuzioni in ogni set di macchine virtuali prima di procedere al set successivo di distribuzioni. È possibile eseguire un controllo dell'integrità dopo ogni iterazione e, se si verifica un problema significativo, la distribuzione in sequenza può essere arrestata.

Le distribuzioni in sequenza possono essere configurate specificando la parola rolling: chiave nel strategy: nodo . La strategy.name variabile è disponibile in questo blocco di strategia, che accetta il nome della strategia. In questo caso, in sequenza.

strategy:
  rolling:
    maxParallel: [ number or percentage as x% ]
    preDeploy:        
      steps:
      - script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
    deploy:          
      steps:
      ...
    routeTraffic:         
      steps:
      ...        
    postRouteTraffic:          
      steps:
      ...
    on:
      failure:         
        steps:
        ...
      success:          
        steps:
        ...

Tutti gli hook del ciclo di vita sono supportati e i processi hook del ciclo di vita vengono creati per l'esecuzione in ogni macchina virtuale.

preDeploy, deploy , , e vengono routeTrafficpostRouteTraffic eseguiti una volta per ogni dimensione del batch definita da maxParallel . Viene quindi eseguito on: success o on: failure .

Con maxParallel: <# or % of VMs> è possibile controllare il numero/percentuale di destinazioni di macchine virtuali in cui eseguire la distribuzione in parallelo. Ciò garantisce che l'app sia in esecuzione in questi computer ed è in grado di gestire le richieste mentre la distribuzione avviene nel resto dei computer, riducendo così il tempo di inattività complessivo.

Nota

Questa funzionalità ha alcune lacune note. Ad esempio, quando si riprova a eseguire una fase, la distribuzione verrà rieseguita in tutte le macchine virtuali e non solo nelle destinazioni non riuscite.

Strategia di distribuzione canary

La strategia di distribuzione canary è una strategia di distribuzione avanzata che consente di attenuare i rischi di implementazione di nuove versioni delle applicazioni. Usando questa strategia, è possibile implementare prima le modifiche a un piccolo subset di server. Con una maggiore attendibilità nella nuova versione, è possibile rilasciarla a più server nell'infrastruttura e indirizzare più traffico a essa.

strategy: 
    canary:
      increments: [ number ]
      preDeploy:        
        pool: [ server | pool ] # See pool schema.        
        steps:
        - script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
      deploy:          
        pool: [ server | pool ] # See pool schema.        
        steps:
        ...
      routeTraffic:         
        pool: [ server | pool ]         
        steps:
        ...        
      postRouteTraffic:          
        pool: [ server | pool ]        
        steps:
        ...
      on:
        failure:         
          pool: [ server | pool ]           
          steps:
          ...
        success:          
          pool: [ server | pool ]           
          steps:
          ...

La strategia di distribuzione Canary supporta l'hook del ciclo di vita (eseguito una sola volta) ed esegue l'iterazione con gli preDeploy hook , e del ciclo di deployrouteTrafficpostRouteTraffic vita. Viene quindi chiuso con success l'hook o failure .

In questa strategia sono disponibili le variabili seguenti:

strategy.name: nome della strategia. Ad esempio, canary.
strategy.action: azione da eseguire nel cluster Kubernetes. Ad esempio, distribuire, alzare di livello o rifiutare.
strategy.increment: valore di incremento utilizzato nell'interazione corrente. Questa variabile è disponibile solo negli deploy hook del ciclo di vita , e routeTrafficpostRouteTraffic .

Esempio

Strategia di distribuzione RunOnce

Il frammento YAML di esempio seguente illustra un semplice uso di un processo di distribuzione usando la runOnce strategia di distribuzione. L'esempio include un passaggio di checkout.


jobs:
  # Track deployments on the environment.
- deployment: DeployWeb
  displayName: deploy Web App
  pool:
    vmImage: 'ubuntu-latest'
  # Creates an environment if it doesn't exist.
  environment: 'smarthotel-dev'
  strategy:
    # Default deployment strategy, more coming...
    runOnce:
      deploy:
        steps:
        - checkout: self 
        - script: echo my first deployment

A ogni esecuzione di questo processo, la cronologia di distribuzione viene registrata smarthotel-dev nell'ambiente.

Nota

  • È anche possibile creare un ambiente con risorse vuote e usarlo come shell astratta per registrare la cronologia di distribuzione, come illustrato nell'esempio precedente.

L'esempio successivo illustra come una pipeline può fare riferimento sia a un ambiente che a una risorsa da usare come destinazione per un processo di distribuzione.

jobs:
- deployment: DeployWeb
  displayName: deploy Web App
  pool:
    vmImage: 'ubuntu-latest'
  # Records deployment against bookings resource - Kubernetes namespace.
  environment: 'smarthotel-dev.bookings'
  strategy: 
    runOnce:
      deploy:
        steps:
          # No need to explicitly pass the connection details.
        - task: KubernetesManifest@0
          displayName: Deploy to Kubernetes cluster
          inputs:
            action: deploy
            namespace: $(k8sNamespace)
            manifests: |
              $(System.ArtifactsDirectory)/manifests/*
            imagePullSecrets: |
              $(imagePullSecret)
            containers: |
              $(containerRegistry)/$(imageRepository):$(tag)

Questo approccio offre i vantaggi seguenti:

  • Registra la cronologia di distribuzione in una risorsa specifica all'interno dell'ambiente, anziché registrare la cronologia in tutte le risorse all'interno dell'ambiente.
  • I passaggi nel processo di distribuzione ereditano automaticamente i dettagli di connessione della risorsa (in questo caso, uno spazio dei nomi Kubernetes, ), perché il processo di distribuzione è collegato all'ambiente. Ciò è utile nei casi in cui lo stesso dettaglio di connessione è impostato per più passaggi del processo.

Strategia di distribuzione in sequenza

La strategia in sequenza per le macchine virtuali aggiorna fino a cinque destinazioni in ogni iterazione. maxParallel determinerà il numero di destinazioni in cui è possibile eseguire la distribuzione in parallelo. La selezione rappresenta il numero assoluto o la percentuale di destinazioni che devono rimanere disponibili in qualsiasi momento, escluse le destinazioni in cui viene eseguita la distribuzione. Viene inoltre usata per determinare le condizioni di esito positivo e negativo durante la distribuzione.

jobs: 
- deployment: VMDeploy
  displayName: web
  environment:
    name: smarthotel-dev
    resourceType: VirtualMachine
  strategy:
    rolling:
      maxParallel: 5  #for percentages, mention as x%
      preDeploy:
        steps:
        - download: current
          artifact: drop
        - script: echo initialize, cleanup, backup, install certs
      deploy:
        steps:
        - task: IISWebAppDeploymentOnMachineGroup@0
          displayName: 'Deploy application to Website'
          inputs:
            WebSiteName: 'Default Web Site'
            Package: '$(Pipeline.Workspace)/drop/**/*.zip'
      routeTraffic:
        steps:
        - script: echo routing traffic
      postRouteTraffic:
        steps:
        - script: echo health check post-route traffic
      on:
        failure:
          steps:
          - script: echo Restore from backup! This is on failure
        success:
          steps:
          - script: echo Notify! This is on success

Strategia di distribuzione canary

Nell'esempio successivo, la strategia canary per il servizio AKS distribuirà prima le modifiche con pod del 10%, seguiti dal 20%, monitorando l'integrità durante postRouteTraffic . Se tutto va bene, verrà promosso al 100%.

jobs: 
- deployment: 
  environment: smarthotel-dev.bookings
  pool: 
    name: smarthotel-devPool
  strategy:                  
    canary:      
      increments: [10,20]  
      preDeploy:                                     
        steps:           
        - script: initialize, cleanup....   
      deploy:             
        steps: 
        - script: echo deploy updates... 
        - task: KubernetesManifest@0 
          inputs: 
            action: $(strategy.action)       
            namespace: 'default' 
            strategy: $(strategy.name) 
            percentage: $(strategy.increment) 
            manifests: 'manifest.yml' 
      postRouteTraffic: 
        pool: server 
        steps:           
        - script: echo monitor application health...   
      on: 
        failure: 
          steps: 
          - script: echo clean-up, rollback...   
        success: 
          steps: 
          - script: echo checks passed, notify... 

Usare gli elementi Decorator della pipeline per inserire i passaggi automaticamente

Gli elementi Decorator della pipeline possono essere usati nei processi di distribuzione per inserire automaticamente qualsiasi passaggio personalizzato (ad esempio, lo scanner di vulnerabilità) a ogni esecuzione hook del ciclo di vita di ogni processo di distribuzione. Poiché gli elementi Decorator della pipeline possono essere applicati a tutte le pipeline di un'organizzazione, questo può essere sfruttato come parte dell'applicazione di procedure di distribuzione sicure.

Inoltre, i processi di distribuzione possono essere eseguiti come processo contenitore insieme ai servizi side-car, se definiti.

Supporto per le variabili di output

Definire le variabili di output negli hook del ciclo di vita di un processo di distribuzione e utilizzare le variabili in altri passaggi e processi downstream all'interno della stessa fase.

Per condividere le variabili tra fasi, eseguire l'output di un elemento in una fase e quindi utilizzarlo in una fase successiva oppure usare la sintassi descritta in Variabili.

Durante l'esecuzione di strategie di distribuzione, è possibile accedere alle variabili di output tra processi usando la sintassi seguente.

  • Per la strategia runOnce: (ad esempio, $[dependencies.JobA.outputs['Deploy.StepA.VariableA']] )
  • Per la strategia runOnce più resourceType: . (ad esempio, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Per la strategia canary:
  • Per la strategia in sequenza:
# Set an output variable in a lifecycle hook of a deployment job executing canary strategy.
- deployment: A
  pool:
    vmImage: 'ubuntu-latest'
  environment: staging
  strategy:                  
    canary:      
      increments: [10,20]  # Creates multiple jobs, one for each increment. Output variable can be referenced with this.
      deploy:
        steps:
        - bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
          name: setvarStep
        - bash: echo $(setvarStep.myOutputVar)
          name: echovar

# Map the variable from the job.
- job: B
  dependsOn: A
  pool:
    vmImage: 'ubuntu-latest'
  variables:
    myVarFromDeploymentJob: $[ dependencies.A.outputs['deploy_10.setvarStep.myOutputVar'] ]
  steps:
  - script: "echo $(myVarFromDeploymentJob)"
    name: echovar

Per un runOnce processo, specificare il nome del processo anziché l'hook del ciclo di vita:

# Set an output variable in a lifecycle hook of a deployment job executing runOnce strategy.
- deployment: A
  pool:
    vmImage: 'ubuntu-latest'
  environment: staging
  strategy:                  
    runOnce:
      deploy:
        steps:
        - bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
          name: setvarStep
        - bash: echo $(setvarStep.myOutputVar)
          name: echovar

# Map the variable from the job.
- job: B
  dependsOn: A
  pool:
    vmImage: 'ubuntu-latest'
  variables:
    myVarFromDeploymentJob: $[ dependencies.A.outputs['A.setvarStep.myOutputVar'] ]
  steps:
  - script: "echo $(myVarFromDeploymentJob)"
    name: echovar

Quando si definisce un ambiente in un processo di distribuzione, la sintassi della variabile di output varia a seconda di come viene definito l'ambiente. In questo esempio usa env1 la notazione abbreviata e env2 include la sintassi completa con un tipo di risorsa definito.

stages:
- stage: StageA
  jobs:
  - deployment: A1
    pool:
      vmImage: 'ubuntu-latest'
    environment: env1
    strategy:                  
      runOnce:
        deploy:
          steps:
          - bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
            name: setvarStep
          - bash: echo $(System.JobName)
  - deployment: A2
    pool:
      vmImage: 'ubuntu-latest'
    environment: 
      name: env2
      resourceType: virtualmachine
    strategy:                  
      runOnce:
        deploy:
          steps:
          - script: echo "##vso[task.setvariable variable=myOutputVarTwo;isOutput=true]this is the second deployment variable value"
            name: setvarStepTwo
  
  - job: B1
    dependsOn: A1
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      myVarFromDeploymentJob: $[ dependencies.A1.outputs['A1.setvarStep.myOutputVar'] ]
      
    steps:
    - script: "echo $(myVarFromDeploymentJob)"
      name: echovar
 
  - job: B2
    dependsOn: A2
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      myVarFromDeploymentJob: $[ dependencies.A2.outputs['A2.setvarStepTwo.myOutputVar'] ]
      myOutputVarTwo: $[ dependencies.A2.outputs['Deploy_vmsfortesting.setvarStepTwo.myOutputVarTwo'] ]
    
    steps:
    - script: "echo $(myOutputVarTwo)"
      name: echovartwo

Quando si restituisce una variabile da un processo di distribuzione, per fare riferimento a essa dal processo successivo viene utilizzata una sintassi diversa a seconda che si voglia impostare una variabile o usarla come condizione per la fase.

stages:
- stage: StageA
  jobs:
  - job: A1
    steps:
      - pwsh: echo "##vso[task.setvariable variable=RunStageB;isOutput=true]true"
        name: setvarStep
      - bash: echo $(System.JobName)

- stage: StageB
  dependsOn: 
    - StageA
 
  # when referring to another stage, stage name is included in variable path
  condition: eq(dependencies.StageA.outputs['A1.setvarStep.RunStageB'], 'true')
  
  # Variables reference syntax differs slightly from inter-stage condition syntax
  variables:
    myOutputVar: $[stageDependencies.StageA.A1.outputs['setvarStep.RunStageB']]
  jobs:
  - deployment: B1
    pool:
      vmImage: 'ubuntu-latest'
    environment: envB
    strategy:                  
      runOnce:
        deploy:
          steps:
          - bash: echo $(myOutputVar)

Altre informazioni su come impostare una variabile di output multi-processo

Domande frequenti

La pipeline è bloccata con il messaggio "Processo in sospeso...". Come si risolve questo problema?

Questo problema può verificarsi quando si verifica un conflitto di nomi tra due processi. Verificare che tutti i processi di distribuzione nella stessa fase contengano un nome univoco e che i nomi dei processi e delle fasi non contengano parole chiave. Se la ridenominazione non risolve il problema, esaminare la risoluzione dei problemi relativi alle esecuzioni della pipeline.

Gli elementi Decorator sono supportati nei gruppi di distribuzione?

No. Non è possibile usare elementi Decorator nei gruppi di distribuzione.