Ejercicio: Incorporación de varios entornos a la canalización

Completado

Ahora está listo para actualizar la canalización a fin de implementarla en los entornos de prueba y producción. En esta unidad va a actualizar la canalización para usar plantillas de modo que se puedan reutilizar las fases en los entornos.

Durante el proceso, hará lo siguiente:

  • Agregar una plantilla de canalización para la fase de lint.
  • Agregar una plantilla de canalización que defina las fases necesarias para realizar la implementación en cualquier entorno.
  • Actualizar la canalización para usar las plantillas.
  • Ejecutar la canalización y ver los resultados.

Incorporación de una plantilla de canalización para la fase de lint

La fase de lint solo se produce una vez durante la ejecución de canalización, independientemente del número de entornos en los que se implemente la canalización. Por lo tanto, no es realmente necesario usar plantillas para la fase de lint. Pero para que el archivo de definición de canalización principal sea sencillo y fácil de leer, decide definir la fase de lint en una plantilla.

  1. En Visual Studio Code, cree una nueva carpeta de nombre pipeline-templates dentro de la carpeta deploy.

  2. Cree un nuevo archivo en la carpeta pipeline-templates con el nombre lint.yml.

    Captura de pantalla del Explorador de Visual Studio Code con la carpeta pipeline-templates y el archivo lint.yml.

  3. Pegue la siguiente definición de plantilla de canalización en el archivo:

    jobs:
    - job: LintCode
      displayName: Lint code
      steps:
        - script: |
            az bicep build --file deploy/main.bicep
          name: LintBicepCode
          displayName: Run Bicep linter
    

    La fase de lint es la misma que la fase de lint que ya está en la canalización, solo que ahora está en un archivo de plantilla de canalización independiente.

  4. Guarde los cambios y cierre el archivo.

Incorporación de una plantilla de canalización para la implementación

