Skip to content

📙 Clase 11 — CI/CD y despliegue en AWS ​

Python para Backend · 2026-09-10 · Carpeta: 02-Ejercicios/Clase-11 ⬅️ Volver al índice de clases

🎯 Qué aprendí (según temario — se amplía a medida que avanza la clase) ​

  • ✅ Automatización de pruebas con pytest dentro del pipeline de CI/CD: qué pasa si el test pasa (el pipeline sigue hacia Docker build y ECR) vs. si falla (el pipeline se detiene ahí mismo, sin build ni despliegue).
  • ✅ Amazon ECR como registro de imágenes Docker en la nube, y la buena práctica de etiquetar cada imagen con el SHA del commit en vez de solo latest, para trazabilidad completa (saber exactamente qué código corre en producción).
  • ✅ Amazon ECS Fargate: cómo se ejecuta un contenedor en AWS sin gestionar servidores (Cluster → Task Definition → Service → Fargate), y la diferencia entre ECR (almacena imágenes) y ECS (las ejecuta y orquesta).
  • ✅ Autenticación segura entre GitHub Actions y AWS con OIDC, en vez de guardar credenciales de AWS de larga duración como secrets del repositorio.
  • ✅ El pipeline de CI/CD completo de punta a punta, aplicado al proyecto final OrderFlow: git push → checkout + pytest → docker build + push a ECR → actualizar Task Definition + Service en ECS.
  • 🔜 Introducción a CI/CD (conceptos generales)
  • 🔜 GitHub Actions (estructura completa del workflow: triggers, jobs, steps)

📖 PARTE TEÓRICA ​

📚 1. Definiciones clave ​

TérminoQué esSe profundiza en
CI/CD (Continuous Integration / Continuous Deployment)Práctica de automatizar, en un pipeline, los pasos que van desde que se sube código (git push) hasta que queda corriendo en producción: instalar dependencias, correr pruebas, construir la imagen y desplegarla.sección 2
PipelineLa secuencia de steps (pasos) que ejecuta el sistema de CI/CD, uno detrás de otro. Si un step falla, los siguientes no se ejecutan.sección 2
pytestFramework de testing de Python. Detecta automáticamente las funciones que empiezan con test_ y las ejecuta como pruebas independientes.sección 2
assertInstrucción de Python que verifica que una condición sea verdadera; si es falsa, lanza un error y la prueba se marca como fallida — así es como pytest sabe si un test pasó o no.sección 2
Endpoint de salud (/health/live)Ruta que expone un servicio solo para confirmar que está vivo y respondiendo, sin lógica de negocio. Es el ejemplo típico para el primer test automatizado de un pipeline.sección 2
Docker buildPaso del pipeline que construye la imagen a partir del Dockerfile (ver Clase 10, sección 3).sección 3
Docker tagComando que le da un nombre/etiqueta a una imagen ya construida, antes de subirla a un registro (p. ej. docker tag orders-service:latest <repo-ecr>:abc75d).sección 3
Amazon ECR (Elastic Container Registry)Registro de imágenes Docker de AWS — el equivalente de Docker Hub, pero privado y dentro de la infraestructura de AWS. Ahí se sube (push) la imagen que luego consumirá el servicio que la despliega (p. ej. ECS Fargate).sección 3
SHA del commitIdentificador único que Git le asigna a cada commit. Usarlo como tag de la imagen (en vez de latest) permite saber, mirando el tag, exactamente qué código quedó empaquetado en esa imagen.sección 3
git rev-parse --short HEADComando de Git que imprime la versión corta (7 caracteres) del SHA del commit actual — el valor típico que se usa como tag de la imagen.sección 3
Amazon ECS (Elastic Container Service)Servicio de AWS que ejecuta y orquesta contenedores (a diferencia de ECR, que solo los almacena). Decide dónde y cuántas copias de un contenedor corren.sección 4
FargateMotor serverless de ECS: ejecuta los contenedores sin que haya que administrar las instancias EC2 subyacentes (no hay servidor que aprovisionar ni parchear).sección 4
Cluster (ECS)Agrupa capacidad y servicios de ECS — el contenedor lógico que organiza todo lo demás (no confundir con el Cluster de Kubernetes de la Clase 10).sección 4
Task DefinitionPlantilla que describe cómo ejecutar los contenedores: qué imagen usar, cuánta CPU/memoria asignar, qué variables de entorno pasar.sección 4
Service (ECS)Mantiene el número deseado de Tasks en ejecución y gestiona las actualizaciones sin tiempo de caída (no confundir con el Service de Kubernetes).sección 4
OIDC (OpenID Connect)Protocolo que le permite a GitHub Actions obtener credenciales temporales de AWS asumiendo un rol IAM, en vez de usar credenciales de larga duración guardadas como secret.sección 5
Rol IAM (assumable role)Identidad de AWS que no tiene credenciales propias fijas, sino que otros (como GitHub, vía OIDC) pueden "asumir" temporalmente para operar con sus permisos.sección 5
permissions: id-token: writePermiso que se declara en el workflow de GitHub Actions para que ese job pueda solicitar un token OIDC.sección 5
aws-actions/configure-aws-credentialsAction oficial de AWS para GitHub Actions que, dado un rol (role-to-assume), intercambia el token OIDC por credenciales temporales de AWS.sección 5
OrderFlowEl proyecto final del curso (diseñado en la Clase 5, sección 7): el sistema de microservicios sobre el que se aplica, en esta clase, el pipeline de CI/CD completo.sección 6

