Trabalhos de implantação

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

Importante

  • Os nomes de trabalho e fase não podem conter palavras-chave (exemplo: deployment).
  • Cada trabalho em uma fase deve ter um nome exclusivo.

Em pipelines do YAML, recomendamos colocar as etapas de implantação em um tipo especial de trabalho chamado de trabalho de implantação. Um trabalho de implantação é uma coleção de etapas que são executadas em sequência no ambiente. Um trabalho de implantação e um trabalho tradicional podem existir na mesma fase. O Azure DevOps dá suporte às estratégias runOnce, rolling e canary.

Os trabalhos de implantação oferecem os seguintes benefícios:

  • Histórico de implantação: é possível obter o histórico de implantação entre pipelines (detalhando até um recurso específico) e o status das implantações para auditorias.
  • Aplicação de estratégia de implantação: você define como seu aplicativo será implantado.

Um trabalho de implantação não clona automaticamente o repositório de origem. Você pode fazer o check-out do repositório de origem de dentro do seu trabalho com checkout: self.

Observação

Este artigo se concentra na implantação com trabalhos de implantação. Para saber como implantar no Azure com pipelines, confira Visão geral de como implantar no Azure.

Esquema

Aqui está a sintaxe completa para especificar um trabalho de implantação:

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 ]

Há uma sintaxe alternativa mais detalhada que você também pode usar para a propriedade 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.

Para máquinas virtuais, você não precisa definir um pool. Todas as etapas definidas em um trabalho de implantação com um recurso de máquina virtual serão executadas nessa máquina virtual, não no agente no pool. Para outros tipos de recursos, como o Kubernetes, você precisa definir um pool para que as tarefas possam ser executadas nesse computador.

Estratégias de implantação

Quando você está implantando atualizações de aplicativos, é importante que a técnica usada para entregar a atualização seja:

  • Habilitar a inicialização.
  • Implantar a atualização.
  • Encaminhar o tráfego para a versão atualizada.
  • Testar a versão atualizada após o roteamento do tráfego.
  • Em caso de falha, execute as etapas para restaurar para a última versão boa conhecida.

Conseguimos isso usando ganchos de ciclo de vida que podem executar etapas durante a implantação. Cada um dos ganchos de ciclo de vida é resolvido em um trabalho de agente ou em um trabalho de servidor (ou em um contêiner ou trabalho de validação no futuro), dependendo do atributo pool. Por padrão, os ganchos de ciclo de vida herdarão o pool especificado pelo trabalho deployment.

Os trabalhos de implantação usam a variável do sistema $(Pipeline.Workspace).

Descrições de ganchos de ciclo de vida

preDeploy: usado para executar etapas que inicializam recursos antes do início da implantação do aplicativo.

deploy: usado para executar etapas que implantam seu aplicativo. A tarefa baixar artefato será injetada automaticamente apenas no gancho de deploy para trabalhos de implantação. Para interromper o download de artefatos, use - download: none ou escolha artefatos específicos para baixar especificando tarefa Baixar artefato de pipeline.

routeTraffic: usado para executar etapas que atendem o tráfego para a versão atualizada.

postRouteTraffic: usado para executar as etapas depois que o tráfego é roteado. Normalmente, essas tarefas monitoram a integridade da versão atualizada para um intervalo definido.

on: failure ou on: success: usado para executar etapas para reverter ações ou limpar.

Estratégia de implantação do RunOnce

runOnce é a estratégia de implantação mais simples em que todos os ganchos do ciclo de vida, ou seja preDeploy deploy, , routeTraffice postRouteTraffic, são executados uma vez. Em seguida, on: success ou on: failure é executado.

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 você estiver usando agentes auto-hospedados, poderá usar as opções de limpeza do workspace para limpar seu workspace de implantação.

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

Estratégia de implantação sem interrupção

uma implantação sem interrupção substitui as instâncias da versão anterior de um aplicativo por instâncias da nova versão do aplicativo em um conjunto fixo de máquinas virtuais (conjunto sem interrupção) em cada iteração.