Cree una plantilla de canalización que defina todas las fases necesarias para implementar cada uno de los entornos. Use los parámetros de la plantilla para especificar la configuración que puede diferir entre los entornos.

  1. Cree un nuevo archivo en la carpeta pipeline-templates con el nombre deploy.yml.

    Captura de pantalla del Explorador de Visual Studio Code con la carpeta pipeline-templates y el archivo deploy.yml.

    Este archivo representa todas las actividades de implementación que se ejecutan para cada uno de los entornos.

  2. Pegue los siguientes parámetros de plantilla de canalización en el archivo:

    parameters:
    - name: environmentType
      type: string
    - name: resourceGroupName
      type: string
    - name: serviceConnectionName
      type: string
    - name: deploymentDefaultLocation
      type: string
      default: westus3
    

    Nota:

    Cuando empiece a trabajar con el archivo YAML en Visual Studio Code, es posible que vea algunas líneas onduladas de color rojo que indican que hay un problema. Esto se debe a que la extensión de Visual Studio Code para los archivos YAML a veces no adivina correctamente el esquema del archivo.

    Puede pasar por alto los problemas que notifica la extensión. O bien, si lo prefiere, puede agregar el código siguiente a la parte superior del archivo para suprimir la adivinación de la extensión:

    # yaml-language-server: $schema=./deploy.yml
    
  3. Debajo de los parámetros, pegue la definición de la fase de validación:

    stages:
    
    - ${{ if ne(parameters.environmentType, 'Production') }}:
      - stage: Validate_${{parameters.environmentType}}
        displayName: Validate (${{parameters.environmentType}} Environment)
        jobs:
        - job: ValidateBicepCode
          displayName: Validate Bicep code
          steps:
            - task: AzureResourceManagerTemplateDeployment@3
              name: RunPreflightValidation
              displayName: Run preflight validation
              inputs:
                connectedServiceName: ${{parameters.serviceConnectionName}}
                location: ${{parameters.deploymentDefaultLocation}}
                deploymentMode: Validation
                resourceGroupName: ${{parameters.resourceGroupName}}
                csmFile: deploy/main.bicep
                overrideParameters: >
                  -environmentType ${{parameters.environmentType}}
    

    Observe que se aplica una condición a esta fase. Solo se ejecuta en entornos que no son de producción.

    Observe también que el identificador de fase incluye el valor del parámetro environmentType. Este parámetro garantiza que todas las fases de la canalización tengan un identificador único. La fase también tiene una propiedad displayName para crear un nombre con el formato correcto que pueda ser leído.

  4. Debajo de la fase de validación, pegue la definición de la fase de versión preliminar:

    - ${{ if eq(parameters.environmentType, 'Production') }}:
      - stage: Preview_${{parameters.environmentType}}
        displayName: Preview (${{parameters.environmentType}} Environment)
        jobs:
        - job: PreviewAzureChanges
          displayName: Preview Azure changes
          steps:
            - task: AzureCLI@2
              name: RunWhatIf
              displayName: Run what-if
              inputs:
                azureSubscription: ${{parameters.serviceConnectionName}}
                scriptType: 'bash'
                scriptLocation: 'inlineScript'
                inlineScript: |
                  az deployment group what-if \
                    --resource-group ${{parameters.resourceGroupName}} \
                    --template-file deploy/main.bicep \
                    --parameters environmentType=${{parameters.environmentType}}
    

    Observe que esta fase también tiene aplicada una condición, aunque opuesta a la condición de la fase de validación. La fase de versión preliminar solo se ejecuta en el entorno de producción.

  5. Debajo de la fase de versión preliminar, pegue la definición de la fase de implementación:

    - stage: Deploy_${{parameters.environmentType}}
      displayName: Deploy (${{parameters.environmentType}} Environment)
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        environment: ${{parameters.environmentType}}
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
    
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: ${{parameters.serviceConnectionName}}
                    deploymentName: $(Build.BuildNumber)
                    location: ${{parameters.deploymentDefaultLocation}}
                    resourceGroupName: ${{parameters.resourceGroupName}}
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType ${{parameters.environmentType}}
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
                  name: SaveDeploymentOutputs
                  displayName: Save deployment outputs into variables
                  env:
                    DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    
  6. Debajo de la fase de implementación, pegue la definición de la fase de prueba de aceptación de la compilación:

    - stage: SmokeTest_${{parameters.environmentType}}
      displayName: Smoke Test (${{parameters.environmentType}} Environment)
      jobs:
      - job: SmokeTest
        displayName: Smoke test
        variables:
          appServiceAppHostName: $[ stageDependencies.Deploy_${{parameters.environmentType}}.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ]
        steps:
          - task: PowerShell@2
            name: RunSmokeTests
            displayName: Run smoke tests
            inputs:
              targetType: inline
              script: |
                $container = New-PesterContainer `
                  -Path 'deploy/Website.Tests.ps1' `
                  -Data @{ HostName = '$(appServiceAppHostName)' }
                Invoke-Pester `
                  -Container $container `
                  -CI
    
          - task: PublishTestResults@2
            name: PublishTestResults
            displayName: Publish test results
            condition: always()
            inputs:
              testResultsFormat: NUnit
              testResultsFiles: 'testResults.xml'
    

    Observe que la definición de la variable appServiceAppHostName incorpora el parámetro environmentType cuando hace referencia a la fase que ha publicado el nombre de host. Este parámetro garantiza que cada fase de prueba de aceptación de la compilación se ejecute en el entorno correcto.

  7. Compruebe que el archivo deploy.yml es similar al ejemplo siguiente:

    parameters:
    - name: environmentType
      type: string
    - name: resourceGroupName
      type: string
    - name: serviceConnectionName
      type: string
    - name: deploymentDefaultLocation
      type: string
      default: westus3
    
    stages:
    
    - ${{ if ne(parameters.environmentType, 'Production') }}:
      - stage: Validate_${{parameters.environmentType}}
        displayName: Validate (${{parameters.environmentType}} Environment)
        jobs:
        - job: ValidateBicepCode
          displayName: Validate Bicep code
          steps:
            - task: AzureResourceManagerTemplateDeployment@3
              name: RunPreflightValidation
              displayName: Run preflight validation
              inputs:
                connectedServiceName: ${{parameters.serviceConnectionName}}
                location: ${{parameters.deploymentDefaultLocation}}
                deploymentMode: Validation
                resourceGroupName: ${{parameters.resourceGroupName}}
                csmFile: deploy/main.bicep
                overrideParameters: >
                  -environmentType ${{parameters.environmentType}}
    
    - ${{ if eq(parameters.environmentType, 'Production') }}:
      - stage: Preview_${{parameters.environmentType}}
        displayName: Preview (${{parameters.environmentType}} Environment)
        jobs:
        - job: PreviewAzureChanges
          displayName: Preview Azure changes
          steps:
            - task: AzureCLI@2
              name: RunWhatIf
              displayName: Run what-if
              inputs:
                azureSubscription: ${{parameters.serviceConnectionName}}
                scriptType: 'bash'
                scriptLocation: 'inlineScript'
                inlineScript: |
                  az deployment group what-if \
                    --resource-group ${{parameters.resourceGroupName}} \
                    --template-file deploy/main.bicep \
                    --parameters environmentType=${{parameters.environmentType}}
    
    - stage: Deploy_${{parameters.environmentType}}
      displayName: Deploy (${{parameters.environmentType}} Environment)
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        environment: ${{parameters.environmentType}}
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
    
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: ${{parameters.serviceConnectionName}}
                    deploymentName: $(Build.BuildNumber)
                    location: ${{parameters.deploymentDefaultLocation}}
                    resourceGroupName: ${{parameters.resourceGroupName}}
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType ${{parameters.environmentType}}
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
                  name: SaveDeploymentOutputs
                  displayName: Save deployment outputs into variables
                  env:
                    DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    
    - stage: SmokeTest_${{parameters.environmentType}}
      displayName: Smoke Test (${{parameters.environmentType}} Environment)
      jobs:
      - job: SmokeTest
        displayName: Smoke test
        variables:
          appServiceAppHostName: $[ stageDependencies.Deploy_${{parameters.environmentType}}.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ]
        steps:
          - task: PowerShell@2
            name: RunSmokeTests
            displayName: Run smoke tests
            inputs:
              targetType: inline
              script: |
                $container = New-PesterContainer `
                  -Path 'deploy/Website.Tests.ps1' `
                  -Data @{ HostName = '$(appServiceAppHostName)' }
                Invoke-Pester `
                  -Container $container `
                  -CI
    
          - task: PublishTestResults@2
            name: PublishTestResults
            displayName: Publish test results
            condition: always()
            inputs:
              testResultsFormat: NUnit
              testResultsFiles: 'testResults.xml'
    
  8. Guarde los cambios en el archivo.

