Processi di distribuzione

Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020

Importante

  • I nomi di processo e di fase 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 di processo specifico denominato processo di distribuzione. Un processo di distribuzione è una raccolta di passaggi eseguiti in sequenza nell'ambiente. Un processo di distribuzione e un processo tradizionale possono esistere nella stessa fase. Azure DevOps supporta le strategie runOnce, rolling e canary .

I processi di distribuzione offrono i vantaggi seguenti:

  • Cronologia della distribuzione: si ottiene la cronologia di distribuzione tra le pipeline, fino a una risorsa specifica e allo stato delle distribuzioni per il controllo.
  • Applicare la strategia di distribuzione: si definisce la modalità di implementazione dell'applicazione.

Un processo di distribuzione non clona automaticamente il repository di origine. È possibile estrarre il repository di origine all'interno del processo con checkout: self.

Nota

Questo articolo è incentrato sulla distribuzione con i processi di distribuzione. Per informazioni su come eseguire la distribuzione in Azure con le pipeline, vedere Panoramica della distribuzione in Azure.

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 usata per recapitare l'aggiornamento:

  • Abilitare l'inizializzazione.
  • Distribuire l'aggiornamento.
  • Instradare 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 valida nota.

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

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

Descrizioni degli hook del ciclo di vita

preDeploy: usato per eseguire i 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 nell'hook per i deploy processi di distribuzione. Per interrompere il download degli artefatti, usare - download: none o scegliere elementi specifici da scaricare specificando l'attività Scarica artefatto pipeline.

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

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

on: failure o on: success: usato per 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, ovvero preDeploy deploy, routeTraffice postRouteTraffic, vengono eseguiti una sola volta. Viene quindi eseguita 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: MyDeploy
    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 istanze della nuova versione dell'applicazione in un set fisso di macchine virtuali (set in sequenza) in ogni iterazione.

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

Ad esempio, una distribuzione in sequenza attende in genere il completamento delle distribuzioni in ogni set di macchine virtuali prima di procedere al set successivo di distribuzioni. È possibile eseguire un controllo di 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 chiave rolling: nel strategy: nodo. La strategy.name variabile è disponibile in questo blocco di strategia, che prende il nome della strategia. In questo caso, rotolare.

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 postRouteTraffic vengono eseguiti una volta per ogni dimensione del batch definita da maxParallel. routeTraffic Viene quindi eseguita on: success o on: failure.

Con maxParallel: <# or % of VMs>è possibile controllare il numero/percentuale di destinazioni di macchine virtuali da distribuire in parallelo. In questo modo l'app è in esecuzione in questi computer ed è in grado di gestire le richieste mentre la distribuzione avviene sul resto dei computer, riducendo così il tempo di inattività complessivo.

Nota

Questa funzionalità presenta alcune lacune note. Ad esempio, quando si ritenta una fase, verrà rieseguita la distribuzione in tutte le macchine virtuali non solo in destinazioni non riuscite.

Strategia di distribuzione canary

La strategia di distribuzione Canary è una strategia di distribuzione avanzata che consente di ridurre il rischio di implementazione di nuove versioni delle applicazioni. Usando questa strategia, è possibile implementare prima le modifiche in un piccolo sottoinsieme di server. Man mano che si acquisisce maggiore familiarità con la nuova versione, è possibile rilasciarla in più server dell'infrastruttura e instradarvi più traffico.

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 preDeploy ciclo di vita (eseguito una sola volta) ed esegue l'iterazione con gli deployhook del ciclo di vita , routeTraffice postRouteTraffic . Viene quindi chiuso con il success gancio 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 usato nell'interazione corrente. Questa variabile è disponibile solo negli deployhook del ciclo di vita , routeTraffice postRouteTraffic .

Esempi

Strategia di distribuzione RunOnce

Il frammento di codice 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 estrazione.


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

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

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.

Nell'esempio seguente viene illustrato 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 presenta i seguenti vantaggi:

  • 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 del processo di distribuzione ereditano automaticamente i dettagli di connessione della risorsa (in questo caso uno spazio dei nomi Kubernetes, smarthotel-dev.bookings), perché il processo di distribuzione è collegato all'ambiente. Ciò è utile nei casi in cui vengono impostati gli stessi dettagli di connessione per più passaggi del processo.

