Módulo 5: Docker en CI/CD

1. Introducción: Docker en CI/CD

Descripción

Sabes Docker. Construyes imágenes, escribes Dockerfiles eficientes, manejas multi-stage builds, y subes contenedores con docker-compose. Todo eso lo aprendiste en la guía #15. Pero hay un problema: cada vez que haces un cambio en tu código, abres la terminal, corres docker build, esperas 3-5 minutos, tageas manualmente, y pusheas al registry. Si trabajas en equipo, cada developer tiene su propio proceso de build — y a veces las imágenes difieren porque los ambientes locales son distintos.

Este módulo elimina ese proceso manual. Después de aquí, cada push al repositorio triggerea un build automático: la imagen se construye, se tagea con el SHA del commit (trazabilidad), se scannea por vulnerabilidades (seguridad), y se pushea al registry (disponibilidad) — todo sin intervención humana.

El cambio de mentalidad: Pasas de "buildeo Docker cuando me acuerdo" a "Docker se buildea automáticamente en cada push, con caching que lo hace rápido, tags que lo hacen rastreable, y security scanning que lo hace seguro."

Contexto: Vienes del Módulo 4, donde aprendiste a manejar secrets de forma segura en CI. Ahora esos secrets (como tokens de registries) se usan para autenticar el push de imágenes. Y lo que construyes aquí — imágenes en un registry — es exactamente lo que el Módulo 6 necesita para deployear automáticamente.


¿Dónde Estamos en la Guía?

Contexto

Esta guía tiene 8 módulos organizados en 3 fases:

Phase 1: CI Fundamentals (Módulos 1-3)
├── Módulo 1: Introducción a CI/CD y GitHub Actions    ✅ Completado
├── Módulo 2: Testing Automatizado en CI               ✅ Completado
└── Módulo 3: AI-Specific CI Checks                    ✅ Completado

Phase 2: CD & Deployment Pipelines (Módulos 4-6)
├── Módulo 4: Secrets y Environment Management         ✅ Completado
├── Módulo 5: Docker en CI/CD                          ← ESTÁS AQUÍ
└── Módulo 6: Deployment Pipelines

Phase 3: Production Pipelines (Módulos 7-8)
├── Módulo 7: Monitoring, Notifications y Advanced Patterns
└── Módulo 8: Proyecto Integrador — Production AI Pipeline

Duración estimada del módulo: 60-90 minutos.

¿Hacia dónde vamos?

Este módulo es el paso 5 de 8. Automatizas la construcción y distribución de imágenes Docker. La progresión es deliberada:

  1. Primero entendiste CI (módulo 1) — workflows, jobs, steps, triggers
  2. Luego automatizaste testing (módulo 2) — pytest, matrix, caching, reports
  3. Agregaste checks AI-specific (módulo 3) — prompt regression, cost estimation
  4. Manejaste secrets de forma segura (módulo 4) — API keys, OIDC, environments
  5. Ahora automatizas Docker (este módulo) — build, tag, scan, push
  6. Después deployeas (módulo 6) — staging → approval → production
  7. Agregas monitoring (módulo 7) — notificaciones, scheduled workflows
  8. Integras todo (módulo 8) — pipeline completo commit-to-production

El problema: Docker build manual

Cómo se ve hoy tu flujo de Docker

Developer: "Subí un fix al endpoint de embeddings"

Terminal:
  $ docker build -t mi-ai-app .         # 3-5 minutos esperando
  $ docker tag mi-ai-app:latest ghcr.io/user/mi-ai-app:v1.2.3   # tag manual
  $ docker push ghcr.io/user/mi-ai-app:v1.2.3                    # push manual

Otro developer:
  $ docker build -t mi-ai-app .         # Su build usa cached layers diferentes
  $ docker tag mi-ai-app:latest ghcr.io/user/mi-ai-app:v1.2.3   # Mismo tag, imagen distinta!
  $ docker push ghcr.io/user/mi-ai-app:v1.2.3                    # Sobreescribe la anterior

Problemas visibles:

  • 📋 Proceso manual repetitivo — Build, tag, push, cada vez que cambias algo
  • 📋 Tags inconsistentes — ¿Quién decidió que es v1.2.3? ¿Qué commit es eso?
  • 📋 Builds no reproducibles — Tu laptop tiene cache; el runner de CI no
  • 📋 Sin verificación de seguridad — La imagen va al registry sin escaneo
  • 📋 Sin garantía de calidad — La imagen se pushea aunque los tests fallaron

Cómo se ve después de este módulo

Developer: "Subí un fix al endpoint de embeddings"