Actualización de la definición de canalización para usar las plantillas

  1. Abra el archivo azure-pipelines.yml.

  2. Actualice el archivo para usar las nuevas plantillas al reemplazar el contenido por el código siguiente:

    trigger:
      batch: true
      branches:
        include:
        - main
    
    pool:
      vmImage: ubuntu-latest
    
    stages:
    
    # Lint the Bicep file.
    - stage: Lint
      jobs: 
      - template: pipeline-templates/lint.yml
    
    # Deploy to the test environment.
    - template: pipeline-templates/deploy.yml
      parameters:
        environmentType: Test
        resourceGroupName: ToyWebsiteTest
        serviceConnectionName: ToyWebsiteTest
    
    # Deploy to the production environment.
    - template: pipeline-templates/deploy.yml
      parameters:
        environmentType: Production
        resourceGroupName: ToyWebsiteProduction
        serviceConnectionName: ToyWebsiteProduction
    

    Esta canalización ejecuta la fase de lint una vez. Luego usa el archivo de plantilla deploy.yml dos veces: una por entorno. Esto mantiene la definición de canalización clara y fácil de entender. Además, los comentarios ayudan a explicar lo que sucede.

  3. Guarde los cambios.

  4. Confirme e inserte los cambios en el repositorio de Git mediante la ejecución de los comandos siguientes en el terminal de Visual Studio Code:

    git add .
    git commit -m "Add pipeline templates"
    git push
    

Visualización de la ejecución de canalización

  1. En el explorador, vaya a Canalizaciones.

  2. Seleccione la ejecución más reciente de la canalización.

    Observe que ahora la ejecución de canalización muestra todas las fases que ha definido en el archivo YAML. Es posible que tenga que desplazarse horizontalmente para verlas todas.

    Captura de pantalla de Azure Pipelines en la que se muestran las fases de ejecución de canalización.

  3. Espere a que la canalización se ponga en pausar antes de la fase Implementación (entorno de producción). La canalización puede tardar unos minutos en llegar a este punto.

    Captura de pantalla de Azure Pipelines en la que se muestra la ejecución de canalización en pausa para su aprobación.

  4. Apruebe la implementación en el entorno de producción mediante la selección del botón Revisar.

  5. Seleccione el botón Aprobar.

    Captura de pantalla de la interfaz de Azure DevOps en la que se muestra la página de aprobación de la canalización y el botón Aprobar.

    Espere a que finalice la ejecución de la canalización.

  6. Seleccione la pestaña Pruebas para mostrar los resultados de las pruebas de esta ejecución de la canalización.

    Observe que ahora hay resultados de cuatro pruebas. La prueba de aceptación de la compilación se ejecuta en los entornos de prueba y producción, por lo que se ven los resultados de ambos conjuntos de pruebas.

    Captura de pantalla de Azure Pipelines en la que se muestra la página de pruebas de ejecución de canalización, con resultados de cuatro pruebas.

  7. Seleccione Canalizaciones>Entornos.

  8. Seleccione el entorno Producción.

  9. Observe que, en la pantalla de detalles del entorno, se ve información general del historial de implementaciones del entorno de producción.

    Captura de pantalla Azure Pipelines en la que se muestra el entorno de producción, con el historial de implementaciones que muestra una sola implementación.

  10. Seleccione la implementación y luego la pestaña Cambios.

    Observe que la pestaña Cambios muestra la lista de confirmaciones incluidas en la implementación. Esta información ayuda a ver exactamente lo que ha cambiado en el entorno a lo largo del tiempo.

    Captura pantalla de Azure Pipelines en la que se muestran los detalles de implementación del entorno de producción, con una lista de confirmaciones.

  11. En el explorador, vaya a Azure Portal.

  12. Vaya al grupo de recursos ToyWebsiteProduction.

  13. En la lista de recursos, abra la aplicación Azure App Service.

    Captura de pantalla de Azure Portal en la que se muestra la aplicación App Service de producción y los detalles de SKU del plan de App Service.

    Observe que el tipo de plan de App Service es S1.

  14. Vaya a la aplicación App Service del grupo de recursos ToyWebsiteTest.

    Observe que el tipo de plan de App Service es F1. Los dos entornos usan configuraciones diferentes, como se ha definido en el archivo de Bicep.