Trabajos de implementación

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

Importante

  • Los nombres de trabajo y fase no pueden contener palabras clave (ejemplo: deployment).
  • Cada trabajo de una fase debe tener un nombre único.

En las canalizaciones de YAML, se recomienda que se coloquen los pasos de implementación en un tipo especial de trabajo denominado trabajo de implementación. Un trabajo de implementación es una colección de pasos que se ejecutan secuencialmente en el entorno. Un trabajo de implementación y un trabajo tradicional pueden existir en la misma fase. Azure DevOps admite las estrategias runOnce, gradual y de valores controlados.

Los trabajos de implementación proporcionan las siguientes ventajas:

  • Historial de implementación: Obtiene el historial de implementación entre canalizaciones, hasta un recurso específico y el estado de las implementaciones para la auditoría.
  • Aplicar estrategia de implementación: defina cómo se implementa la aplicación.

Un trabajo de implementación no clona automáticamente el repositorio de origen. Puede comprobar el repositorio de origen dentro de su trabajo con checkout: self.

Nota:

Este artículo se centra en la implementación con trabajos de implementación. Para obtener información sobre cómo implementar en Azure con canalizaciones, consulte Introducción a la implementación en Azure.

Schema

Esta es la sintaxis completa para especificar un trabajo de implementación:

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 ]

Hay una sintaxis alternativa más detallada que también se puede usar para la propiedad 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.

En el caso de las máquinas virtuales, no es necesario definir un grupo. Los pasos que defina en un trabajo de implementación con un recurso de máquina virtual se ejecutarán en esa máquina virtual y no en el agente del grupo. Para otros tipos de recursos, como Kubernetes, debe definir un grupo para que las tareas se puedan ejecutar en esa máquina.

Estrategias de implementación

Al implementar actualizaciones de aplicaciones, es importante que la técnica que use para entregar la actualización:

  • Habilitación de la inicialización.
  • Implementación de la actualización.
  • Enrutamiento del tráfico a la versión actualizada.
  • Prueba de la versión actualizada después del enrutamiento del tráfico.
  • En caso de error, ejecute los pasos para restaurar a la última versión correcta conocida.

Esto se logra mediante enlaces de ciclo de vida que pueden ejecutar pasos durante la implementación. Cada uno de los enlaces de ciclo de vida se resuelve en un trabajo de agente o en un trabajo de servidor (o en un contenedor o trabajo de validación en el futuro), en función del atributo pool. De forma predeterminada, los enlaces de ciclo de vida heredarán el pool especificado por el trabajo deployment.

Los trabajos de implementación usan la variable del sistema $(Pipeline.Workspace).

Descripciones de enlaces de ciclo de vida

preDeploy: se usa para ejecutar pasos que inicializan los recursos antes de que comience la implementación de la aplicación.

deploy: se usa para ejecutar los pasos que implementan la aplicación. La tarea Descargar artefacto se insertará automáticamente solo en el enlace de deploy para trabajos de implementación. Para detener la descarga de artefactos, use - download: none o elija artefactos específicos para descargar mediante la especificación de la tarea Descargar artefacto de canalización.

routeTraffic: se usa para ejecutar pasos que sirven el tráfico a la versión actualizada.

postRouteTraffic: se usa para ejecutar los pasos después de enrutar el tráfico. Normalmente, estas tareas supervisan el estado de la versión actualizada durante un intervalo definido.

on: failure u on: success: se usan para ejecutar pasos de acciones de reversión o limpieza.

Estrategia de implementación de RunOnce

runOnce es la estrategia de implementación más sencilla en la que todos los enlaces del ciclo de vida, es decir preDeploy deploy, , routeTrafficy postRouteTraffic, se ejecutan una vez. Luego, se ejecuta 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:
          ...

Si usa agentes autohospedados, puede usar las opciones de limpieza del área de trabajo para limpiar el área de trabajo de implementación.

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

Estrategia de implementación gradual

Una implementación gradual reemplaza las instancias de la versión anterior de una aplicación por instancias de la nueva versión en un conjunto fijo de máquinas virtuales (conjunto de implementación gradual) en cada iteración.