🧪 2. Pruebas automatizadas con pytest en el pipeline ​

La idea central: el pipeline no avanza a menos que las pruebas pasen. Antes de construir la imagen Docker y subirla a un registro, el pipeline corre los tests del proyecto — si alguno falla, ahí se corta todo.

Ejemplo de test sobre el endpoint de salud del servicio:

python
def test_health(client):
    response = client.get(
        "/health/live"
    )
    assert response.status_code == 200
    assert (
        response.json()["status"] == "ok"
    )

Este test hace dos verificaciones sobre la respuesta del endpoint /health/live:

  1. Que el código de estado HTTP sea 200 (la petición se resolvió bien).
  2. Que el cuerpo de la respuesta tenga "status": "ok" (el servicio se declara sano a sí mismo, no solo que respondió).

Los steps correspondientes en el pipeline (instalar dependencias y correr pytest):

yaml
- name: Install dependencies
  run: |
    python -m pip install -r requirements.txt
    python -m pip install pytest

- name: Run tests
  run: pytest -v

Comportamiento del pipeline según el resultado de los tests:

Resultado de pytestQué pasa en el pipeline
✅ Código correctopytest pasa → el pipeline continúa hacia docker build y el push a ECR.
❌ Código defectuosopytest falla → el pipeline se detiene ahí mismo. No hay build, no hay despliegue.

⚠️ Dinámica propuesta en la clase: cambiar el valor "ok" por "error" en el handler del endpoint de salud y hacer git push. El pipeline debería fallar en el step de tests, sin llegar a construir ni desplegar la imagen — es la forma más directa de comprobar en vivo que el pipeline realmente bloquea código roto.

🗺️ Diagrama: pruebas automatizadas con pytest y comportamiento del pipeline ​

Slide "Pruebas automatizadas con pytest": a la izquierda, el ejemplo de test test_health(client) que verifica GET /health/live (status_code 200 y status "ok" en el JSON) y los steps del pipeline (Install dependencies, Run tests con pytest -v); a la derecha, el comportamiento del pipeline según el resultado — código correcto: pytest pasa y el pipeline continúa hacia Docker build y ECR; código defectuoso: pytest falla y el pipeline se detiene, sin build ni despliegue — y la dinámica propuesta: cambiar "ok" por "error" en el handler y hacer push para que el pipeline falle

📦 3. Amazon ECR — registro de imágenes en la nube ​

Una vez que el pipeline pasó las pruebas, el flujo para publicar la imagen es:

text
docker build              docker tag                 push a ECR
(construir la imagen  →   (etiquetar para ECR)   →   (subir al repositorio
 localmente)                                          remoto de Amazon ECR)
     │                          │                            │
     ▼                          ▼                            ▼