Nota

Se si usa un cluster del servizio Azure Kubernetes privato, assicurarsi di essere connessi alla rete virtuale del cluster perché l'endpoint del server API non viene esposto tramite un indirizzo IP pubblico.

Azure Pipelines consiglia di configurare un agente self-hosted all'interno di una rete virtuale che abbia accesso alla rete virtuale del cluster. Per informazioni dettagliate, vedere Opzioni per la connessione al cluster privato.

Strategia di distribuzione in sequenza

La strategia in sequenza per le macchine virtuali viene aggiornata 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 seguente, la strategia canary per il servizio Azure Kubernetes distribuirà prima di tutto le modifiche con pod al 10%, seguito dal 20% durante il monitoraggio dell'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 i decorator della pipeline per inserire automaticamente i passaggi

I decorator della pipeline possono essere usati nei processi di distribuzione per inserire automaticamente qualsiasi passaggio personalizzato (ad esempio, scanner di vulnerabilità) a ogni esecuzione dell'hook del ciclo di vita di ogni processo di distribuzione. Poiché gli elementi decorator della pipeline possono essere applicati a tutte le pipeline in un'organizzazione, questa operazione può essere applicata nell'ambito 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 usarle in altri passaggi downstream e processi all'interno della stessa fase.

Per condividere le variabili tra le fasi, generare un artefatto in una fase e quindi usarla in una fase successiva oppure usare la stageDependencies sintassi descritta in variabili.

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

  • Per la strategia runOnce : $[dependencies.<job-name>.outputs['<job-name>.<step-name>.<variable-name>']] (ad esempio, $[dependencies.JobA.outputs['JobA.StepA.VariableA']])
  • Per la strategia runOnce e un resourceType: $[dependencies.<job-name>.outputs['Deploy_<resource-name>.<step-name>.<variable-name>']]. (ad esempio, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Per la strategia canary : $[dependencies.<job-name>.outputs['<lifecycle-hookname>_<increment-value>.<step-name>.<variable-name>']]
  • Per la strategia in sequenza : $[dependencies.<job-name>.outputs['<lifecycle-hookname>_<resource-name>.<step-name>.<variable-name>']]
# 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 della modalità di definizione dell'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
      resourceName: vmsfortesting
      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.myOutputVarTwo'] ]
      myOutputVarTwo: $[ dependencies.A2.outputs['Deploy_vmsfortesting.setvarStepTwo.myOutputVarTwo'] ]
    
    steps:
    - script: "echo $(myOutputVarTwo)"
      name: echovartwo

Quando si restituisce una variabile da un processo nella fase 1, fare riferimento a una variabile da un processo di distribuzione nella fase successiva usa 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)

Quando si restituisce una variabile da un processo di distribuzione, usare la sintassi stageDependencies per farvi riferimento dalla fase successiva, $[stageDependencies.<stage-name>.<job-name>.outputs[Deploy_<resource-name>.<step-name>.<variable-name>]]ad esempio .

stages:
- stage: StageA
  jobs:
    - deployment: A1
      environment: 
        name:  env1
        resourceName: DevEnvironmentV
        resourceType: virtualMachine
      strategy:
        runOnce:
          deploy:
            steps:
              - script: echo "##vso[task.setvariable variable=myVar;isOutput=true]true"
                name: setvarStep
              - script: |
                  echo "Value of myVar in the same Job : $(setVarStep.myVar)"
                displayName: 'Verify variable in StageA'
- stage: StageB
  dependsOn: StageA

  # Full Variables syntax for inter-stage jobs
  variables:
    myOutputVar: $[stageDependencies.StageA.A1.outputs['Deploy_DevEnvironmentV.setvarStep.myVar']]
  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?

Ciò può verificarsi quando si verifica un conflitto di nomi tra due processi. Verificare che tutti i processi di distribuzione nella stessa fase abbiano un nome univoco e che i nomi di processo e fase 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.