Actualmente solo se admite la estrategia gradual para los recursos de máquina virtual.

Por ejemplo, una implementación gradual suele esperar a que se completen las implementaciones en cada conjunto de máquinas virtuales antes de continuar con el siguiente conjunto de implementaciones. Puede realizar una comprobación de estado después de cada iteración y, si se produce un problema significativo, se puede detener la implementación gradual.

Las implementaciones graduales se pueden configurar especificando la palabra clave rolling: en el nodo strategy:. La variable strategy.name está disponible en este bloque de estrategia, que toma el nombre de la estrategia. En este caso, gradual.

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

Se admiten todos los enlaces de ciclo de vida y se crean trabajos de enlace de ciclo de vida para ejecutarse en cada máquina virtual.

preDeploy, deploy, routeTraffic y postRouteTraffic se ejecutan una vez por tamaño de lote definido por maxParallel. Luego, se ejecuta on: success o on: failure.

Con maxParallel: <# or % of VMs>, puede controlar el número o porcentaje de destinos de máquina virtual que se van a implementar en paralelo. Esto garantiza que la aplicación se ejecute en estas máquinas y sea capaz de controlar las solicitudes mientras la implementación se realiza en el resto de las máquinas, lo que reduce el tiempo de inactividad general.

Nota:

Hay algunas lagunas conocidas en esta característica. Por ejemplo, cuando vuelva a intentar una fase, volverá a ejecutar la implementación en todas las máquinas virtuales, no solo destinos con errores.

Estrategia de implementación de valores controlados

La estrategia de implementación de valores controlados es una estrategia de implementación avanzada que ayuda a mitigar el riesgo implicado en la implementación de nuevas versiones de aplicaciones. Con esta estrategia, puede implementar primero los cambios en un pequeño subconjunto de servidores. A medida que gana confianza con la nueva versión, puede usarla en más servidores de la infraestructura y enrutar más tráfico a ella.

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 estrategia de implementación de valores controlados admite el enlace de ciclo de vida preDeploy (ejecutado una vez) e itera con los enlaces de ciclo de vida deploy, routeTraffic y postRouteTraffic. A continuación, sale con el enlace success o failure.

Las siguientes variables están disponibles en esta estrategia:

strategy.name: nombre de la estrategia. Por ejemplo, "de valores controlados".
strategy.action: la acción que se va a realizar en el clúster de Kubernetes. Por ejemplo, implemente, promueva o rechace.
strategy.increment: valor de incremento utilizado en la interacción actual. Esta variable solo está disponible en enlaces de ciclo de vida deploy, routeTraffic y postRouteTraffic.

Ejemplos

Estrategia de implementación de RunOnce

En el siguiente fragmento de código YAML de ejemplo se muestra un uso sencillo de un trabajo de implementación mediante la estrategia de implementación runOnce. En el ejemplo se incluye un paso de restauración.


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 cada ejecución de este trabajo, el historial de implementación se registra en el entorno smarthotel-dev.

Nota:

  • También es posible crear un entorno con recursos vacíos y usarlo como shell abstracto para registrar el historial de implementación, como se muestra en el ejemplo anterior.

En el siguiente ejemplo se muestra cómo una canalización puede hacer referencia tanto a un entorno como a un recurso que se va a usar como destino para un trabajo de implementación.

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)

Este procedimiento tiene las siguientes ventajas:

  • Registra el historial de implementación en un recurso específico dentro del entorno, en lugar de registrar el historial en todos los recursos del entorno.
  • Los pasos del trabajo de implementación heredan automáticamente los detalles de conexión del recurso (en este caso, un espacio de nombres de Kubernetes, smarthotel-dev.bookings), porque el trabajo de implementación está vinculado al entorno. Esto resulta útil en los casos en los que se establece el mismo detalle de conexión para varios pasos del trabajo.

Nota:

Si usa un clúster de AKS privado, asegúrese de que está conectado a la red virtual del clúster, ya que el punto de conexión del servidor de API no se expone a través de una dirección IP pública.