Imagen local              Imagen local            Amazon ECR
(verificar y probar       lista, apuntando a      (repositorio remoto)
 la imagen)                un repo de ECR

Buena práctica de etiquetado: etiquetar la imagen solo con latest hace imposible saber después qué código originó esa imagen en concreto. La práctica correcta es usar el SHA del commit de Git como tag, para trazabilidad completa:

bash
IMAGE_TAG=$(git rev-parse --short HEAD)
# Resultado: abc75d

Con eso, un repositorio ECR típico queda con varias imágenes etiquetadas por commit, más un tag latest que siempre apunta a la más reciente:

text
ECR: orderflow-products
├── abc75d...   (commit anterior)
├── f281ac...   (commit reciente)
└── latest      (apunta al más nuevo)

💡 Así se puede saber exactamente qué código está corriendo en producción en cualquier momento — algo que con solo latest no es posible, porque ese tag se sobrescribe en cada push y pierde el historial de qué versión representaba antes.

🧪 Tip de entrevista: ¿por qué no alcanza con etiquetar siempre latest? Porque latest es un puntero que se mueve — no es una versión fija. Si algo falla en producción, latest no te dice qué commit generó esa imagen; el SHA del commit sí, y por eso es la etiqueta que da trazabilidad real.

🗺️ Diagrama: Amazon ECR — flujo y estructura del repositorio ​

Slide "Amazon ECR — Registro de imágenes en la nube": a la izquierda, el flujo del entorno local a la nube (docker build para construir la imagen localmente, docker tag para etiquetarla para ECR) y la estructura de un repositorio ECR de ejemplo (orderflow-products con abc75d... como commit anterior, f281ac... como commit reciente, y latest apuntando al más nuevo); a la derecha, la buena práctica de etiquetado con el SHA del commit de Git (IMAGE_TAG = git rev-parse --short HEAD, resultado abc75d) en vez de solo latest, para saber exactamente qué código está corriendo en producción en cualquier momento

🚀 4. Amazon ECS Fargate — ejecutar contenedores sin gestionar servidores ​

Una vez que la imagen está en ECR, alguien tiene que ejecutarla. Ese "alguien" es ECS (Elastic Container Service), y Fargate es el motor que lo hace sin necesidad de administrar servidores propios:

text
Imagen Docker        ECR                Task Definition      Fargate Task        Contenedor FastAPI
(construir y     →   (registro de   →   (plantilla:      →   (ejecutar sin   →   (servicio web
 subir a ECR)         imágenes)          imagen, CPU,          gestionar            listo)
                                          memoria)              servidores)

Las cuatro piezas del rompecabezas:

PiezaQué hace
ClusterAgrupa capacidad y servicios ECS. Es el contenedor lógico que organiza todo.
Task DefinitionPlantilla que describe cómo ejecutar los contenedores: imagen, CPU, memoria, variables de entorno.
ServiceMantiene el número deseado de Tasks en ejecución y gestiona las actualizaciones sin tiempo de caída.
FargateMotor serverless que ejecuta contenedores sin que administremos instancias EC2 subyacentes.

❓ ¿ECR y ECS hacen lo mismo? No. ECR almacena imágenes. ECS las ejecuta y orquesta. Uno es el almacén, el otro es quien pone a correr lo que hay en el almacén.

🧪 Tip de entrevista: ¿qué diferencia a Fargate de "ECS a secas"? ECS es el orquestador; puede correr sobre instancias EC2 que vos administrás, o sobre Fargate, que es el modo serverless de ECS: no hay servidor que aprovisionar, parchear ni escalar a mano — AWS se encarga de la capa de infraestructura.

🗺️ Diagrama: Amazon ECS Fargate — de la imagen Docker al contenedor corriendo ​

