Ejercicio: Implementación de una solución de varios contenedores en un clúster de Kubernetes

Completado

La canalización de versión que se proporciona con el proyecto está diseñada para compilar la solución como contenedor de Docker e implementarla en Azure App Service. Para admitir la implementación de varios contenedores en un clúster de Kubernetes, debe modificar esta canalización.

En esta unidad aprenderá a:

  • Actualizar la canalización para desencadenar al confirmar en la rama principal.
  • Definición de las variables que se van a compartir en la canalización.
  • Compile y publique imágenes de Docker.
  • Publicar manifiestos de Kubernetes.
  • Agregar una tarea para crear un secreto de extracción de imágenes a fin de usarlo entre las instancias de Kubernetes y las del registro de contenedor
  • Implementar imágenes actualizadas en un clúster de Kubernetes.

Actualización de la canalización para admitir desencadenadores

  1. Inicie sesión en su organización de Azure DevOps y vaya a su proyecto.

  2. Seleccione Canalizaciones y elija la canalización.

  3. Seleccione Editar para editar azure-pipelines.yml.

    Andy: Esta era la fase de compilación que teníamos para la solución anterior de un solo contenedor. Sabía que no se iba a ejecutar correctamente, así que la deshabilité. Podemos empezar por volver a habilitar desencadenadores para las confirmaciones en la rama main.

  4. Reemplace la línea trigger existente en la parte superior del archivo por el siguiente fragmento de código. Esto desencadena una ejecución de canalización cada vez que se realiza una confirmación en la rama principal.

    trigger:
    - 'main'
    

Definición de variables accesibles en la canalización

Andy: Va a ser necesario agregar dos variables de canalización. Uno para especificar el nombre del repositorio de tabla de clasificación, que es tabla de clasificación. El otro es para el nombre del secreto de extracción de imágenes que se usa para compartir entre instancias de AKS y ACR durante la implementación.

  1. Agregue el siguiente código resaltado a la sección variables.

    variables:
      buildConfiguration: 'Release'
      leaderboardRepository: 'leaderboard'
      webRepository: 'web'
      tag: '$(Build.BuildId)'
      imagePullSecret: 'secret'
    

Compilación y publicación de la imagen de Docker en Azure Container Registry

Andy: Ya tenemos una tarea para compilar la aplicación web como contenedor de Docker, que publicamos en nuestro registro de contenedor. Podemos simplemente usar una segunda tarea para hacer lo mismo con la tabla de clasificación.

  1. Agregue una segunda tarea Docker@2 que compile y publique el contenedor de tablas de clasificación con el fragmento de código resaltado a continuación. Agregue esta tarea después de la tarea del contenedor web.

    - task: Docker@2
      displayName: 'Build and push the web image to container registry'
      inputs:
        command: buildAndPush
        buildContext: $(Build.Repository.LocalPath)
        repository: $(webRepository)
        dockerfile: '$(Build.SourcesDirectory)/Tailspin.SpaceGame.Web/Dockerfile'
        containerRegistry: 'Container Registry Connection'
        tags: |
          $(tag)
    
    - task: Docker@2
      displayName: 'Build and push the leaderboard image to container registry'
      inputs:
        command: buildAndPush
        buildContext: $(Build.Repository.LocalPath)
        repository: $(leaderboardRepository)
        dockerfile: '$(Build.SourcesDirectory)/Tailspin.SpaceGame.LeaderboardContainer/Dockerfile'
        containerRegistry: 'Container Registry Connection'
        tags: |
          $(tag)
    

Sugerencia

Asegúrese de que la tarea que agregue aquí usa sangría coherente con la tarea anterior, ya que el espacio en blanco es importante en un archivo YAML.

Publicación de los manifiestos de Kubernetes

Andy: Creo que podemos pasar a la fase siguiente. ¿Creéis que falta algo?

Mara: mencionó que había algunos archivos de manifiesto en el proyecto de código fuente que definen la implementación y los servicios que Kubernetes necesitará en la implementación. Deberíamos publicarlos antes de finalizar esta fase.

Andy: ¿Por qué? ¿No estarán de todas formas en el disco local?

Mara: Lo estarían si agregáramos las tareas de implementación en la misma fase que la compilación. Sin embargo, como nuestras tareas de implementación tienen lugar en una fase Implementación propia, se ejecuta en un entorno nuevo, incluso es probable que en un agente distinto. Debemos asegurarnos de publicar cualquier cosa que esta fase genere que la otra fase necesite.

