Trabajos de implementación

Azure DevOps Services | | de 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 colocar 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.

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: se define cómo se implementa la aplicación.

    Nota

    Actualmente solo se admiten las estrategias runOnce, rolling y canary .

Un trabajo de implementación no clona automáticamente el repositorio de origen. Puede desproteger el repositorio de origen en el trabajo con checkout: self. Los trabajos de implementación solo admiten un paso de desprotección.

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 ]

También puede usar una sintaxis alternativa más detallada para la environment propiedad .

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 será:

  • 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 del 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), según el pool atributo . De forma predeterminada, los enlaces de ciclo de vida heredarán el pool especificado por el deployment trabajo.

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 se inicie la implementación de la aplicación.

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

routeTraffic: se usa para ejecutar pasos que atienden 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 para el intervalo definido.

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

Estrategia de implementación runOnce

runOnce es la estrategia de implementación más sencilla, donde todos los enlaces del ciclo de vida, es decir preDeploydeploy, , 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 limpias 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 de la aplicación en un conjunto fijo de máquinas virtuales (conjunto 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 normalmente espera 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 strategy: nodo. La strategy.name variable está disponible en este bloque de estrategia, que toma el nombre de la estrategia. En este caso, rodando.

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, routeTrafficy 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 ejecuta en estas máquinas y es capaz de controlar las solicitudes mientras se realiza la implementación 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 controlada 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 controlada admite el enlace de preDeploy ciclo de vida (ejecutado una vez) e itera con los deployenlaces de ciclo de vida , routeTrafficy postRouteTraffic . A continuación, sale con el success gancho o failure .

Las siguientes variables están disponibles en esta estrategia:

strategy.name: nombre de la estrategia. Por ejemplo, valor controlado.
strategy.action: la acción que se va a realizar en el clúster de Kubernetes. Por ejemplo, implementar, promover o rechazar.
strategy.increment: valor de incremento utilizado en la interacción actual. Esta variable solo está disponible en deploylos enlaces de ciclo de vida , routeTrafficy postRouteTraffic .

Ejemplos

Estrategia de implementación runOnce

El siguiente fragmento de código YAML de ejemplo muestra un uso sencillo de un trabajo de implementación mediante la runOnce estrategia de implementación. En el ejemplo se incluye un paso de desprotecció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 smarthotel-dev entorno.

Nota

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

En el ejemplo siguiente 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 enfoque 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.

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 ejemplo siguiente, 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, se promoverá 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, detector de vulnerabilidades) en 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 aprovechar 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 los 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 los consuma en otros pasos y trabajos de nivel inferior 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 stageDependencies sintaxis descrita en variables.

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

  • Para la estrategia runOnce : $[dependencies.<job-name>.outputs['<lifecycle-hookname>.<step-name>.<variable-name>']] (por ejemplo, $[dependencies.JobA.outputs['Deploy.StepA.VariableA']])
  • Para la estrategia runOnce más un resourceType: $[dependencies.<job-name>.outputs['<lifecycle-hookname>_<resource-name>.<step-name>.<variable-name>']]. (por ejemplo, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Para la estrategia de valor controlado : $[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 runOnce trabajo, 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 defina el entorno. En este ejemplo, env1 usa la notación abreviada e 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
      resourceType: virtualmachine
    strategy:                  
      runOnce:
        deploy:
          steps:
          - script: echo "##vso[task.setvariable variable=myOutputVarTwo;isOutput=true]this is the second deployment variable value"
            name: setvarStepTwo
  
  - job: B1
    dependsOn: A1
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      myVarFromDeploymentJob: $[ dependencies.A1.outputs['A1.setvarStep.myOutputVar'] ]
      
    steps:
    - script: "echo $(myVarFromDeploymentJob)"
      name: echovar
 
  - job: B2
    dependsOn: A2
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      myVarFromDeploymentJob: $[ dependencies.A2.outputs['A2.setvarStepTwo.myOutputVar'] ]
      myOutputVarTwo: $[ dependencies.A2.outputs['Deploy_vmsfortesting.setvarStepTwo.myOutputVarTwo'] ]
    
    steps:
    - script: "echo $(myOutputVarTwo)"
      name: echovartwo

Al generar una variable de un trabajo de implementación, al hacer referencia a ella desde el siguiente trabajo se 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)

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 se pueden usar decoradores en grupos de implementación.