Compartir comentarios
Las respuestas se generan en base a la documentación.

Introducción a Azure Pipelines con Docker

Esta guía es una contribución de la comunidad. A Docker le gustaría agradecer a Kristiyan Velkov por su valiosa contribución.

Prerrequisitos

Antes de comenzar, asegúrate de cumplir con los siguientes requisitos:

Descripción general

Esta guía te acompaña a través de la compilación y subida de imágenes de Docker usando Azure Pipelines, lo que permite un flujo de trabajo de CI simplificado y seguro para aplicaciones contenedorizadas. Aprenderás a:

  • Configurar la autenticación de Docker de forma segura.
  • Configurar un pipeline automatizado para compilar y subir imágenes.

Configurar Azure DevOps para trabajar con Docker Hub

Paso 1: Configurar una conexión de servicio de Docker Hub

Para autenticarse de forma segura en Docker Hub utilizando Azure Pipelines:

  1. Navega a Project Settings > Service Connections en tu proyecto de Azure DevOps.
  2. Selecciona New service connection > Docker Registry.
  3. Selecciona Docker Hub y proporciona tus credenciales o el token de acceso de Docker Hub.
  4. Asigna un nombre reconocible a la conexión de servicio, como my-docker-registry.
  5. Concede acceso únicamente a los pipelines específicos que lo requieran para mejorar la seguridad y aplicar el principio de menor privilegio.
Important

Evita seleccionar la option para conceder acceso a todos los pipelines a menos que sea absolutamente necesario. Aplica siempre el principio de menor privilegio.

Paso 2: Crear tu pipeline

Añade el siguiente archivo azure-pipelines.yml en la raíz de tu repositorio:

# Activa el pipeline con los commits en la rama main
trigger:
  - main

# Activa el pipeline en pull requests destinados a la rama main
pr:
  - main

# Define variables para su reutilización en todo el pipeline
variables:
  imageName: 'docker.io/$(dockerUsername)/my-image'
  buildTag: '$(Build.BuildId)'
  latestTag: 'latest'

stages:
  - stage: BuildAndPush
    displayName: Build and Push Docker Image
    jobs:
      - job: DockerJob
        displayName: Build and Push
        pool:
          vmImage: ubuntu-latest
          demands:
            - docker
        steps:
          - checkout: self
            displayName: Checkout Code

          - task: Docker@2
            displayName: Docker Login
            inputs:
              command: login
              containerRegistry: 'my-docker-registry'  # Nombre de la conexión de servicio

          - task: Docker@2
            displayName: Build Docker Image
            inputs:
              command: build
              repository: $(imageName)
              tags: |
                $(buildTag)
                $(latestTag)
              dockerfile: './Dockerfile'
              arguments: |
                --sbom=true
                --attest type=provenance
                --cache-from $(imageName):latest
            env:
              DOCKER_BUILDKIT: 1

          - task: Docker@2
            displayName: Push Docker Image
            condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
            inputs:
              command: push
              repository: $(imageName)
              tags: |
                $(buildTag)
                $(latestTag)

          # Opcional: cerrar sesión para agentes auto-alojados
          - script: docker logout
            displayName: Docker Logout (Self-hosted only)
            condition: ne(variables['Agent.OS'], 'Windows_NT')

Qué hace este pipeline

Este pipeline automatiza el proceso de compilación y despliegue de imágenes de Docker para la rama main. Garantiza un flujo de trabajo seguro y eficiente con buenas prácticas como el almacenamiento en caché, el etiquetado y la limpieza condicional. Esto es lo que hace:

  • Se activa con los commits y pull requests destinados a la rama main.
  • Se autentica de forma segura con Docker Hub mediante una conexión de servicio de Azure DevOps.
  • Compila y etiqueta la imagen de Docker utilizando Docker BuildKit para el almacenamiento en caché.
  • Sube tanto las etiquetas de buildId como latest a Docker Hub.
  • Cierra la sesión de Docker si se ejecuta en un agente Linux auto-alojado.

Cómo funciona el pipeline

Paso 1: Definir los activadores (triggers) del pipeline

trigger:
  - main

pr:
  - main

Este pipeline se activa automáticamente en:

  • Commits subidos a la rama main
  • Pull requests destinados a la rama principal main
Tip

Paso 2: Definir variables comunes

variables:
  imageName: 'docker.io/$(dockerUsername)/my-image'
  buildTag: '$(Build.BuildId)'
  latestTag: 'latest'

Estas variables garantizan la coherencia en el nombre, la versión y la reutilización en todos los pasos del pipeline:

  • imageName: la ruta de tu imagen en Docker Hub
  • buildTag: una etiqueta única para cada ejecución del pipeline
  • latestTag: un alias estable para tu imagen más reciente
Important