Andy: Buena observación. ¿Es fácil de hacer? Solo hay que asegurarse de que la carpeta manifests se copie en el nuevo agente.

Mara: Para eso está la tarea PublishBuildArtifacts@1. Es tan común que incluso tiene una forma abreviada, publish.

  1. Agregue una tarea publish que almacene la carpeta manifests para una fase futura, como se muestra a continuación. Asegúrese de que la sangría de esta tarea coincide con la de la tarea anterior.

    - task: Docker@2
      displayName: 'Build and push the leaderboard image to container registry'
      inputs:
        command: buildAndPush
        buildContext: $(Build.Repository.LocalPath)
        repository: $(leaderboardRepository)
        dockerfile: '$(Build.SourcesDirectory)/Tailspin.SpaceGame.LeaderboardContainer/Dockerfile'
        containerRegistry: 'Container Registry Connection'
        tags: |
          $(tag)
    
    - publish: '$(Build.SourcesDirectory)/manifests'
      artifact: manifests
    

Reemplazo de la fase de implementación

Mara: Voy a reemplazar la fase Implementación existente por una que use un trabajo de implementación. Un trabajo de implementación es un tipo de trabajo especial que nos permite asociar nuestra implementación con el entorno de Azure DevOps creado anteriormente. Esto facilita el seguimiento del historial de implementación, lo que será especialmente útil a medida que nuestras soluciones se vuelvan más sofisticadas.

  1. Quite la fase Implementación existente (todo lo que aparece después de la fase de compilación) y reemplácela por el siguiente fragmento de código. Tome nota de la línea resaltada que indica el entorno de implementación que se va a usar.

    - stage: 'Deploy'
      displayName: 'Deploy the containers'
      dependsOn: Build
      jobs:
      - deployment: Deploy
        displayName: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: 'Dev'
        variables:
        - group: Release
        strategy:
          runOnce:
            deploy:
              steps:
    

    Mara: El primer paso que agregaremos en la fase de implementación consiste en descargar los artefactos de manifiesto publicados anteriormente mediante la tarea DownloadBuildArtifacts@0.

    Andy: A ver si adivino: ¿existe la forma abreviada download para esa tarea?

    Mara: ¡Efectivamente! Podemos usar el especificador current para indicar que queremos el artefacto de la ejecución actual de la canalización.

  2. Agregue las líneas resaltadas como primer paso de la fase Implementación.

    - stage: 'Deploy'
      displayName: 'Deploy the containers'
      dependsOn: Build
      jobs:
      - deployment: Deploy
        displayName: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: 'spike.default'
        variables:
        - group: Release
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: manifests
    

    Andy: Ahora tenemos que crear un secreto de extracción de imágenes que se compartirá entre nuestras instancias de ACR y AKS. ¿Hay alguna una tarea que podamos usar?

    Mara: Lo estaba mirando ahora mismo y estamos de suerte. La tarea KubernetesManifest@0 admite una acción para crear el secreto necesario.

Tarea de manifiesto de Kubernetes

