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
, , routeTraffic
y 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.