Travaux de déploiement

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

Important

  • Les noms de travail et d’index ne peuvent pas contenir de mots clés (exemple : deployment).
  • Chaque travail inclus dans un index doit porter un nom unique.

Dans les pipelines YAML, nous vous recommandons de placer vos étapes de déploiement dans un travail de type spécial appelée travail de déploiement. Un travail de déploiement est une collection d’étapes qui sont exécutées séquentiellement sur l’environnement. Un travail de déploiement et un travail traditionnel peuvent exister dans le même index. Azure DevOps prend en charge les stratégies de déploiement runOnce, propagé et avec contrôle de validité.

Les travaux de déploiement offrent les avantages suivants :

  • Historique de déploiement : vous obtenez l’historique de déploiement entre les pipelines, jusqu’à une ressource et un état spécifiques des déploiements pour l’audit.
  • Appliquer une stratégie de déploiement : vous définissez la façon dont votre application est déployée.

Un travail de déploiement ne clone pas automatiquement le dépôt source. Vous pouvez extraire le dépôt source au sein de votre travail avec checkout: self.

Notes

Cet article se concentre sur le déploiement à l’aide de travaux de déploiement. Pour découvrir comment déployer sur Azure avec des pipelines, consultez Vue d’ensemble du déploiement sur Azure.

schéma

Voici la syntaxe complète permettant de spécifier un travail de déploiement :

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 ]

Il existe une autre syntaxe plus précise que vous pouvez également utiliser pour la propriété environment.

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.

Pour les machines virtuelles, vous n’avez pas besoin de définir un pool. Toutes les étapes que vous définissez dans un travail de déploiement avec une ressource de machine virtuelle s’exécutent sur cette machine virtuelle et non sur l’agent dans le pool. Pour d’autres types de ressources, comme Kubernetes, vous avez besoin de définir un pool afin que les tâches puissent s’exécuter sur cette machine.

Stratégies de déploiement

Lorsque vous déployez des mises à jour d’application, il est important que la technique que vous utilisez pour distribuer la mise à jour vous permette d’effectuer ceci :

  • Activez l’initialisation.
  • Déployez la mise à jour.
  • Routez le trafic vers la version mise à jour.
  • Testez la version mise à jour après le routage du trafic.
  • En cas d’échec, exécutez les étapes de restauration de la dernière bonne version connue.

Pour cela, nous utilisons des hooks de cycle de vie qui peuvent exécuter des étapes pendant le déploiement. Chacun des hooks de cycle de vie se résout en travail d’agent ou travail de serveur (prochainement en travail de conteneur ou de validation), en fonction de l’attribut pool. Par défaut, les hooks de cycle de vie héritent du pool spécifié par le travail deployment.

Les travaux de déploiement utilisent la variable système $(Pipeline.Workspace).

Descriptions des hooks de cycle de vie

preDeploy : Utilisé pour exécuter les étapes qui initialisent les ressources avant le démarrage du déploiement de l’application.

deploy : Utilisé pour exécuter les étapes qui déploient votre application. La tâche de téléchargement des artefacts est injectée automatiquement uniquement dans le hook deploy pour les travaux de déploiement. Pour arrêter le téléchargement d’artefacts, utilisez - download: none ou choisissez des artefacts spécifiques à télécharger en spécifiant la tâche Télécharger un artefact de pipeline.

routeTraffic : Utilisé pour exécuter les étapes qui servent le trafic à la version mise à jour.

postRouteTraffic : Utilisé pour exécuter les étapes après le routage du trafic. En règle générale, ces tâches supervisent l’intégrité de la version mise à jour pendant une période définie.

on: failure ou on: success : Utilisé pour exécuter les étapes de nettoyage ou des actions de restauration.

Stratégie de déploiement RunOnce

runOnce est la stratégie de déploiement la plus simple où tous les hooks de cycle de vie, à savoir preDeploy deploy, routeTrafficet postRouteTraffic, sont exécutés une seule fois. Ensuite, on: success ou on: failure est exécuté.

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:
          ...

Si vous utilisez des agents auto-hébergés, vous pouvez utiliser les options de nettoyage d’espace de travail pour nettoyer votre espace de travail de déploiement.

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

Stratégie de déploiement propagé

Un déploiement propagé remplace les instances de la version précédente d’une application par les instances de la nouvelle version de l’application sur un ensemble fixe de machines virtuelles (ensemble propagé) à chaque itération.