La tarea del manifiesto de Kubernetes está diseñada para administrar todas las operaciones de implementación estándar necesarias para Kubernetes. Admite varias opciones de action que van desde la creación de secretos hasta la implementación de imágenes. En este caso, se usa la acción createSecret, junto con los siguientes parámetros:

  • action indica la característica que se va a ejecutar. En este caso, createSecret crea el secreto compartido.
  • connectionType especifica el tipo de conexión de servicio que se va a usar. Opciones: azureResourceManager o kubernetesServiceConnection.
  • secretName especifica el nombre del secreto que se va a crear.
  • dockerRegistryEndpoint especifica el nombre de la conexión de Azure Container Registry Services.
  • azureSubscriptionConnection especifica el nombre de la conexión de servicios ARM.
  • azureResourceGroup especifica el nombre del grupo de recursos.
  • kubernetesCluster especifica el nombre del clúster de AKS.
  • namespace especifica el espacio de nombres de Kubernetes al que se aplica esta acción.
  1. Agregue el fragmento de código siguiente al final de la canalización. Asegúrese de que tanto el nombre del grupo de recursos como el nombre del clúster coinciden con los nombres de los que creó anteriormente. Asegúrese de que la sangría de esta tarea coincide con la de la tarea de descarga.

    - task: KubernetesManifest@1
      displayName: Create imagePullSecret
      inputs:
        action: createSecret
        connectionType: azureResourceManager
        secretName: $(imagePullSecret)
        dockerRegistryEndpoint: 'Container Registry Connection'
        azureSubscriptionConnection: 'Kubernetes Cluster Connection'
        azureResourceGroup: 'tailspin-space-game-rg'
        kubernetesCluster: 'tailspinspacegame-24591'
        namespace: 'default'
    

    Andy: El paso final consiste en desencadenar la implementación de nuestras imágenes en el clúster de Kubernetes. Según la documentación, parece que podemos usar la misma tarea, pero con una acción y parámetros diferentes.

    • action indica la característica que se va a ejecutar. En este caso, deploy para implementar en el clúster de AKS.
    • connectionType especifica el tipo de conexión de servicio que se va a usar. Opciones: azureResourceManager o kubernetesServiceConnection.
    • azureSubscriptionConnection especifica el nombre de la conexión de servicios ARM.
    • azureResourceGroup especifica el nombre del grupo de recursos.
    • kubernetesCluster especifica el nombre del clúster de AKS.
    • namespace especifica el espacio de nombres de Kubernetes al que se aplica esta acción.
    • imagePullSecrets especifica la lista de secretos necesarios para extraer del registro de contenedor.
    • containers especifica la lista de imágenes del contenedor para implementar.
  2. Agregue el fragmento de código siguiente al final de la canalización. Asegúrese de que tanto el nombre del grupo de recursos como el nombre del clúster coinciden con los nombres de los que creó anteriormente. Asegúrese de que la sangría de esta tarea coincide con la de la tarea anterior.

    - task: KubernetesManifest@1
      displayName: Deploy to Kubernetes cluster
      inputs:
        action: deploy
        connectionType: azureResourceManager
        azureSubscriptionConnection: 'Kubernetes Cluster Connection'
        azureResourceGroup: 'tailspin-space-game-rg'
        kubernetesCluster: 'tailspinspacegame-24591'
        namespace: 'default'
        manifests: |
          $(Pipeline.Workspace)/manifests/deployment.yml
          $(Pipeline.Workspace)/manifests/service.yml
        imagePullSecrets: |
          $(imagePullSecret)
        containers: |
          $(RegistryName)/$(webRepository):$(tag)
          $(RegistryName)/$(leaderboardRepository):$(tag)
    

Ejecución de la canalización

  1. Seleccione Guardar en la esquina superior derecha de la página. Seleccione Guardar para confirmar el mensaje de confirmación.

  2. Seleccione Ejecutar, confirme el nombre de la rama y, a continuación, seleccione Ejecutar para desencadenar una ejecución de canalización.

  3. Seleccione Canalizaciones y, a continuación, seleccione la canalización para ver los registros a medida que se ejecuta la canalización.

  4. Después de completar la ejecución de la canalización, seleccione Entornos en el panel izquierdo y, a continuación, seleccione el entorno de desarrollo para ver los trabajos de implementación.

  5. Consultemos nuestra aplicación web implementada y el punto de conexión de API. Para ello, debemos obtener las direcciones IP externas para los servicios web y de tabla de clasificación.

  6. Vaya a Azure Portal, seleccione el clúster de AKS y, a continuación, seleccione Servicios y entradas.

    Screenshot of how to find the external IPs for your web and leaderboard services.

  7. Seleccione la dirección IP externa del servicio web para ver el sitio en AKS.

    Screenshot of the Space Game web site.

  8. Regrese a la ventana de Azure Portal donde se quedó y, a continuación, copie la dirección IP externa del servicio de tabla de clasificación. Esta dirección IP es la ubicación en la que se hospeda la API de la tabla de clasificación de forma pública.

  9. Reemplace el marcador de posición en el siguiente vínculo por la dirección IP externa que copió. También puede agregar un parámetro de consulta pageSize=10 para facilitar la visualización de la respuesta de JSON en el explorador. Use una dirección URL como la siguiente en una nueva pestaña del explorador.

    http://[IP]/api/Leaderboard?pageSize=10
    
  10. Aparecerá la respuesta de JSON sin formato de la API de tablas de clasificación hospedada en el clúster de AKS. Ahora tiene una API REST a la que puede llamar desde otras aplicaciones.

    Screenshot of a web browser showing the JSON response from the leaderboard service.

Andy: ¡Ha salido muy bien! Creo que el uso de Kubernetes es una manera excelente de adoptar una estrategia de microservicios más amplia.