Deploy de Django en AWS (ECS Fargate + ECR + ALB + CloudFront + EventBridge) — Guía paso a paso

Deploy de Django en AWS (ECS Fargate + ECR + ALB + CloudFront + EventBridge) — Guía paso a paso

Esta guía documenta el flujo de despliegue para un backend Django corriendo en AWS ECS Fargate, con imágenes en ECR, expuesto por un Application Load Balancer (ALB), y opcionalmente acelerado/protegido por CloudFront. Además, tareas programadas (como crawlers o jobs diarios) corren mediante EventBridge.


Arquitectura (qué está pasando)

  • ECR (Elastic Container Registry): aquí se guardan las imágenes Docker versionadas (tags únicos).
  • ECS Fargate: corre tu contenedor sin administrar servidores. Tu servicio mantiene 1+ tasks “vivas”.
  • Task Definition (example-web): es la “receta” del contenedor (imagen, env vars, puertos, CPU/RAM, logs, etc.). Cada cambio crea una revisión nueva (ej: example-web:3).
  • Service (example-backend-service-s2zz1bm3): mantiene la cantidad deseada de tasks y aplica despliegues (rolling deploy).
  • ALB (Application Load Balancer): recibe tráfico HTTP/HTTPS y lo manda a tus tasks (Target Group → container:8000).
  • CloudFront: puede ir delante del ALB para cache, TLS, WAF, y mejor latencia. (En logs verás requests a /admin y /static).
  • CloudWatch Logs: logs del contenedor (entrypoint, gunicorn, etc.).
  • EventBridge (Schedules): ejecuta tareas programadas (por ejemplo un crawler diario) disparando un task en ECS.

Los 3 comandos importantes del deploy

  1. Build + Push a ECR (tag único)
  2. Registrar nueva revisión de Task Definition (apuntando al nuevo tag)
  3. Actualizar el Service para usar la nueva revisión + force new deployment

0) Pre-requisitos

  • Docker corriendo localmente
  • AWS CLI configurado (profile: example)
  • Repositorio con Dockerfile + entrypoint.sh funcional
  • Permisos para: ECR push, ECS register task definition, ECS update-service

1) Build + Push con tag único (ECR)

Este paso empaqueta tu código en una imagen Docker, la etiqueta con un tag único (por ejemplo el commit corto), y la sube a ECR.

export AWS_REGION=us-east-2
export AWS_PROFILE=example
export AWS_ACCOUNT_ID=XXXXXXXX
export ECR_REPO=example-backend
export ECR_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO
export IMAGE_TAG=$(git rev-parse --short HEAD)

# Login a ECR
aws ecr get-login-password --region $AWS_REGION --profile $AWS_PROFILE \
| docker login --username AWS --password-stdin \
  $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

# Build, tag y push
docker build -t $ECR_REPO:$IMAGE_TAG .
docker tag $ECR_REPO:$IMAGE_TAG $ECR_URI:$IMAGE_TAG
docker push $ECR_URI:$IMAGE_TAG

Resultado esperado: verás “Pushed” y un digest: sha256:....


2) Actualiza la Task Definition (example-web) para usar ese tag

Idea clave: aunque ya subiste la imagen a ECR, ECS NO la usa hasta que una Task Definition revision apunte a ese tag.

Opción A (recomendada): AWS Console

  1. Ve a ECS → Task definitions → example-web
  2. Click en Create new revision
  3. En Container: web cambia Image URI de:


    ...:latest


    a:


    030512689265.dkr.ecr.us-east-2.amazonaws.com/example-backend:$IMAGE_TAG
  4. Click Create

✅ Resultado: se crea una nueva revisión, por ejemplo example-web:4.

Opción B: CLI (equivalente, avanzado)

Este método descarga la task definition actual, reemplaza el campo image del contenedor web, y registra una nueva revisión.

# Requiere jq
export AWS_REGION=us-east-2
export FAMILY=example-web
export NEW_IMAGE="030512689265.dkr.ecr.us-east-2.amazonaws.com/example-backend:$IMAGE_TAG"

# Toma la última definición y prepara JSON registrable
aws ecs describe-task-definition \
  --task-definition $FAMILY \
  --region $AWS_REGION \
| jq --arg IMG "$NEW_IMAGE" '
  .taskDefinition
  | del(.taskDefinitionArn,.revision,.status,.requiresAttributes,.compatibilities,.registeredAt,.registeredBy)
  | .containerDefinitions |= map(if .name=="web" then .image=$IMG else . end)
' > td.json

# Registrar nueva revisión
aws ecs register-task-definition \
  --cli-input-json file://td.json \
  --region $AWS_REGION

3) Update Service a la nueva revisión (deploy real)

Ahora le dices al Service que use la nueva revisión de task definition y haga rollout.

Opción A (AWS Console)

  1. ECS → Clusters → example-cluster
  2. Tab Services
  3. Click example-backend-service-s2zz1bm3
  4. Click Update
  5. En Task definition revision selecciona la nueva (ej: example-web:4)
  6. Marca ✅ Force new deployment
  7. Click Update service

Opción B (CLI equivalente)

aws ecs update-service \
  --cluster example-cluster \
  --service example-backend-service-s2zz1bm3 \
  --task-definition example-web \
  --force-new-deployment \
  --region us-east-2

4) Confirmar que quedó (checks rápidos)

En AWS Console

  1. ECS → Clusters → example-cluster → Services
  2. Abre example-backend-service-s2zz1bm3
  3. En Deployments valida:


    Task definition = example-web:<NUEVA_REVISION>


    y que haya Running = Desired

Validación en CloudWatch Logs

  • Busca que el log muestre algo como:


    Collecting static... y xxx static files copied
  • Y que no haya errores en el entrypoint (ej. command not found)

Validación HTTP (lo que te confirma todo)

  • /admin carga con estilos
  • /static/… responde 200 (no 404)
  • /healthz responde 200 (ALB health checks)

Notas prácticas

  • Evita latest en producción: usa tags únicos (git rev-parse --short HEAD o timestamp).
  • Si subiste imagen pero no se reflejó: casi siempre es porque no creaste una nueva revisión de task definition apuntando a ese tag.
  • Jobs diarios (EventBridge): se usan para correr tareas tipo crawler sin tocar el servicio web (ej: “run-task” programado).
  • CloudFront: útil para TLS, WAF, caching y latencia; típicamente apunta al ALB como origen.