Use Cases

Claude Code × AWS ECS/Fargate Guía Completa | Automatiza los Despliegues de Contenedores

Automatiza despliegues en AWS ECS/Fargate con Claude Code. Desde definiciones de tareas y configuración de servicios hasta despliegues Blue/Green e infraestructura CDK — basado en la experiencia real de Masa.

“Quiero ejecutar contenedores en AWS, pero la configuración de ECS es demasiado compleja” — este es el obstáculo que encuentran muchos desarrolladores al principio. Definiciones de tareas, servicios, clústeres, balanceadores de carga, escalado automático… hay tantas configuraciones que no sabes por dónde empezar.

He estado construyendo entornos de contenedores serverless con ECS/Fargate en el trabajo, y desde que empecé a usar Claude Code para describir mi arquitectura y generar todo, desde definiciones de tareas hasta código CDK, los despliegues se han vuelto mucho más fáciles. Este artículo explica los pasos prácticos.


Fundamentos de ECS/Fargate en 3 Minutos

Clúster:           La "caja" donde se ejecutan los contenedores ECS
Definición de tarea: Especificación del contenedor (imagen, CPU, memoria, variables de entorno)
Servicio:          Define cuántas tareas ejecutar (incluye escalado automático)
Fargate:           Motor de ejecución de contenedores serverless — sin gestión de servidores

A diferencia de EC2, Fargate no requiere aprovisionamiento ni gestión de parches del servidor. Puedes concentrarte completamente en la configuración del contenedor.


Paso 1: Generar Automáticamente una Definición de Tarea

claude -p "
Genera una definición de tarea ECS en JSON con los siguientes requisitos:

- App: Node.js API (puerto 3000)
- CPU: 512 (0.5 vCPU)
- Memoria: 1024 MB
- Variables de entorno: DATABASE_URL (obtenida de Secrets Manager), NODE_ENV=production
- Registro: CloudWatch Logs (grupo de logs /ecs/myapp)
- Health check: endpoint /health, intervalo de 30 segundos
- Imagen de contenedor: 123456789.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest
- Rol de ejecución: ecsTaskExecutionRole
- Rol de tarea: myapp-task-role (acceso a DynamoDB y S3)
"

Definición de tarea generada:

{
  "family": "myapp-task",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789:role/myapp-task-role",
  "containerDefinitions": [
    {
      "name": "myapp",
      "image": "123456789.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest",
      "portMappings": [{ "containerPort": 3000, "protocol": "tcp" }],
      "environment": [{ "name": "NODE_ENV", "value": "production" }],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789:secret:prod/database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/myapp",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      }
    }
  ]
}

Paso 2: Construir Infraestructura ECS/Fargate con CDK

Además de la definición de tarea, generamos el clúster, el servicio y el ALB de una sola vez con CDK.

claude -p "
Implementa el siguiente entorno ECS/Fargate en CDK TypeScript en lib/ecs-stack.ts:

- VPC: Importar VPC existente (obtener vpcId de variable de entorno)
- Clúster ECS: Solo Fargate
- Definición de tarea: Mismas especificaciones que las generadas arriba
- ALB: HTTPS (obtener ARN del certificado ACM de variable de entorno), HTTP redirige con 301
- Servicio: Escalado automático con mínimo 2, máximo 10 tareas
  - Escalar al 70% de utilización de CPU
  - Escalado de seguimiento de objetivos
- Soporte para despliegue Blue/Green (integración con CodeDeploy)
- Mostrar el estado del servicio en un panel de CloudWatch
"
// lib/ecs-stack.ts
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecsp from "aws-cdk-lib/aws-ecs-patterns";
import * as acm from "aws-cdk-lib/aws-certificatemanager";

export class EcsStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Importar VPC existente
    const vpc = ec2.Vpc.fromLookup(this, "Vpc", {
      vpcId: process.env.VPC_ID!,
    });

    // Clúster ECS
    const cluster = new ecs.Cluster(this, "Cluster", {
      vpc,
      clusterName: "myapp-cluster",
      containerInsights: true,  // Habilitar CloudWatch Container Insights
    });

    // Certificado ACM
    const certificate = acm.Certificate.fromCertificateArn(
      this, "Certificate",
      process.env.CERTIFICATE_ARN!
    );

    // Definición de tarea
    const taskDef = new ecs.FargateTaskDefinition(this, "TaskDef", {
      cpu: 512,
      memoryLimitMiB: 1024,
    });

    const container = taskDef.addContainer("app", {
      image: ecs.ContainerImage.fromEcrRepository(
        ecr.Repository.fromRepositoryName(this, "Repo", "myapp")
      ),
      environment: { NODE_ENV: "production" },
      logging: ecs.LogDrivers.awsLogs({ streamPrefix: "ecs" }),
      healthCheck: {
        command: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        interval: cdk.Duration.seconds(30),
        timeout: cdk.Duration.seconds(5),
        retries: 3,
        startPeriod: cdk.Duration.seconds(60),
      },
    });
    container.addPortMappings({ containerPort: 3000 });

    // ALB + Servicio Fargate (conciso usando patrones)
    const service = new ecsp.ApplicationLoadBalancedFargateService(
      this, "Service", {
        cluster,
        taskDefinition: taskDef,
        desiredCount: 2,
        certificate,
        redirectHTTP: true,
        publicLoadBalancer: true,
      }
    );

    // Configuración de escalado automático
    const scaling = service.service.autoScaleTaskCount({
      minCapacity: 2,
      maxCapacity: 10,
    });
    scaling.scaleOnCpuUtilization("CpuScaling", {
      targetUtilizationPercent: 70,
      scaleInCooldown: cdk.Duration.seconds(60),
      scaleOutCooldown: cdk.Duration.seconds(60),
    });

    // Salida
    new cdk.CfnOutput(this, "LoadBalancerDns", {
      value: service.loadBalancer.loadBalancerDnsName,
    });
  }
}