La variable dockerUsername no se establece automáticamente.
Configúrala de forma segura en las variables de tu pipeline de Azure DevOps:

  1. Ve a Pipelines > Edit > Variables
  2. Añade dockerUsername con tu nombre de usuario de Docker Hub

Más información: Definir y utilizar variables en Azure Pipelines

Paso 3: Definir etapas (stages) y trabajos (jobs) del pipeline

stages:
  - stage: BuildAndPush
    displayName: Build and Push Docker Image

Esta etapa se ejecuta solo si la rama de origen es main.

Tip

Paso 4: Configuración del trabajo (job)

jobs:
  - job: DockerJob
  displayName: Build and Push
  pool:
    vmImage: ubuntu-latest
    demands:
      - docker

Este trabajo utiliza la última imagen de VM de Ubuntu con soporte para Docker, proporcionada por los agentes alojados por Microsoft. Se puede reemplazar con un grupo (pool) personalizado para agentes auto-alojados si es necesario.

Tip

Paso 4.1: Descargar código (Checkout)

steps:
  - checkout: self
    displayName: Checkout Code

Este paso descarga el código de tu repositorio en el agente de compilación, de modo que el pipeline pueda acceder al Dockerfile y a los archivos de la aplicación.

Tip

Paso 4.2: Autenticarse en Docker Hub

- task: Docker@2
  displayName: Docker Login
  inputs:
    command: login
    containerRegistry: 'my-docker-registry'  # Reemplaza con el nombre de tu conexión de servicio

Utiliza una conexión de servicio del registro de Docker preconfigurada en Azure DevOps para autenticarse de forma segura sin exponer las credenciales directamente.

Tip

Paso 4.3: Compilar la imagen de Docker

 - task: Docker@2
    displayName: Build Docker Image
    inputs:
      command: build
      repository: $(imageName)
      tags: |
          $(buildTag)
          $(latestTag)
      dockerfile: './Dockerfile'
      arguments: |
          --sbom=true
          --attest type=provenance
          --cache-from $(imageName):latest
    env:
      DOCKER_BUILDKIT: 1

Esto compila la imagen con:

  • Dos etiquetas: una con el ID de compilación único (Build ID) y otra como latest
  • Docker BuildKit habilitado para compilaciones más rápidas y un almacenamiento en caché de capas eficiente
  • Obtención de caché (cache pull) de la imagen latest subida más recientemente
  • Lista de materiales de software (SBOM) para la transparencia de la cadena de suministro
  • Atestación de procedencia (provenance attestation) para verificar cómo y dónde se compiló la imagen
Tip

Paso 4.4: Subir (push) la imagen de Docker

- task: Docker@2
  displayName: Push Docker Image
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  inputs:
      command: push
      repository: $(imageName)
      tags: |
        $(buildTag)
        $(latestTag)

Al aplicar esta condición, el pipeline compila la imagen de Docker en cada ejecución para garantizar la detección temprana de problemas, pero solo sube la imagen al registro cuando los cambios se fusionan (merge) en la rama main, manteniendo tu Docker Hub limpio y enfocado.

Esto sube ambas etiquetas a Docker Hub:

  • $(buildTag) garantiza la trazabilidad por ejecución.
  • latest se utiliza para las referencias a la imagen más reciente.

Paso 4.5 Cerrar sesión de Docker (agentes auto-alojados)

- script: docker logout
  displayName: Docker Logout (Self-hosted only)
  condition: ne(variables['Agent.OS'], 'Windows_NT')

Ejecuta docker logout al final del pipeline en agentes auto-alojados basados en Linux para limpiar proactivamente las credenciales y mejorar la postura de seguridad.

Resumen

Con esta configuración de CI de Azure Pipelines, obtienes:

  • Autenticación segura de Docker mediante una conexión de servicio integrada.
  • Compilación y etiquetado automático de imágenes activados por los cambios de código.
  • Compilaciones eficientes aprovechando la caché de Docker BuildKit.
  • Limpieza segura con cierre de sesión en agentes persistentes.
  • Compilación de imágenes que cumplen con los requisitos modernos de la cadena de suministro de software con SBOM y atestaciones.

Más información

  • Documentación de Azure Pipelines: Guía completa para configurar y gestionar pipelines de CI/CD en Azure DevOps.
  • Tarea de Docker para Azure Pipelines: Referencia detallada para usar la tarea de Docker en Azure Pipelines para compilar y subir imágenes.
  • Docker Buildx Bake: Explora la herramienta de compilación avanzada de Docker para configuraciones de compilación complejas, multi-etapa y multi-plataforma. Consulta también la Guía de dominio de Buildx Bake para ver ejemplos prácticos y buenas prácticas.
  • Docker Build Cloud: Aprende sobre el servicio de compilación administrado de Docker para compilaciones de imágenes más rápidas, escalables y multi-plataforma en la nube.