GitHub Actions (automático):
  1. Build con docker/build-push-action
  2. Cache layers con GitHub Cache backend
  3. Tag: sha-abc1234 + v1.2.3 (si es release)
  4. Scan con trivy → 0 vulnerabilidades CRITICAL
  5. Push a GHCR → ghcr.io/user/mi-ai-app:sha-abc1234
  
  ✅ Imagen disponible en 2 minutos. Trazable. Segura. Reproducible.

La diferencia: cero intervención manual, trazabilidad por commit, seguridad por escaneo, y consistencia por build centralizado.


Qué hace diferente Docker en CI vs Docker local

Diferencias clave

AspectoDocker localDocker en CI
CacheLayers en disco local (persistente)Runner limpio en cada run (sin cache por defecto)
TagsLo que tú decidasAutomático: SHA, semver, branch
PushManual (docker push)Automático después del build
SeguridadTú decides si escaneasEscaneo obligatorio en el pipeline
ReproducibilidadDepende de tu entorno localIdéntico en cada run (runner estandarizado)
TriggerCuando te acuerdasCada push/PR automáticamente
Multi-platformSolo tu arquitecturaamd64 + arm64 si lo necesitas

La diferencia más importante: el cache

En tu laptop, Docker cachea layers en disco. Si cambias solo el código (no las dependencias), el build tarda segundos porque las layers de pip install están cacheadas. En CI, cada workflow run empieza con un runner limpio — no hay cache. Sin configuración explícita, cada build descarga la imagen base y reinstala todas las dependencias desde cero.

Para un proyecto AI con dependencias pesadas (torch, transformers, langchain), esto significa builds de 5-10 minutos sin cache. Con cache configurado, 1-2 minutos. Configurar caching es la primera prioridad después del build básico, no un "nice to have."


Objetivo del módulo

Al completar este módulo serás capaz de:

  • ✅ Configurar docker/build-push-action para buildear imágenes automáticamente en CI
  • ✅ Implementar Docker layer caching en Actions: GitHub Cache backend y Registry cache backend
  • ✅ Pushear imágenes a GHCR con GITHUB_TOKEN y a Docker Hub con secrets
  • ✅ Diseñar estrategias de tagging: SHA, semver, latest, branch-based
  • ✅ Integrar image scanning con trivy para detectar vulnerabilidades en CI
  • ✅ Configurar multi-platform builds (amd64 + arm64) cuando sea necesario
  • ✅ Construir un Docker CI Pipeline completo como proyecto del módulo

Objetivo profesional

Cuando alguien de tu equipo haga push de un cambio, la imagen Docker se construye, se escanea, se tagea con el SHA del commit, y se pushea al registry — sin que nadie abra una terminal ni corra un solo comando. Si la imagen tiene vulnerabilidades críticas, el pipeline la bloquea. Eso es Docker en CI/CD profesional.


Contenido del módulo

Mapa de cápsulas

#CápsulaQué aprenderásTipo
01Introducción (esta)Por qué automatizar Docker, el problema manual, la visiónIntro
02Build Docker Images en CIdocker/build-push-action, buildx, workflow YAMLTécnica
03Docker Layer Caching en ActionsGitHub Cache vs Registry cache, configuración, impactoTécnica
04Container RegistriesGHCR con GITHUB_TOKEN, Docker Hub con secrets, comparaciónTécnica
05Image Tagging StrategiesSHA, semver, latest, branch-based, rollbacksTécnica
06Image Scanning en CItrivy, severidades, quality gates, interpretaciónTécnica
07Multi-Platform Buildsamd64 + arm64, QEMU, cuándo necesitarloTécnica
08Proyecto: Docker CI PipelinePipeline completo: build → tag → scan → pushProyecto

Flujo de aprendizaje

Primero construyes un build básico en CI (cápsula 02). Luego lo haces rápido con caching (cápsula 03). Después configuras a dónde pushear la imagen (cápsula 04). Aprendes a tagear para trazabilidad y rollbacks (cápsula 05). Integras escaneo de seguridad para detectar vulnerabilidades (cápsula 06). Opcionalmente, agregas multi-platform para diferentes arquitecturas (cápsula 07). Finalmente, integras todo en un pipeline completo (cápsula 08).

La progresión es: build → optimización → distribución → trazabilidad → seguridad → proyecto.

Duración estimada del módulo: 60-90 minutos.


Lo que viene del Módulo 4

Tu pipeline actual (después del Módulo 4) maneja secrets de forma segura:

