Claude Code × AWS ECS/Fargate Guía Completa | Automatiza Despliegues Seguros
Despliega ECS/Fargate con Claude Code: Docker, ECR, task definitions, logs, IAM y errores reales.
ECS/Fargate es una buena opción cuando quieres ejecutar contenedores en AWS sin administrar instancias EC2. La parte difícil no es Docker. Lo difícil es conectar ECR, task definitions, roles IAM, Secrets Manager, load balancing, CloudWatch Logs, health checks y control de costos sin dejar un hueco operativo.
En esta guía usamos Claude Code como asistente de implementación, no como piloto automático. Una task definition es la especificación de arranque del contenedor, Fargate es el runtime serverless que lo ejecuta, el execution role permite que ECS descargue imágenes, lea secrets y escriba logs, y el task role es el permiso que usa tu aplicación al llamar APIs de AWS.
En una migración real de Masa, el primer fallo no fue Docker sino el health check. La app tardaba unos 40 segundos en arrancar y ECS empezó a comprobarla demasiado pronto. Claude Code escribe archivos rápido, pero debes explicarle el tiempo de arranque, la red privada, los logs, el rollback y qué permiso pertenece a cada rol.
Arquitectura objetivo
Developer
|
| docker build / push
v
Amazon ECR ----> Amazon ECS Service on AWS Fargate
|
| pulls secrets / writes logs
v
Secrets Manager CloudWatch Logs
^
|
Application Load Balancer -> /health -> Node.js container
Los ejemplos usan ap-northeast-1, una VPC existente y un target group de ALB existente. Si quieres crear red y balanceador con IaC, combina esta guía con Claude Code × AWS CloudFormation/CDK. Para el modelo de permisos, revisa primero Claude Code × AWS IAM.
Tres casos de uso reales
| Caso | Por qué encaja Fargate | Cuidado con |
|---|---|---|
| API REST de SaaS | Mantiene dos o más tasks detrás de ALB y permite despliegues rolling | Diseñar conexiones a DB y health checks antes |
| Backend de administración | Empieza pequeño sin parches EC2 ni AMI | desiredCount en 0 vuelve lenta la primera petición |
| Imagen compartida para API y batch | La misma imagen sirve para service y run-task | Limitar el task role al mínimo necesario |
Si el trabajo es corto y basado en eventos, Claude Code × AWS Lambda puede ser más simple. Elige Fargate para HTTP siempre activo, peticiones largas, paridad Docker o runtimes que no encajan bien en Lambda.
1. API mínima con health check
Primero crea una app que funcione localmente. /health lo miran ECS y ALB, así que debe ser ligero. Si una lentitud temporal de la base de datos devuelve 500, ECS puede reemplazar tasks sanas.
{
"scripts": {
"start": "node src/server.js"
},
"dependencies": {
"express": "^4.19.2"
}
}
// src/server.js
const express = require("express");
const app = express();
const port = Number(process.env.PORT || 3000);
app.get("/health", (_req, res) => {
res.status(200).json({
ok: true,
service: "myapp",
time: new Date().toISOString(),
});
});
app.get("/", (_req, res) => {
res.json({ message: "Hello from ECS Fargate" });
});
app.listen(port, "0.0.0.0", () => {
console.log(`myapp listening on ${port}`);
});
# Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["node", "src/server.js"]
Verifica curl -f http://localhost:3000/health antes de pasar a ECS. Al pedirlo a Claude Code, especifica que ALB y ECS usan la misma ruta, que la app escucha en 0.0.0.0 y que startPeriod protege el arranque.
2. Construir y enviar a ECR
Este script crea el repositorio si falta, inicia sesión, construye la imagen y la sube con una etiqueta versionada. No dependas solo de latest, porque complica rollback y auditoría.
set -euo pipefail
export AWS_REGION="ap-northeast-1"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export ECR_REPOSITORY="myapp"
export IMAGE_TAG="$(git rev-parse --short HEAD 2>/dev/null || date +%Y%m%d%H%M%S)"
export IMAGE_URI="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}"
aws ecr describe-repositories \
--repository-names "${ECR_REPOSITORY}" \
--region "${AWS_REGION}" >/dev/null 2>&1 || \
aws ecr create-repository \
--repository-name "${ECR_REPOSITORY}" \
--image-scanning-configuration scanOnPush=true \
--region "${AWS_REGION}"
aws ecr get-login-password --region "${AWS_REGION}" | \
docker login --username AWS --password-stdin \
"${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
docker build -t "${IMAGE_URI}" .
docker push "${IMAGE_URI}"
echo "Pushed ${IMAGE_URI}"
El flujo oficial de ECR usa get-login-password. En CI conviene usar GitHub Actions OIDC y credenciales temporales de AWS, no access keys de larga duración guardadas como secrets.
3. Registrar la task definition
Las tasks de Fargate usan red awsvpc. Si inyectas valores desde Secrets Manager, el execution role necesita secretsmanager:GetSecretValue; si el secret usa una KMS key administrada por ti, también necesita kms:Decrypt. El task role es para el código de la aplicación.
set -euo pipefail
export AWS_REGION="ap-northeast-1"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export IMAGE_URI="${IMAGE_URI:?Run the ECR push script first}"
export EXECUTION_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole"
export TASK_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/myapp-task-role"
export SECRET_ARN="arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:prod/myapp/DATABASE_URL"
aws logs create-log-group --log-group-name /ecs/myapp --region "${AWS_REGION}" 2>/dev/null || true
aws logs put-retention-policy --log-group-name /ecs/myapp --retention-in-days 30 --region "${AWS_REGION}"
cat > ecs-task-definition.json <<EOF
{
"family": "myapp-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "${EXECUTION_ROLE_ARN}",
"taskRoleArn": "${TASK_ROLE_ARN}",
"runtimePlatform": {
"cpuArchitecture": "X86_64",
"operatingSystemFamily": "LINUX"
},
"containerDefinitions": [
{
"name": "app",
"image": "${IMAGE_URI}",
"essential": true,
"portMappings": [
{ "containerPort": 3000, "hostPort": 3000, "protocol": "tcp" }
],
"environment": [
{ "name": "NODE_ENV", "value": "production" },
{ "name": "PORT", "value": "3000" }
],
"secrets": [
{ "name": "DATABASE_URL", "valueFrom": "${SECRET_ARN}" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "${AWS_REGION}",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "wget -qO- http://localhost:3000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
EOF
aws ecs register-task-definition \
--cli-input-json file://ecs-task-definition.json \
--region "${AWS_REGION}"
startPeriod parece menor, pero evita que ECS juzgue el contenedor antes de que la aplicación tenga tiempo razonable para arrancar.
4. Crear el servicio Fargate
El comando siguiente usa subnets privadas, un security group para la task y un target group de ALB ya existentes. El security group de la task debe aceptar el puerto 3000 solo desde el security group del ALB.
set -euo pipefail
export AWS_REGION="ap-northeast-1"
export CLUSTER_NAME="myapp-cluster"
export SERVICE_NAME="myapp-service"
export TASK_FAMILY="myapp-task"
export SUBNET_1="subnet-xxxxxxxx"
export SUBNET_2="subnet-yyyyyyyy"
export TASK_SECURITY_GROUP="sg-xxxxxxxx"
export TARGET_GROUP_ARN="arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp/abc123"
aws ecs create-cluster \
--cluster-name "${CLUSTER_NAME}" \
--region "${AWS_REGION}" >/dev/null
aws ecs create-service \
--cluster "${CLUSTER_NAME}" \
--service-name "${SERVICE_NAME}" \
--task-definition "${TASK_FAMILY}" \
--desired-count 2 \
--launch-type FARGATE \
--platform-version LATEST \
--health-check-grace-period-seconds 90 \
--network-configuration "awsvpcConfiguration={subnets=[${SUBNET_1},${SUBNET_2}],securityGroups=[${TASK_SECURITY_GROUP}],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=${TARGET_GROUP_ARN},containerName=app,containerPort=3000" \
--region "${AWS_REGION}"
aws ecs wait services-stable \
--cluster "${CLUSTER_NAME}" \
--services "${SERVICE_NAME}" \
--region "${AWS_REGION}"
Con subnets privadas y assignPublicIp=DISABLED, la task aún necesita camino hacia ECR, CloudWatch Logs y Secrets Manager. Usa NAT Gateway o VPC endpoints. NAT es cómodo, pero puede ser el costo dominante de un entorno pequeño.
5. Revisar CloudWatch Logs y eventos ECS
Depurar ECS suele requerir eventos del service, razón de tasks detenidas y logs de la app. Dale los tres a Claude Code cuando pidas diagnóstico.
export AWS_REGION="ap-northeast-1"
export CLUSTER_NAME="myapp-cluster"
export SERVICE_NAME="myapp-service"
aws ecs describe-services \
--cluster "${CLUSTER_NAME}" \
--services "${SERVICE_NAME}" \
--query "services[0].events[0:5].[createdAt,message]" \
--output table \
--region "${AWS_REGION}"
aws ecs list-tasks \
--cluster "${CLUSTER_NAME}" \
--service-name "${SERVICE_NAME}" \
--desired-status STOPPED \
--region "${AWS_REGION}"
aws logs tail /ecs/myapp \
--follow \
--since 10m \
--region "${AWS_REGION}"
Los fallos frecuentes son: no se puede descargar la imagen, acceso denegado al secret, el ALB no llega al security group de la task o la app escucha en localhost en vez de 0.0.0.0.
Plantilla para pedir la implementación a Claude Code
Crea una implementación de despliegue AWS ECS/Fargate para una API Node.js.
Contexto:
- Region: ap-northeast-1
- ECR repository: myapp
- Container port: 3000
- Health endpoint: /health
- ECS launch type: FARGATE
- Network mode: awsvpc
- Desired count: 2
- Task CPU/memory: 512 / 1024
- Secret: inyectar DATABASE_URL desde Secrets Manager
- Logs: CloudWatch Logs /ecs/myapp, retention 30 days
Entregables:
1. Dockerfile de producción
2. Script bash para push a ECR
3. Script bash para registrar la ECS task definition
4. Script bash para crear el Fargate service
5. Script bash para inspeccionar CloudWatch Logs
6. Explicación que separe execution role y task role
Restricciones:
- Sin pseudocódigo. Los comandos deben ejecutarse con AWS CLI tras completar variables.
- No hardcodear secrets.
- No exponer la task directamente en una public subnet.
- Terminar con puntos de documentación oficial AWS que debo verificar.
Comprobaciones con documentación oficial AWS
- Guía de AWS Fargate: restricciones y comportamiento de plataforma.
- Parámetros de task definition:
awsvpc, CPU/memory y health check. - Task execution IAM role: permisos para ECR pull, logs y Secrets Manager.
- Enviar logs de ECS a CloudWatch: configuración del driver
awslogs. - Precios de AWS Fargate: varían por región, vCPU, memoria, ephemeral storage adicional y tiempo de ejecución.
- Servicios AWS por región: confirma que la región elegida soporta todo el diseño.
Errores concretos
El primero es mezclar execution role y task role. ECR, logging y lectura de secrets van en execution role. DynamoDB, S3, SQS y permisos de la app van en task role.
El segundo es tener el secret en otra región. La task de ECS, Secrets Manager y KMS key deben coincidir. En multi-región, usa el ARN del secret como variable de despliegue por entorno.
El tercero es el costo. Fargate se cobra por uso, pero ALB, NAT Gateway, CloudWatch Logs y almacenamiento ECR también cuentan. En pruebas, define retención de logs, detén services no usados y revisa NAT.
El cuarto es un health check demasiado estricto. Mantén /health ligero y mueve comprobaciones profundas a /ready o a smoke tests.
El quinto es la arquitectura de imagen. Si construyes ARM64 en Apple Silicon y la task define X86_64, fallará el arranque. Usa docker buildx build --platform linux/amd64 o alinea runtimePlatform.
CTA y siguiente paso
Si trabajas solo, copia estos scripts en una API pequeña y usa la chuleta gratuita de Claude Code para mejorar tus prompts. Si tu equipo necesita diseñar ECS, IAM, CI/CD, observabilidad y rollback juntos, empieza por formación y consultoría Claude Code. Para prompts y material de revisión reutilizable, revisa productos ClaudeCodeLab.
Resumen
ECS/Fargate no es solo un lugar para subir imágenes Docker. Es un diseño combinado de IAM, red, secrets, logs, health checks y costo. Claude Code rinde mejor cuando primero recibe las reglas operativas y después se le piden archivos ejecutables.
Al probar este flujo, la mayor mejora vino del prompt, no del Dockerfile. Pedir explícitamente separar execution role y task role, incluir comandos de CloudWatch Logs y configurar health check grace period evitó repetir el mismo fallo. La primera ejecución todavía reveló un permiso faltante para secrets, pero al pasar eventos ECS y logs a Claude Code, devolvió la corrección IAM y los pasos de redeploy en una sola vuelta.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.