Slide "Amazon ECS Fargate — Ejecutar contenedores sin gestionar servidores": a la izquierda, el flujo de 4 pasos (Imagen Docker: construir y subir a ECR → ECR: registro de imágenes → Task Definition: plantilla con imagen, CPU, memoria → Fargate Task: ejecutar sin gestionar servidores → Contenedor FastAPI: servicio web listo); a la derecha, 4 tarjetas numeradas explicando Cluster (agrupa capacidad y servicios ECS), Task Definition (plantilla que describe cómo ejecutar los contenedores), Service (mantiene el número deseado de Tasks en ejecución sin tiempo de caída) y Fargate (motor serverless que ejecuta contenedores sin administrar instancias EC2); abajo, la aclaración "¿ECR y ECS hacen lo mismo? No. ECR almacena imágenes. ECS las ejecuta y orquesta."

🔐 5. Autenticación segura: GitHub → AWS con OIDC ​

El pipeline necesita credenciales de AWS para poder hacer push a ECR y desplegar en ECS. El problema es cómo dárselas sin exponer un riesgo de seguridad.

❌ No recomendado — credenciales de larga duración como secret:

yaml
secrets:
  AWS_ACCESS_KEY_ID: AKIA...
  AWS_SECRET_ACCESS_KEY: wJal...

El riesgo: si estas credenciales se filtran (un log mal armado, un fork del repo, un secret mal configurado), pueden usarse indefinidamente y son difíciles de rotar — quien las tenga puede operar como si fuera vos hasta que alguien note la fuga y las revoque a mano.

✅ Recomendado — OIDC (OpenID Connect):

yaml
permissions:
  id-token: write
  contents: read

- name: Configure AWS
  uses: aws-actions/configure-aws-credentials@v6
  with:
    role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
    aws-region: us-east-1

Con OIDC, GitHub no guarda ninguna clave de AWS. En su lugar, obtiene credenciales temporales asumiendo un rol IAM, en tres pasos:

PasoQué pasa
1. GitHub genera un token OIDCFirmado y válido solo para ese workflow específico.
2. AWS IAM verifica el tokenComprueba que proviene del repositorio y la rama autorizados.
3. AWS entrega credenciales temporalesExpiran automáticamente. Sin secrets de larga duración almacenados.

⚠️ Notar que en el ejemplo recomendado el único secret que queda en GitHub es AWS_ROLE_ARN — y eso no es una credencial en sí misma, es solo el identificador del rol que IAM va a verificar. Sin el token OIDC firmado por GitHub para ese workflow puntual, ese ARN no le sirve a nadie.

🧪 Tip de entrevista: ¿por qué OIDC es más seguro que guardar AWS_SECRET_ACCESS_KEY como secret? Porque una credencial de larga duración, una vez filtrada, sigue siendo válida hasta que alguien la rota a mano. Con OIDC no hay credencial que filtrar: el token lo emite GitHub por workflow, AWS lo valida contra el repo/rama exactos, y la credencial que entrega expira sola.

🗺️ Diagrama: autenticación GitHub Actions → AWS con OIDC ​

Slide "Autenticación segura: GitHub → AWS con OIDC": a la izquierda, la comparación entre lo no recomendado (secrets AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY de larga duración, con el riesgo de que credenciales permanentes filtradas puedan usarse indefinidamente y sean difíciles de rotar) y lo recomendado (permissions id-token write y contents read, más el step "Configure AWS" con aws-actions/configure-aws-credentials@v6 y role-to-assume desde secrets.AWS_ROLE_ARN); a la derecha, cómo funciona OIDC en 3 pasos: 1) GitHub genera un token OIDC firmado y válido solo para ese workflow específico, 2) AWS IAM verifica el token comprobando que proviene del repositorio y rama autorizados, 3) AWS entrega credenciales temporales que expiran automáticamente, sin secrets de larga duración almacenados

🔁 6. El pipeline completo de OrderFlow ​

Uniendo todo lo anterior (pytest, ECR, ECS Fargate, OIDC), así queda el pipeline de punta a punta aplicado al proyecto final OrderFlow:

text
1. git push → GitHub
   El desarrollador sube código a la rama main.
        │
        ▼
2. Checkout + pytest
   Se clona el repositorio y se ejecutan los tests. Si fallan → STOP.
        │
        ▼