name: CI Pipeline
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  quality-gate:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"
      - run: pip install -r requirements-dev.txt
      - name: Lint
        run: ruff check src/ tests/
      - name: Type check
        run: mypy src/ --ignore-missing-imports
      - name: Tests
        run: pytest tests/ -v --tb=short
      - name: Prompt regression
        run: python scripts/evaluate_prompts.py
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
      - name: Cost estimation
        run: python scripts/estimate_costs.py

Funcional. Tests automatizados, checks AI-specific, secrets seguros. Pero falta un paso crucial: la imagen Docker. Tu código pasa todos los checks, ¿pero se buildea correctamente en Docker? ¿La imagen resultante es segura? ¿Está disponible en un registry para deployment?

Lo que este módulo agrega

# Lo que agregas DESPUÉS del quality gate
  docker:
    runs-on: ubuntu-latest
    needs: quality-gate
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:sha-${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Un job nuevo: build, cache, tag, push. Automático. Trazable. Rápido.


Conexión con el proyecto de la guía

Proyecto de este módulo: Docker CI Pipeline

El mini-proyecto construye un pipeline completo:

  1. Build — Construye la imagen con docker/build-push-action
  2. Cache — GitHub Cache backend para builds rápidos
  3. Tag — SHA del commit + semver para releases
  4. Scan — trivy para detectar vulnerabilidades
  5. Push — A GHCR con autenticación vía GITHUB_TOKEN
Push / PR
    ↓
┌──────────────────────────────────────┐
│  Job: Docker CI Pipeline             │
│                                      │
│  1. Checkout + Setup Buildx          │
│  2. Login to GHCR                    │
│  3. Build image (con cache)          │
│  4. Tag: sha-abc1234 + v1.2.3        │
│  5. Scan con trivy                   │
│  6. Push to GHCR                     │
│                                      │
│  Si scan CRITICAL → ❌ Block push    │
│  Si scan clean → ✅ Push to registry │
└──────────────────────────────────────┘

Conexión con módulos posteriores

Módulo 4: Secrets management (tokens para registries)
    ↓
Módulo 5: Docker CI Pipeline (build + push imágenes)  ← ESTÁS AQUÍ
    ↓
Módulo 6: Deployment Pipelines (deploya la imagen al servidor)
    ↓
Módulo 8: Pipeline integrador (lint → test → build → push → deploy)

La imagen que este pipeline produce es la que el Módulo 6 deploya a staging y production. Sin una imagen en un registry, no hay nada que deployear.


Prerequisitos

  • Módulos 1-4 completados: Workflows, testing, AI checks, secrets management
  • Docker fundamentals (guía #15): Dockerfile, docker build, multi-stage builds, docker-compose
  • Cuenta de GitHub: Para GHCR (incluido gratuitamente)
  • Python intermedio: Funciones, clases, REST APIs

Si no tienes estos prerequisitos

Te faltaRecurso recomendado
Docker basicsDocker Essentials Guide (#15, NIEVA)
GitHub ActionsMódulos 1-2 de esta guía
Secrets en CIMódulo 4 de esta guía

Setup técnico

Estructura de archivos del módulo

tu-proyecto-ai/
├── .github/
│   └── workflows/
│       ├── ci.yml              # Quality gate (Módulos 1-4)
│       └── docker.yml          # Docker CI Pipeline (este módulo)
├── Dockerfile                  # Tu Dockerfile existente
├── .dockerignore               # Excluir archivos innecesarios
├── src/
│   └── ai_app/
│       ├── __init__.py
│       ├── main.py             # FastAPI app
│       └── chain.py            # LangChain pipeline
├── tests/
│   └── test_chain.py
├── requirements.txt
└── requirements-dev.txt

Verificación rápida

# Verifica que Docker funciona
docker --version
# Output esperado: Docker version 24.x o superior

# Verifica que tu Dockerfile construye
docker build -t test:local .
# Output esperado: build exitoso

# Verifica que tienes un repo en GitHub
git remote -v
# Output esperado: origin  https://github.com/tu-user/tu-repo.git

Si los tres comandos funcionan, estás listo para el módulo.


Qué NO cubre este módulo

  • Docker basics: Cómo escribir un Dockerfile, multi-stage builds, docker-compose — eso es la guía #15
  • Deployment: Cómo deployear la imagen a un servidor — eso es el Módulo 6
  • Kubernetes: Orquestación de contenedores — eso es la guía #17
  • Docker en desarrollo local: docker-compose para desarrollo — guía #15
  • Custom registries: AWS ECR, Google Artifact Registry — mencionados pero no configurados

Analogía: La línea de producción automatizada

Imagina una fábrica de automóviles. En un taller artesanal, cada mecánico arma el auto completo a mano: instala el motor, pinta la carrocería, verifica que todo funcione, y lo estaciona en el lote. Es lento, inconsistente, y depende de quién lo haga.

En una fábrica moderna, la línea de producción automatiza todo: el chasis entra, se suelda automáticamente, se pinta con robots, pasa por control de calidad, y sale al lote — cada auto igual al anterior, trazable por número de serie, inspeccionado por sensores.

Tu Docker build manual es el taller artesanal. Docker en CI/CD es la línea de producción:

  • Build automático = la soldadura robótica (siempre consistente)
  • Cache de layers = piezas pre-fabricadas (no rehacer lo que no cambió)
  • Tags con SHA = número de serie (sabes exactamente qué es cada unidad)
  • Trivy scanning = control de calidad (detecta defectos antes de la entrega)
  • Push al registry = estacionamiento del lote (disponible para el comprador)

El cambio de mentalidad

Antes de este módulo

Developer: "Terminé el feature de embeddings"
Developer: *abre terminal, corre docker build*
Developer: *espera 4 minutos*
Developer: *tagea manualmente como v1.3.0*
Developer: *corre docker push*
Developer: *avisa al equipo por Slack*
Ops: *descarga la imagen, verifica que funciona*
Ops: *deploya manualmente*

Tiempo total: 15-30 minutos
Confianza: "debería funcionar"
Trazabilidad: "creo que es la versión de hoy"

Después de este módulo

Developer: "Terminé el feature de embeddings"
Developer: *git push*

GitHub Actions (automático, 2 minutos):
  ✅ Tests pasan
  ✅ Imagen construida (cache → 45 segundos)
  ✅ Tag: sha-abc1234 + main + latest
  ✅ Scan: 0 vulnerabilidades CRITICAL
  ✅ Push a GHCR
  ✅ Summary en el workflow

Ops: *ve la imagen en Packages, sabe exactamente qué commit es*

Tiempo total: 2 minutos (automático)
Confianza: "tests pasaron, scan limpio"
Trazabilidad: "commit abc1234, 8 de marzo, 2:34 PM"

Esa es la diferencia entre Docker manual y Docker en CI/CD.


Test rápido de autoevaluación

Antes de empezar las cápsulas, verifica que tienes el contexto necesario:

  1. ¿Qué hace docker build -t myapp .? (guía #15)
  2. ¿Qué es un multi-stage build en Docker? (guía #15)
  3. ¿Cómo funcionan los secrets en GitHub Actions? (Módulo 4)
  4. ¿Qué es el GITHUB_TOKEN? (Módulo 4)
  5. ¿Qué es un artifact en GitHub Actions? (Módulo 2)

Si alguna pregunta te suena completamente nueva, revisa el módulo o guía correspondiente antes de continuar.


Evidencia de éxito

Al terminar este módulo, deberías poder:

  • Configurar un workflow que buildea imágenes Docker automáticamente en cada push
  • Implementar caching que reduce el build time de 5+ minutos a < 2 minutos
  • Pushear imágenes a GHCR usando GITHUB_TOKEN (sin configurar secrets adicionales)
  • Tagear imágenes con SHA del commit para trazabilidad total
  • Integrar trivy para escanear vulnerabilidades antes del push
  • Explicar cuándo multi-platform builds son necesarios y cuándo no
  • Construir el Docker CI Pipeline completo del proyecto

Si marcas todos los checks → estás listo para el Módulo 6.


Resumen

  • Docker build manual es el problema: Lento, inconsistente, sin trazabilidad, sin seguridad
  • Docker en CI lo automatiza: Build, tag, scan, push — en cada push, sin intervención
  • El cache es la primera prioridad: Sin cache, builds de 5-10 min; con cache, 1-2 min
  • Tags = trazabilidad: SHA del commit te dice exactamente qué código corre en producción
  • Security scanning = prevención: Detectar vulnerabilidades antes del push, no después del deploy
  • GHCR es el default natural: Autenticación con GITHUB_TOKEN, mismo ecosistema que Actions
  • La imagen que builds aquí es la que el Módulo 6 deploya a staging y production

Recursos adicionales

  1. docker/build-push-action — Action oficial para build y push de Docker images
  2. GitHub Container Registry Docs — Documentación de GHCR
  3. Docker Layer Caching in CI — Cómo funciona el cache de Docker
  4. trivy — Container Scanner — Escáner de vulnerabilidades para imágenes
  5. GitHub Actions — Publishing Docker Images — Guía oficial de GitHub
  6. Docker Essentials Guide (#15, NIEVA) — Prerequisito: fundamentos de Docker