13. Docker en CI/CD

La Integración Continua (CI) y el Despliegue Continuo (CD) son prácticas fundamentales en el desarrollo de software moderno (DevOps). Docker se integra de forma natural en este flujo, ya que las imágenes de contenedor son el artefacto perfecto para pasar de una etapa a otra del pipeline.

Imágenes como artefactos en pipelines

Un pipeline de CI/CD automatiza los pasos para llevar el código desde el repositorio hasta producción. Un flujo típico que involucra Docker se ve así:

  1. Commit: Un desarrollador sube código a un repositorio Git (ej. GitHub, GitLab).
  2. Build (CI): El servidor de CI (ej. GitHub Actions, Jenkins) detecta el cambio y ejecuta un trabajo que:
    1. Descarga el código fuente.
    2. Ejecuta pruebas unitarias y de integración (a menudo usando Docker y Docker Compose para levantar servicios como bases de datos).
    3. Si las pruebas pasan, construye una imagen Docker de la aplicación usando el `Dockerfile` del proyecto.
    4. (Opcional pero recomendado) Escanea la imagen en busca de vulnerabilidades (`docker scan`).
  3. Push (CI): La imagen recién construida y verificada se etiqueta con un identificador único (como el hash del commit o un número de versión) y se sube a un registro de contenedores (ej. Docker Hub, GitHub Container Registry, Amazon ECR).
  4. Deploy (CD): Otro trabajo, a menudo manual o automático para ciertos branches, se activa. Este trabajo se conecta al servidor de producción y le ordena que descargue (`pull`) la nueva versión de la imagen desde el registro y reinicie los contenedores para usarla.

La imagen Docker es el artefacto inmutable que se mueve a través del pipeline. Esto garantiza que lo que se probó en CI es exactamente lo mismo que se desplegará en producción.

Ejemplo de integración con GitHub Actions

GitHub Actions es una potente herramienta de CI/CD integrada en GitHub. Vamos a crear un "workflow" que construya una imagen Docker y la suba a GitHub Container Registry (GHCR) cada vez que se haga un push a la rama `main`.

Paso 1: Crear el workflow

En tu repositorio, crea el directorio .github/workflows/ y dentro un archivo, por ejemplo, docker-publish.yml.

name: Docker Image CI

on:
  push:
    branches: [ "main" ]

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Log in to the Container registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: . # Directorio del Dockerfile
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          # Ejemplo de tag: ghcr.io/mi-usuario/mi-repo:a1b2c3d4

Análisis del workflow:

  • on: Define el disparador. Este workflow se ejecuta en cada `push` a la rama `main`.
  • jobs: Define los trabajos a ejecutar. Aquí solo tenemos uno: `build_and_push`.
  • runs-on: Especifica que el trabajo se ejecutará en una máquina virtual con la última versión de Ubuntu.
  • permissions: Otorga permisos al workflow para leer el contenido del repo y escribir en el registro de paquetes (GHCR).
  • steps: Son las acciones que se ejecutan secuencialmente.
  • actions/checkout@v3: Una "Action" predefinida que clona tu repositorio en la máquina virtual.
  • docker/login-action@v2: Otra Action que inicia sesión en un registro Docker. Aquí usamos `ghcr.io` y las credenciales automáticas que GitHub proporciona (`GITHUB_TOKEN`).
  • docker/build-push-action@v4: La acción principal. Construye la imagen (`build`) y la sube (`push: true`).
  • tags: Define cómo se etiquetará la imagen. Usamos una etiqueta dinámica: `ghcr.io/` seguido del nombre del repositorio y el hash del commit (`github.sha`). Esto asegura que cada imagen tenga una etiqueta única e inmutable.

Con este archivo en tu repositorio, cada vez que fusiones código a `main`, GitHub Actions automáticamente construirá tu aplicación, la empaquetará en una imagen Docker y la publicará en el registro de tu proyecto, lista para ser desplegada.