Actuellement, nous prenons uniquement en charge la stratégie de déploiement propagé vers des ressources de machine virtuelle.

Par exemple, un déploiement propagé attend généralement que les déploiements sur chaque ensemble de machines virtuelles se terminent avant de passer à l’ensemble suivant de déploiements. Vous pouvez effectuer un contrôle d’intégrité après chaque itération et si un problème important se produit, le déploiement propagé peut être arrêté.

Les déploiements propagés peuvent être configurés en spécifiant le mot clé rolling: sous le nœud strategy:. La variable strategy.name est disponible dans ce bloc de stratégie, qui prend le nom de la stratégie. Dans ce cas, un déploiement propagé.

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:
        ...

Tous les hooks de cycle de vie sont pris en charge et les travaux de hook de cycle de vie sont créés pour s’exécuter sur chaque machine virtuelle.

preDeploy, deploy, routeTraffic et postRouteTraffic sont exécutés une fois par taille de lot définie par maxParallel. Ensuite, on: success ou on: failure est exécuté.

Avec maxParallel: <# or % of VMs>, vous pouvez contrôler le nombre/pourcentage de cibles de machine virtuelle sur lesquelles effectuer un déploiement en parallèle. Cela garantit que l’application s’exécute sur ces machines et qu’elle est capable de gérer les demandes pendant que le déploiement a lieu sur le reste des machines, ce qui réduit le temp d’arrêt global.

Notes

Il existe quelques lacunes connues dans cette fonctionnalité. Par exemple, lorsque vous retentez un index, elle va réexécuter le déploiement sur toutes les machines virtuelles, pas seulement sur les cibles ayant échoué.

Stratégie de déploiement avec contrôle de validité

La stratégie de déploiement avec contrôle de validité est une stratégie de déploiement avancée qui permet d’atténuer les risques liés au déploiement de nouvelles versions d’applications. En utilisant cette stratégie, vous pouvez d’abord déployer les changements sur un petit sous-ensemble de serveurs. Quand la nouvelle version vous semble fiable, vous pouvez la publier sur davantage de serveurs de votre infrastructure et lui envoyer plus de trafic.

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 stratégie de déploiement avec contrôle de validité prend en charge le hook de cycle de vie preDeploy (exécuté une seule fois) et itère avec les hooks de cycle de vie deploy, routeTraffic et postRouteTraffic. Elle se termine ensuite avec le hook success ou failure.

Les variables suivantes sont disponibles dans cette stratégie :

strategy.name : Nom de la stratégie. Exemple : « canari ».
strategy.action : Action à effectuer sur le cluster Kubernetes. Par exemple, déployer, promouvoir ou rejeter.
strategy.increment : Valeur d’incrément utilisée dans l’interaction actuelle. Cette variable est disponible uniquement dans les hooks de cycle de vie deploy, routeTraffic et postRouteTraffic.

Exemples

Stratégie de déploiement RunOnce

L’exemple d’extrait de code YAML suivant présente une utilisation simple d’un travail de déploiement à l’aide de la stratégie de déploiement runOnce. L’exemple inclut une étape de validation.


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

Avec chaque exécution de ce travail, l’historique de déploiement est enregistré sur l’environnement smarthotel-dev.

Notes

  • Il est également possible de créer un environnement avec des ressources vides et de l’utiliser comme interpréteur de commandes abstrait pour enregistrer l’historique de déploiement, comme illustré dans l’exemple précédent.

L’exemple suivant montre comment un pipeline peut faire référence à un environnement et à une ressource à utiliser comme cible pour un travail de déploiement.

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)

Cette approche offre les avantages suivants :

  • Enregistre l’historique de déploiement sur une ressource spécifique au sein de l’environnement, au lieu d’enregistrer l’historique sur toutes les ressources au sein de l’environnement.
  • Les étapes du travail de déploiement héritent automatiquement des détails de connexion de la ressource (dans ce cas, un espace de noms Kubernetes, smarthotel-dev.bookings), car le travail de déploiement est lié à l’environnement. Cela s’avère utile dans les cas où le même détail de connexion est défini pour plusieurs étapes du travail.

Remarque

Si vous utilisez un cluster AKS privé, vérifiez que vous êtes connecté au réseau virtuel du cluster, car le point de terminaison du serveur d’API n’est pas exposé via une adresse IP publique.

Azure Pipelines recommande de configurer un agent auto-hébergé au sein d’un réseau virtuel qui a accès au réseau virtuel du cluster. Pour plus d’informations, consultez les options de connexion au cluster privé.

