Los despliegues manuales no escalan. Cada paso manual es una fuente potencial de errores y, cuanto más rápido entrega tu equipo, más se acumulan esos errores. Un pipeline de CI/CD construido sobre Docker y AWS elimina el trabajo manual del camino crítico: el código se compila, se prueba, se empaqueta como contenedor y se despliega a producción sin intervención humana en cada etapa.

Este post recorre un pipeline completo usando AWS CodePipeline, AWS CodeBuild, Amazon ECR y Amazon ECS.

¿Por qué Docker y AWS para CI/CD?

Los contenedores Docker ofrecen un comportamiento consistente entre entornos. Lo que corre localmente corre de la misma manera en CI y en producción. Los servicios de AWS gestionan el escalado y la seguridad a nivel de infraestructura, y CodePipeline une las etapas en un único flujo de trabajo automatizado.

Requisitos previos

  • Una cuenta de AWS con privilegios administrativos.
  • Conocimiento básico de Docker y conceptos de contenedorización.
  • Familiaridad con ECS y ECR.
  • Una aplicación de ejemplo (esta guía usa Node.js).

Arquitectura del Pipeline

El pipeline tiene tres etapas:

  1. Source: El código se envía a un sistema de control de versiones (AWS CodeCommit o GitHub).
  2. Build: CodeBuild construye la imagen Docker y ejecuta las pruebas.
  3. Deploy: La imagen se sube a ECR y ECS la despliega en un clúster.

Paso 1: Configuración del Repositorio de Origen

Para una integración fluida con AWS, AWS CodeCommit es la opción más directa.

  1. Navega a la consola de AWS CodeCommit.

  2. Crea un nuevo repositorio llamado my-app-repo.

  3. Clona el repositorio en tu máquina local:

    git clone https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-app-repo
  4. Agrega el código de tu aplicación y haz push:

    git add .
    git commit -m "Initial commit"
    git push origin master

Paso 2: Contenedorizando la Aplicación

Crea un Dockerfile para definir cómo se empaqueta tu aplicación.

Dockerfile de ejemplo para Node.js

# Use Node.js LTS version as the base image
FROM node:18-alpine

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install --production

# Copy the rest of the application code
COPY . .

# Expose the application port
EXPOSE 3000

# Start the application
CMD ["node", "app.js"]

Algunos puntos a tener en cuenta: usa una imagen base mínima para mantener la imagen pequeña, copia solo los archivos necesarios para optimizar el caché de build y ejecuta los contenedores como usuario no root por seguridad.

Paso 3: Especificación de Build para CodeBuild

AWS CodeBuild necesita un archivo buildspec.yml en la raíz de tu repositorio.

buildspec.yml de ejemplo

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging into Amazon ECR...
      - $(aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com)
      - REPOSITORY_URI=<AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/my-app-repo
  build:
    commands:
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
  post_build:
    commands:
      - echo Pushing the Docker image...
      - docker push $REPOSITORY_URI:latest
artifacts:
  files:
    - '**/*'

Reemplaza <AWS_ACCOUNT_ID> con tu ID real de cuenta de AWS.

Paso 4: Configuración de Amazon ECR

Amazon ECR almacena tus imágenes Docker.

  1. Navega a la consola de Amazon ECR.
  2. Crea un nuevo repositorio llamado my-app-repo.
  3. Anota el URI del repositorio para usarlo en buildspec.yml.

Paso 5: Configuración de AWS CodeBuild

  1. Navega a la consola de AWS CodeBuild.
  2. Crea un nuevo proyecto de build llamado my-app-build.
  3. Configura:
    • Source Provider: AWS CodeCommit.
    • Repository: my-app-repo.
    • Environment: runtime Ubuntu Standard, Docker habilitado, modo Privileged activado (requerido para builds Docker).
    • Buildspec: Usa el buildspec.yml del código fuente.

Paso 6: Configuración de AWS CodePipeline

  1. Navega a la consola de AWS CodePipeline.
  2. Crea un nuevo pipeline llamado my-app-pipeline.
  3. Etapa Source: AWS CodeCommit, my-app-repo, rama master.
  4. Etapa Build: AWS CodeBuild, proyecto my-app-build.
  5. Etapa Deploy: AWS ECS, my-ecs-cluster, my-app-service.

Paso 7: Configuración de Amazon ECS

Crear un Clúster ECS

  1. Navega a la consola de Amazon ECS.
  2. Crea un clúster llamado my-ecs-cluster. Usa Fargate si deseas evitar la gestión de instancias EC2.

Definir una Task Definition

  1. Ve a Task Definitions.
  2. Crea my-app-task con:
    • Task Role: IAM role con los permisos necesarios.
    • Network Mode: awsvpc para Fargate.
    • Container: nombre my-app-container, imagen desde ECR, puerto 3000 mapeado al host 80.

Crear un Service

  1. Ve a Services y crea my-app-service.
  2. Launch type: Fargate. Task definition: my-app-task. Habilita rolling updates.

Paso 8: Probando el Pipeline

  1. Realiza un cambio en el código localmente.

  2. Haz push a master:

    git add .
    git commit -m "Updated application"
    git push origin master
  3. Observa el progreso del pipeline en la consola de CodePipeline.

  4. Verifica el nuevo despliegue en la consola de ECS.

Mejorando el Pipeline

Pruebas Automatizadas

Agrega comandos de prueba al buildspec.yml antes del paso de build de Docker:

phases:
  build:
    commands:
      - echo Running unit tests...
      - npm test
      - echo Building Docker image...
      - docker build -t $REPOSITORY_URI:latest .

Infraestructura como Código

Define todos los recursos de AWS en plantillas de CloudFormation o Terraform. Mantener el código de infraestructura en control de versiones junto al código de la aplicación hace que la configuración del entorno sea reproducible y los cambios sean auditables.

Gestión de Dependencias con CodeArtifact

AWS CodeArtifact almacena y sirve paquetes npm de forma privada. Esto es útil para compartir librerías internas entre servicios sin publicarlas en el registro público de npm.

Buenas Prácticas de Seguridad

  • Asigna IAM roles con privilegios mínimos a cada componente de CodeBuild y ECS.
  • Almacena los secretos en AWS Secrets Manager, no en variables de entorno ni en el código fuente.
  • Configura VPCs, subnets y security groups para restringir el acceso a la red.
  • Habilita CloudTrail y CloudWatch para auditoría y logging.

Monitoreo y Logging

  • CloudWatch Logs recopila la salida de las tasks de ECS.
  • AWS X-Ray rastrea las solicitudes a través de la aplicación desplegada.
  • Configura Amazon SNS para enviar notificaciones cuando fallen las etapas del pipeline.

Optimización de Costos

  • Usa capacidad Spot de ECS para workloads que toleren interrupciones.
  • Ajusta correctamente la CPU y memoria de las tasks basándote en las métricas reales de CloudWatch en lugar de estimaciones.
  • Revisa y elimina regularmente las imágenes ECR no utilizadas y las task definitions de ECS.

Conclusión

Una vez que este pipeline está en marcha, desplegar un cambio requiere solo un git push. CodePipeline se encarga del resto: construye la imagen, ejecuta las pruebas, hace push a ECR y despliega la nueva versión en ECS. El costo inicial de configuración es real, pero se amortiza rápidamente en tiempo ahorrado e incidentes de producción evitados.