Azure Pipelines recomienda configurar un agente autohospedado dentro de una red virtual que tenga acceso a la red virtual del clúster. Consulte Opciones para conectarse al clúster privado para obtener más información.

Estrategia de implementación gradual

La estrategia gradual de las máquinas virtuales actualiza hasta cinco destinos en cada iteración. maxParallel determinará el número de destinos que se pueden implementar en paralelo. La selección tiene en cuenta el número absoluto o el porcentaje de destinos que deben permanecer disponibles en cualquier momento excluyendo los destinos en los que se está realizando la implementación. También se usa para determinar las condiciones de acierto y error durante la implementación.

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

Estrategia de implementación de valores controlados

En el siguiente ejemplo, la estrategia de valor controlado para AKS implementará primero los cambios con pods del 10 por ciento, seguido del 20 por ciento, mientras supervisa el estado durante postRouteTraffic. Si todo va bien, ascenderá al 100 por ciento.

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

Uso de decoradores de canalización para insertar pasos automáticamente

Los decoradores de canalización se pueden usar en trabajos de implementación para insertar automáticamente cualquier paso personalizado (por ejemplo, analizador de vulnerabilidades) a cada ejecución de enlace de ciclo de vida de cada trabajo de implementación. Dado que los decoradores de canalización se pueden aplicar a todas las canalizaciones de una organización, esto se puede aplicar como parte de la aplicación de prácticas de implementación seguras.

Además, los trabajos de implementación se pueden ejecutar como un trabajo de contenedor junto con servicios side-car si se definen.

Compatibilidad con variables de salida

Defina variables de salida en los enlaces de ciclo de vida de un trabajo de implementación y consúmalas en otros pasos y trabajos de bajada dentro de la misma fase.

Para compartir variables entre fases, genera un artefacto en una fase y, a continuación, lo consume en una fase posterior o usa la sintaxis stageDependencies descrita en variables.

Al ejecutar estrategias de implementación, puede acceder a variables de salida entre trabajos mediante la siguiente sintaxis.

  • Para la estrategia runOnce: $[dependencies.<job-name>.outputs['<job-name>.<step-name>.<variable-name>']] (por ejemplo, $[dependencies.JobA.outputs['JobA.StepA.VariableA']])
  • Para la estrategia runOnce más un resourceType: $[dependencies.<job-name>.outputs['Deploy_<resource-name>.<step-name>.<variable-name>']]. (por ejemplo, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Para la estrategia de valores controlados: $[dependencies.<job-name>.outputs['<lifecycle-hookname>_<increment-value>.<step-name>.<variable-name>']]
  • Para la estrategia gradual: $[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 un trabajo runOnce, especifique el nombre del trabajo en lugar del enlace del 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

Al definir un entorno en un trabajo de implementación, la sintaxis de la variable de salida varía en función de cómo se define el entorno. En este ejemplo, env1 usa la notación abreviada y env2 incluye la sintaxis completa con un 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

Al generar una variable de un trabajo en la fase uno, hacer referencia a ella desde un trabajo de implementación en la fase siguiente usa una sintaxis diferente en función de si desea establecer una variable o usarla como condición para 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)

Al generar una variable desde un trabajo de implementación, use la sintaxis stageDependencies para hacer referencia a ella desde la fase siguiente (por ejemplo, $[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)

Obtenga más información sobre cómo establecer una variable de salida de varios trabajos

Preguntas más frecuentes

Mi canalización está bloqueada con el mensaje "El trabajo está pendiente...". ¿Cómo lo puedo corregir?

Esto puede ocurrir cuando hay un conflicto de nombres entre dos trabajos. Compruebe que los trabajos de implementación de la misma fase tengan un nombre único y que los nombres de trabajo y fase no contengan palabras clave. Si el cambio de nombre no corrige el problema, revise la solución de problemas de ejecuciones de canalización.

¿Se admiten decoradores en grupos de implementación?

No. No puede usar decoradores en grupos de implementación.