Stratégie de déploiement propagé

La stratégie de déploiement propagé pour les machines virtuelles met à jour jusqu’à cinq cibles dans chaque itération. maxParallel détermine le nombre de cibles qui peuvent simultanément faire l’objet d’un déploiement. La sélection prend en compte le nombre absolu ou le pourcentage de cibles qui doivent rester disponibles à tout moment, à l’exclusion des cibles sur lesquelles un déploiement est en cours. Il est également utilisé pour déterminer les conditions de réussite et d’échec lors d’un déploiement.

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

Stratégie de déploiement avec contrôle de validité

Dans l’exemple suivant, la stratégie avec contrôle de validité pour AKS va d’abord déployer les changements sur 10 % des pods, puis 20 %, tout en supervisant l’intégrité pendant postRouteTraffic. Si tout se passe bien, une promotion jusqu’à 100 pour cent aura lieu.

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... 

Utiliser des éléments décorateurs de pipeline pour injecter automatiquement des étapes

Des éléments décoratifs de pipeline peuvent être utilisés dans les travaux de déploiement pour injecter automatiquement n’importe quelle étape personnalisée (par exemple, l’analyseur de vulnérabilité) dans l’exécution de chaque hook de cycle de vie de chaque travail de déploiement. Étant donné que les éléments décoratifs de pipeline peuvent être appliqués à tous les pipelines dans une organisation, ils sont applicables dans le cadre de pratiques de déploiement sécurisées.

En outre, les travaux de déploiement peuvent être exécutés en tant que travail de conteneur avec un side-car de services s’il est défini.

Prise en charge des variables de sortie

Définissez des variables de sortie dans les hooks de cycle de vie d’un travail de déploiement et consommez-les dans d’autres étapes et travaux en aval au sein du même index.

Pour partager des variables entre des index, générez un artefact dans un index, puis consommez-le dans un index ultérieur, ou bien utilisez la syntaxe stageDependencies décrite dans les variables.

Lors de l’exécution de stratégies de déploiement, vous pouvez accéder aux variables de sortie entre les travaux à l’aide de la syntaxe suivante.

  • Pour une stratégie runOnce : $[dependencies.<job-name>.outputs['<job-name>.<step-name>.<variable-name>']] (par exemple, $[dependencies.JobA.outputs['JobA.StepA.VariableA']])
  • Pour une stratégie runOnce plus un resourceType : $[dependencies.<job-name>.outputs['Deploy_<resource-name>.<step-name>.<variable-name>']]. (Par exemple, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Pour une stratégie avec contrôle de validité : $[dependencies.<job-name>.outputs['<lifecycle-hookname>_<increment-value>.<step-name>.<variable-name>']]
  • Pour une stratégie de propagation : $[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

Pour un travail runOnce, spécifiez le nom du travail au lieu du hook de cycle de vie :

# 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

Lorsque vous définissez un environnement dans un travail de déploiement, la syntaxe de la variable de sortie varie en fonction de la façon dont l’environnement est défini. Dans cet exemple, env1 utilise une notation abrégée et env2 inclut la syntaxe complète dans un type de ressource défini.

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

Lorsque vous extrayez une variable d’un travail à l’étape 1, le fait de le référencer à partir d’un travail de déploiement à l’étape suivante utilise une syntaxe différente selon que vous souhaitez définir une variable ou l’utiliser comme condition pour l’étape.

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)

Lorsque vous extrayez une variable à partir d’un travail de déploiement, utilisez la syntaxe stageDependencies pour la référencer à partir de la phase suivante (par exemple). $[stageDependencies.<stage-name>.<job-name>.outputs[Deploy_<resource-name>.<step-name>.<variable-name>]]

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)

Découvrez-en plus sur la définition d’une variable de sortie multi-travaux.

Questions fréquentes (FAQ)

Mon pipeline est bloqué avec le message « Travail en attente... ». Comment puis-je résoudre ce problème ?

Ce problème peut se produire en cas de conflit de noms entre deux travaux. Vérifiez que tous les travaux de déploiement inclus dans le même index portent un nom unique et que les noms des travaux et des index ne contiennent pas de mots clés. Si un renommage ne résout pas le problème, passez en revue la section sur la résolution des problèmes d’exécutions de pipeline.

Les éléments décoratifs sont-ils pris en charge dans les groupes de déploiement ?

Non. Vous ne pouvez pas utiliser d’éléments décoratifs dans des groupes de déploiement.