Paso 3: Construir un Pipeline CI/CD con GitHub Actions

claude -p "
Crea el siguiente pipeline CI/CD con GitHub Actions:

1. Activar al hacer push a la rama main
2. Construir imagen Docker y enviarla a ECR
3. Actualizar el tag de imagen en la definición de tarea ECS al nuevo SHA
4. Desplegar la nueva definición de tarea al servicio ECS
5. Notificar a Slack al completar el despliegue

Entorno: ap-northeast-1, nombre del repositorio ECR: myapp
"
name: Deploy to ECS

on:
  push:
    branches: [main]

env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY: myapp
  ECS_CLUSTER: myapp-cluster
  ECS_SERVICE: myapp-service
  CONTAINER_NAME: app

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, and push image to ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - name: Download task definition
        run: |
          aws ecs describe-task-definition --task-definition myapp-task \
            --query taskDefinition > task-definition.json

      - name: Update ECS task definition with new image
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      - name: Deploy to ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

      - name: Notify Slack on success
        if: success()
        uses: slackapi/slack-github-action@v1
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK }}
          payload: |
            {"text": "✅ Deployed to ECS: ${{ github.sha }}"}

Paso 4: Configurar el Despliegue Blue/Green

claude -p "
Genera código CDK y un archivo AppSpec para configurar el despliegue Blue/Green (CodeDeploy) para un servicio ECS.
Condición de rollback: revertir automáticamente si una alarma de CloudWatch se activa dentro de los 5 minutos posteriores al despliegue.
"
# appspec.yaml (para CodeDeploy)
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: app
          ContainerPort: 3000
        PlatformVersion: LATEST
Hooks:
  - BeforeAllowTraffic: "arn:aws:lambda:ap-northeast-1:123456789:function:health-check"
  - AfterAllowTraffic: "arn:aws:lambda:ap-northeast-1:123456789:function:smoke-test"

Los 5 Errores Más Comunes

1. Olvidar startPeriod en el health check

Los contenedores Fargate ejecutan health checks inmediatamente al arrancar, pero si tu aplicación tarda en iniciar, se marca como UNHEALTHY al instante. Configura startPeriod: 60 (segundos) para dar tiempo a tu app a inicializarse.

2. Confundir el rol de tarea con el rol de ejecución

executionRole (ecsTaskExecutionRole): Permisos necesarios para iniciar el contenedor
  → Extraer imagen de ECR, escribir en CloudWatch Logs

taskRole (myapp-task-role): Permisos que usa la aplicación
  → Acceso a DynamoDB, S3, SQS, etc.

Es muy fácil añadir permisos al rol equivocado.

3. Configuración del grupo de seguridad en modo de red awsvpc

Fargate requiere el modo de red awsvpc. Si olvidas restringir el grupo de seguridad del contenedor para permitir solo tráfico del ALB, el contenedor quedará expuesto directamente a internet.

4. El rol de ejecución es necesario para obtener valores de Secrets Manager

Para obtener valores de Secrets Manager mediante secrets, el executionRole debe tener el permiso secretsmanager:GetSecretValue. La falta de este permiso es una razón muy común por la que los contenedores no arrancan.

5. El problema de desired_count: 0 durante el despliegue

Un servicio con un mínimo de 0 en el escalado automático puede apagar todos los contenedores por la noche, causando arranques lentos por la mañana. Establece el mínimo en al menos 2 en producción.


Resumen

TareaContribución de Claude Code
Generación de definición de tareaJSON completo solo con los requisitos
Implementación CDKClúster, servicio, ALB, escalado automático generados juntos
Configuración CI/CDGeneración de flujo de trabajo de GitHub Actions
Despliegue Blue/GreenAppSpec y configuración de CodeDeploy generados automáticamente
Resolución de problemasIdentifica causa raíz y soluciones desde logs de error

ECS tiene muchos elementos de configuración, pero diciendo a Claude Code “quiero este tipo de configuración”, obtienes una configuración completa siguiendo las mejores prácticas. Empezar con CDK es el enfoque más fácil y recomendado.

Artículos Relacionados

Referencias

#claude-code #aws #ecs #fargate #container #devops

Lleva tu flujo con Claude Code al siguiente nivel

50 plantillas de prompts probadas en producción, listas para copiar y pegar en Claude Code.

Gratis

PDF gratuito: Hoja de trucos de Claude Code en 5 minutos

Solo deja tu correo y te enviaremos al instante la hoja de trucos en una página A4.

Cuidamos tus datos personales y nunca enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero apasionado por Claude Code. Dirige claudecode-lab.com, un medio tecnológico en 10 idiomas con más de 2.000 páginas.