No momento, só damos suporte à estratégia sem interrupção para recursos de VM.

Por exemplo, uma implantação sem interrupção normalmente aguarda a conclusão de implantações em cada conjunto de máquinas virtuais antes de prosseguir para o próximo conjunto de implantações. Você pode fazer uma verificação de integridade após cada iteração e, se ocorrer um problema significativo, a implantação sem interrupção poderá ser interrompida.

Implantações dinâmicas podem ser configuradas especificando a palavra-chave rolling: no nó strategy:. A variável strategy.name está disponível nesse bloco de estratégia, que leva o nome da estratégia. Nesse caso, dinâmica.

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

Todos os ganchos de ciclo de vida têm suporte e trabalhos de gancho de ciclo de vida são criados para execução em cada VM.

preDeploy, deploy, routeTraffic e postRouteTraffic são executados uma vez por tamanho de lote definido por maxParallel. Depois, on: success ou on: failure é executado.

Com maxParallel: <# or % of VMs>, você pode controlar o número/percentual de destinos de máquina virtual para implantar em paralelo. Isso garante que o aplicativo esteja em execução nesses computadores e seja capaz de lidar com solicitações enquanto a implantação está ocorrendo no restante dos computadores, o que reduz o tempo de inatividade geral.

Observação

Há algumas lacunas conhecidas nesse recurso. Por exemplo, quando você tentar novamente uma fase, ele executará novamente a implantação em todas as VMs, não apenas em destinos com falha.

Estratégia de implantação canário

A estratégia de implantação canário é avançada e ajuda a atenuar o risco envolvido na implantação de novas versões de aplicativos. Com essa estratégia, você pode implantar as alterações em um pequeno subconjunto de servidores primeiro. À medida que você tiver confiança na nova versão, poderá liberá-la para mais servidores na sua infraestrutura e rotear mais tráfego para ela.

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

A estratégia de implantação canário dá suporte ao gancho do ciclo de vida preDeploy (executado uma vez) e itera com os ganchos de ciclo de vida deploy, routeTraffic e postRouteTraffic. Em seguida, ele sai com o gancho success ou failure.

As seguintes variáveis estão disponíveis nesta estratégia:

strategy.name: nome da estratégia. Por exemplo, canário.
strategy.action: a ação a ser executada no cluster do Kubernetes. Por exemplo, implantar, promover ou rejeitar.
strategy.increment: o valor de incremento usado na interação atual. Essa variável está disponível apenas em ganchos de ciclo de vida deploy, routeTraffic e postRouteTraffic.

Exemplos

Estratégia de implantação do RunOnce

O snippet do YAML de exemplo a seguir mostra um uso simples de um trabalho de implantação usando a estratégia de implantação runOnce. O exemplo inclui uma etapa de check-out.


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

Com cada execução desse trabalho, o histórico de implantação é registrado no ambiente smarthotel-dev.

Observação

  • Também é possível criar um ambiente com recursos vazios e usá-lo como um shell abstrato para registrar o histórico de implantação, conforme mostrado no exemplo anterior.

O exemplo a seguir demonstra como um pipeline pode se referir um ambiente e um recurso a ser usado como o destino de um trabalho de implantação.

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)

Essa abordagem tem os seguintes benefícios:

  • Registra o histórico de implantação em um recurso específico dentro do ambiente, em vez de registrar o histórico em todos os recursos dentro do ambiente.
  • As etapas no trabalho de implantação herdam automaticamente os detalhes de conexão do recurso (nesse caso, um namespace do Kubernetes, smarthotel-dev.bookings), porque o trabalho de implantação está vinculado ao ambiente. Isso é útil nos casos em que o mesmo detalhe de conexão é definido para várias etapas do trabalho.

Observação

Se você estiver usando um cluster AKS privado, verifique se está conectado à rede virtual do cluster, pois o ponto de extremidade do servidor de API não é exposto por meio de um endereço IP público.