3. docker build + ECR push
   Se construye la imagen con el tag del commit SHA y se sube a ECR.
        │
        ▼
4. Task Definition + ECS update
   Se registra la nueva definición y se actualiza el servicio en Fargate.

Cada uno de estos 4 pasos ya se vio por separado: el step 2 es la sección 2 (pytest), el step 3 combina las secciones 3 y 5 (ECR + OIDC para autenticarse), y el step 4 es la sección 4 (ECS Fargate). Lo nuevo acá es verlos encadenados como un solo pipeline, donde cada paso depende de que el anterior haya salido bien.

🏆 Reto final — verifica tu pipeline:

SituaciónQué debería pasar
✅ Tests OKDeploy exitoso. Nueva imagen en producción.
❌ Test fallaPipeline bloqueado. Producción sin cambios.
🔁 Nuevo commitNueva imagen versionada con SHA único.

💡 Este es el mismo comportamiento de la sección 2 (pytest bloquea el pipeline si falla) pero visto ahora sobre el pipeline completo: un test roto no solo detiene "los tests", detiene todo el camino hacia producción — no llega a construirse la imagen, no se sube a ECR, no se actualiza ECS.

Hoja de ruta del bootcamp (dónde encaja esta clase):

text
Sesión 10 ──────────────── Sesión 11 (Aquí estamos)
Docker Image manual        CI/CD + ECR + ECS
(build/run a mano)         (todo automatizado en el pipeline)

📌 La Clase 10 dejó la imagen Docker lista pero el build/push era manual; esta clase automatiza exactamente esa parte manual dentro de un pipeline de CI/CD.

🗺️ Diagrama: pipeline completo de OrderFlow + reto final + hoja de ruta ​

Slide "El pipeline completo de OrderFlow": a la izquierda, el flujo de 4 pasos (git push → GitHub: el desarrollador sube código a la rama main; Checkout + pytest: se clona el repositorio y se ejecutan los tests, si fallan → STOP; docker build + ECR push: se construye la imagen con el tag del commit SHA y se sube a ECR; Task Definition + ECS update: se registra la nueva definición y se actualiza el servicio en Fargate); a la derecha, el reto final "Verifica tu pipeline" con 3 tarjetas (Tests OK: deploy exitoso, nueva imagen en producción; Test falla: pipeline bloqueado, producción sin cambios; Nuevo commit: nueva imagen versionada con SHA único) y la hoja de ruta del bootcamp mostrando Sesión 10 (Docker Image manual) → Sesión 11 CI/CD + ECR + ECS, marcada como "Aquí estamos"

💻 PARTE PRÁCTICA ​

🖱️ 7. Desplegar OrderFlow en AWS ECS Fargate — paso a paso en la consola ​

Recorrido real en la consola de AWS para crear la infraestructura de ECS a la que va a apuntar el pipeline de la sección 6. (en progreso — se amplía con cada paso a medida que avanza la clase)

1) Entrar a Amazon ECS y abrir "Clusters" — desde el buscador de servicios de AWS se llega a la landing de Amazon Elastic Container Service (/ecs/v2/getStarted); en el menú lateral, Clusters es donde se crea el cluster que va a alojar el Service y las Tasks de OrderFlow (ver glosario, sección 1):

Consola de AWS: landing page de Amazon Elastic Container Service, con el menú lateral desplegado (Clusters, Namespaces, Task definitions, Daemon task definitions, Account settings) y el cursor sobre la opción Clusters, primer paso para crear el cluster de ECS

📝 La región activa en este paso es United States (Ohio) (us-east-2) — distinta de us-east-1 usada como ejemplo en el fragmento de la sección 5 (aws-region: us-east-1). Al terminar el recorrido real, ajustar ese valor del workflow para que coincida con la región donde termine viviendo el cluster.

🏋️ EJERCICIOS CON SOLUCIÓN ​

(pendiente — 10 ejercicios graduales)

❓ Preguntas y respuestas (autoevaluación) ​

(pendiente — 10 preguntas graduales)

📎 Apuntes relacionados ​

(pendiente)

➡️ Siguiente ​

Clase 12