O Azure Pipelines recomenda configurar um agente auto-hospedado em uma VNET que tenha acesso à rede virtual do cluster. Consulte Opções para se conectar ao cluster privado para obter detalhes.

Estratégia de implantação sem interrupção

A estratégia sem interrupção para VMs atualiza até cinco destinos em cada iteração. maxParallel determinará o número de destinos para os quais as implantações podem ocorrer paralelamente. As contas de seleção para um número absoluto ou um percentual de destinos que devem permanecer disponíveis a qualquer momento, excluindo os destinos para os quais as implantações estão sendo realizadas. Ele também é usado para determinar as condições de êxito e falha durante a implantação.

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

Estratégia de implantação canário

No próximo exemplo, a estratégia canário do AKS implantará primeiro as alterações com pods de 10%, seguidos por 20%, enquanto monitora a integridade durante o postRouteTraffic. Se tudo correr bem, promoverá 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... 

Usar decoradores de pipeline para injetar etapas automaticamente

Os decoradores de pipeline podem ser usados em trabalhos de implantação para injetar automaticamente qualquer etapa personalizada (por exemplo, verificador de vulnerabilidades) em cada execução de gancho de ciclo de vida de cada trabalho de implantação. Como os decoradores de pipeline podem ser aplicados a todos os pipelines em uma organização, isso pode ser aplicado como parte da imposição de práticas de implantação seguras.

Além disso, os trabalhos de implantação podem ser executados como um trabalho de contêiner junto com os serviços de sidecar, se definidos.

Suporte para variáveis de saída

Defina variáveis de saída nos ganchos de ciclo de vida de um trabalho de implantação e consuma-as em outras etapas downstream e trabalhos dentro da mesma fase.

Para compartilhar variáveis entre fases, gere um artefato em uma fase e, em seguida, consuma-o em uma fase subsequente ou use a sintaxe stageDependencies descrita em variáveis.

Ao executar estratégias de implantação, você pode acessar variáveis de saída entre trabalhos usando a sintaxe a seguir.

  • Para a estratégia runOnce: $[dependencies.<job-name>.outputs['<job-name>.<step-name>.<variable-name>']] (por exemplo, $[dependencies.JobA.outputs['JobA.StepA.VariableA']])
  • Para a estratégia runOnce mais um resourceType: $[dependencies.<job-name>.outputs['Deploy_<resource-name>.<step-name>.<variable-name>']]. (por exemplo, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Para estratégia canary: $[dependencies.<job-name>.outputs['<lifecycle-hookname>_<increment-value>.<step-name>.<variable-name>']]
  • Para a estratégia dinâmica: $[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

Para um trabalho runOnce, especifique o nome do trabalho em vez do gancho de ciclo de vida:

# 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 você define um ambiente em um trabalho de implantação, a sintaxe da variável de saída varia dependendo de como o ambiente é definido. Neste exemplo, env1 usa notação abreviada e env2 inclui a sintaxe completa com um tipo de recurso definido.

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 você gera uma variável de um trabalho no estágio um, referenciá-la de um trabalho de implantação no próximo estágio usa uma sintaxe diferente, dependendo se você deseja definir uma variável ou usá-la como uma condição para o estágio.

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)

Ao gerar uma variável de um trabalho de implantação, use a sintaxe stageDependencies para referenciá-la no próximo estágio (por exemplo, $[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)

Saiba mais sobre como definir uma variável de saída de vários trabalhos

Perguntas frequentes

Meu pipeline está travado com a mensagem "O trabalho está pendente…". Como posso corrigir isso?

Isso pode acontecer quando há um conflito de nomes entre dois trabalhos. Verifique se os trabalhos de implantação na mesma fase têm um nome exclusivo e se os nomes de trabalho e fase não contêm palavras-chave. Se renomear não corrigir o problema, examine solucionar problemas de execuções de pipeline.

Há suporte para decoradores em grupos de implantação?

Não. Você não pode usar decoradores em grupos de implantação.