El código de MercadoFresco ya vive en un repositorio con reglas, ramas cortas y pull requests que alguien revisa. Pero revisar una PR es una persona leyendo un diff, y hay preguntas que nadie puede responder leyendo: ¿pasan las 214 pruebas? ¿arranca el proyecto en una máquina limpia, o solo en el portátil de Luis donde hay un psycopg2 instalado a mano en 2024? ¿alguna de las 87 dependencias tiene una vulnerabilidad publicada esta semana? ¿se ha colado una clave en un fichero de configuración?

AWS CodeBuild responde todo eso automáticamente. Es un servicio de construcción gestionado y sin servidores: arranca un contenedor limpio, clona tu código, ejecuta lo que le digas en un buildspec.yml, guarda los resultados y se apaga, cobrando por minuto de ejecución y no por tenerlo. Aquí es donde «funciona en mi portátil» deja de ser un argumento, porque a partir de ahora lo único que cuenta es si funciona en un contenedor vacío que nadie ha tocado.

Aviso de coste. CodeBuild cobra por minuto según el tamaño de cómputo: en eu-west-1, un BUILD_GENERAL1_SMALL unos 0,005 USD/min y un MEDIUM unos 0,010, con 100 minutos gratis al mes del tipo pequeño. Con 20 construcciones diarias de 4 minutos en MEDIUM, MercadoFresco pagará unos 24 USD al mes. Lo caro no es eso: es una construcción de 22 minutos que nadie optimiza, o construir dentro de la VPC sin ser consciente del coste del NAT, que veremos al final. Datos ficticios.

Contenido

  1. Qué es la integración continua y qué problema resuelve
  2. Anatomía de un proyecto de CodeBuild
  3. El rol de servicio y sus permisos
  4. El buildspec.yml de MercadoFresco, comentado
  5. Las fases, una a una
  6. Variables de entorno y secretos sin fugas en los registros
  7. Pruebas unitarias e informes de pruebas y cobertura
  8. Qué hacer cuando una prueba falla
  9. Pruebas de integración: simulación o clon de Aurora
  10. Análisis estático, dependencias y secretos filtrados
  11. Calidad como puerta, no como informe
  12. Artefactos, caché y construcciones en paralelo
  13. CodeBuild dentro de la VPC, con su aviso de coste
  14. Depurar una construcción que falla
  15. Métricas, alarmas y coste real
  16. Errores comunes y consejos
  17. Ejercicios
  18. Conclusión

Qué es la integración continua y qué problema resuelve

Integración continua significa que cada cambio se integra en la rama compartida y se verifica automáticamente, varias veces al día. Lo importante es la segunda mitad: se verifica automáticamente. Sin verificación, «integración continua» es solo empujar código a menudo.

Síntoma Causa real Qué lo elimina
«En mi portátil funciona» El portátil tiene estado acumulado Contenedor limpio en cada construcción
«Se me olvidó ejecutar las pruebas» Ejecutarlas es voluntario Ejecución automática en cada push
«Esa prueba lleva meses fallando» Nadie mira el resultado La construcción en rojo bloquea la PR
«Funcionaba antes de tu cambio» Nadie sabe cuándo se rompió Historial de construcciones por commit
«No sé qué versión hay desplegada» Se despliega una carpeta, no un artefacto Artefacto versionado e inmutable

Fíjate en el patrón: en todos los casos el arreglo es quitar a las personas la responsabilidad de acordarse. Un proceso que depende de la disciplina individual falla el día que hay prisa, que es exactamente el día que más falta hace. El flujo que vamos a montar:

flowchart LR
    A[Push o PR] --> B[Contenedor limpio]
    B --> C[install] --> D[pre_build<br/>secretos, lint, escaneos]
    D --> E[build<br/>pruebas + empaquetado] --> F[post_build]
    F --> G{Todo verde?}
    G -->|Si| H[Artefacto en<br/>mercadofresco-artefactos] --> J[Despliegue: 08-03]
    G -->|No| I[Rojo: alertas-mercadofresco] --> K[La PR no se fusiona]

Anatomía de un proyecto de CodeBuild

Un proyecto es la definición de cómo construir algo. Sus siete piezas:

Pieza Qué define Decisión de MercadoFresco
Origen De dónde sale el código GitHub vía conn-mercadofresco-github
Entorno Imagen, tamaño de cómputo, privilegios amazonlinux2-x86_64-standard:5.0, MEDIUM
Rol de servicio Qué puede hacer la construcción en AWS rol-codebuild-mercadofresco-tienda
Buildspec Los comandos a ejecutar buildspec.yml en la raíz del repositorio
Artefactos Qué se guarda y dónde ZIP en mercadofresco-artefactos
Caché Qué se reutiliza entre construcciones Local (custom + docker layer)
Registros Dónde va la salida /aws/codebuild/build-mercadofresco-tienda

Sobre la imagen del entorno. Las gestionadas de AWS traen Python, Node, Java, Go, la CLI y Docker preinstalados, y se actualizan solas. Una imagen propia en ECR tiene sentido cuando instalar dependencias del sistema tarda más de dos o tres minutos en cada construcción: mueves ese tiempo a una construcción de imagen ocasional. MercadoFresco empieza con la gestionada.

Sobre el tamaño de cómputo. Es la decisión con más impacto en coste y duración, y la intuición engaña:

Tamaño vCPU / RAM USD/min Duración en MercadoFresco Coste por construcción
SMALL 2 / 3 GB 0,005 9 min 40 s 0,048 USD
MEDIUM 4 / 7 GB 0,010 4 min 10 s 0,042 USD
LARGE 8 / 15 GB 0,020 3 min 30 s 0,070 USD

MEDIUM es más barato que SMALL aquí, porque el doble de precio por minuto se compensa con más del doble de velocidad: las pruebas corren con pytest -n auto y aprovechan los 4 vCPU. De MEDIUM a LARGE, en cambio, el tiempo baja poco y el coste sube, porque el trabajo ya no es paralelizable. Hay que medir, no suponer: el tamaño óptimo depende de si tu construcción sabe usar los núcleos que le das.

Sobre privilegedMode. Solo hace falta para construir imágenes de Docker, porque el demonio necesita privilegios. Actívalo únicamente cuando lo necesites: un contenedor privilegiado que ejecuta código de un repositorio tiene mayor superficie de ataque.

aws codebuild create-project \
  --name build-mercadofresco-tienda \
  --source '{"type": "GITHUB",
    "location": "https://github.com/mercadofresco/mercadofresco-tienda.git",
    "buildspec": "buildspec.yml", "gitCloneDepth": 1, "reportBuildStatus": true}' \
  --environment '{"type": "LINUX_CONTAINER",
    "image": "aws/codebuild/amazonlinux2-x86_64-standard:5.0",
    "computeType": "BUILD_GENERAL1_MEDIUM", "privilegedMode": false,
    "environmentVariables": [
      {"name": "BUCKET_ARTEFACTOS", "value": "mercadofresco-artefactos", "type": "PLAINTEXT"}]}' \
  --artifacts '{"type": "S3", "location": "mercadofresco-artefactos",
                "packaging": "ZIP", "namespaceType": "BUILD_ID"}' \
  --cache '{"type": "LOCAL", "modes": ["LOCAL_CUSTOM_CACHE", "LOCAL_DOCKER_LAYER_CACHE"]}' \
  --service-role arn:aws:iam::111122223333:role/rol-codebuild-mercadofresco-tienda \
  --timeout-in-minutes 20 --queued-timeout-in-minutes 30 \
  --tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
         Key=Componente,Value=cicd Key=Propietario,Value=luis Key=CentroCoste,Value=plataforma \
  --profile mercadofresco-dev --region eu-west-1

Dos parámetros merecen atención. gitCloneDepth: 1 clona solo el último commit: los 1.847 commits de Luis añaden 40 segundos a cada construcción sin aportar nada —salvo que necesites la historia para git describe, en cuyo caso pon 0—. Y timeout-in-minutes 20 es un freno de emergencia: una construcción colgada consumiría el timeout por defecto de 60 minutos, y eso son minutos facturados.

El rol de servicio y sus permisos

La construcción actúa en AWS con el rol de servicio, y aquí el privilegio mínimo de 04-01 importa mucho: este rol lo usa código que cambia en cada commit. Con PowerUserAccess, cualquiera que pueda fusionar una PR podría hacer cualquier cosa en la cuenta.

{
  "Version": "2012-10-17",
  "Statement": [
    { "Sid": "Registros", "Effect": "Allow",
      "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
      "Resource": "arn:aws:logs:eu-west-1:111122223333:log-group:/aws/codebuild/build-mercadofresco-*:*" },
    { "Sid": "ArtefactosSoloEnSuPrefijo", "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:GetObjectVersion"],
      "Resource": "arn:aws:s3:::mercadofresco-artefactos/tienda/*" },
    { "Sid": "ConfiguracionNoSensible", "Effect": "Allow",
      "Action": ["ssm:GetParameters", "ssm:GetParameter"],
      "Resource": "arn:aws:ssm:eu-west-1:111122223333:parameter/mercadofresco/construccion/*" },
    { "Sid": "SoloElSecretoDePruebas", "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "arn:aws:secretsmanager:eu-west-1:111122223333:secret:mercadofresco/pruebas/*" },
    { "Sid": "InformesDePruebas", "Effect": "Allow",
      "Action": ["codebuild:CreateReportGroup", "codebuild:CreateReport",
                 "codebuild:UpdateReport", "codebuild:BatchPutTestCases",
                 "codebuild:BatchPutCodeCoverages"],
      "Resource": "arn:aws:codebuild:eu-west-1:111122223333:report-group/build-mercadofresco-*" }
  ]
}

Lo importante está en los Resource, no en las acciones. El rol no puede leer mercadofresco/produccion/rds/mfadmin: solo secretos bajo mercadofresco/pruebas/. Es la diferencia entre una construcción comprometida que estropea un entorno de pruebas y una que tiene la contraseña de producción. Un proyecto de construcción no necesita jamás credenciales de producción, y si crees que las necesita, es que estás desplegando desde la construcción en lugar de desde CodeDeploy (08-03).

El buildspec.yml de MercadoFresco, comentado

Este fichero vive en la raíz de mercadofresco-tienda, versionado junto al código, y es el corazón de la lección:

version: 0.2

# Variables. Las de 'variables' son publicas y aparecen en los registros.
# Las de 'parameter-store' y 'secrets-manager' se resuelven en tiempo de ejecucion
# y CodeBuild las enmascara en la salida.
env:
  variables:
    BUCKET_ARTEFACTOS: "mercadofresco-artefactos"
    COBERTURA_MINIMA: "75"
  parameter-store:
    URL_API_PRUEBAS: "/mercadofresco/construccion/url_api_pruebas"
    HOST_AURORA_PRUEBAS: "/mercadofresco/construccion/host_aurora_pruebas"
  secrets-manager:
    CLAVE_PASARELA_PRUEBAS: "mercadofresco/pruebas/pasarela:clave_api"
  exported-variables:
    - VERSION_APP
    - COMMIT_CORTO

phases:

  install:
    runtime-versions:
      python: 3.12
    commands:
      - pip install --upgrade pip
      - pip install -r requirements.txt -r requirements-dev.txt
      - pip install --quiet bandit pip-audit ruff

  pre_build:
    commands:
      - export COMMIT_CORTO=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c1-7)
      - export VERSION_APP=$(cat VERSION)-${COMMIT_CORTO}
      - echo "Construyendo version ${VERSION_APP}"
      # Puertas ordenadas de mas rapida a mas lenta
      - ruff check app/ tests/ && ruff format --check app/ tests/
      - git secrets --scan || (echo "SECRETO DETECTADO EN EL CODIGO"; exit 1)
      - pip-audit --strict --desc
      - bandit -r app/ -ll -f json -o informe-bandit.json

  build:
    commands:
      # Pruebas unitarias en paralelo, con informe JUnit y cobertura.
      - |
        pytest tests/unitarias -n auto \
          --junitxml=informes/junit-unitarias.xml \
          --cov=app --cov-report=xml:informes/cobertura.xml \
          --cov-fail-under=${COBERTURA_MINIMA}
      # Pruebas de integracion contra servicios simulados (moto).
      - pytest tests/integracion --junitxml=informes/junit-integracion.xml
      # Empaquetado: solo lo que hace falta en produccion.
      - mkdir -p paquete
      - pip install -r requirements.txt --target paquete/ --quiet
      - cp -r app/ scripts/ appspec.yml paquete/
      - echo "${VERSION_APP}" > paquete/VERSION
      - cd paquete && zip -rq ../mercadofresco-tienda-${VERSION_APP}.zip . && cd ..

  post_build:
    # finally se ejecuta AUNQUE los comandos anteriores fallen.
    commands:
      - |
        if [ "${CODEBUILD_BUILD_SUCCEEDING}" = "1" ]; then
          aws s3 cp mercadofresco-tienda-${VERSION_APP}.zip \
            s3://${BUCKET_ARTEFACTOS}/tienda/${VERSION_APP}/ \
            --metadata commit=${CODEBUILD_RESOLVED_SOURCE_VERSION},version=${VERSION_APP}
          echo "Artefacto publicado: ${VERSION_APP}"
        else
          echo "Construccion fallida: no se publica artefacto"
        fi
    finally:
      - echo "Duracion total: $((SECONDS / 60))m $((SECONDS % 60))s"

# Los informes se registran en CodeBuild y se ven en la consola con su historico.
reports:
  pruebas-unitarias:
    files: ["informes/junit-unitarias.xml"]
    file-format: JUNITXML
  cobertura:
    files: ["informes/cobertura.xml"]
    file-format: COBERTURAXML

artifacts:
  files: [mercadofresco-tienda-*.zip]
  discard-paths: yes

cache:
  paths: ["/root/.cache/pip/**/*", ".ruff_cache/**/*"]

Las fases, una a una

Fase Para qué es Si falla Duración
install Tiempos de ejecución y herramientas Aborta; solo se ejecuta finally 20-90 s
pre_build Credenciales, variables, comprobaciones rápidas Aborta antes de gastar en pruebas 30-60 s
build Compilar, probar, empaquetar Aborta, pero post_build sí se ejecuta 2-4 min
post_build Publicar, notificar, limpiar Marca la construcción como fallida 10-30 s

Dos comportamientos confunden a todo el mundo la primera vez. post_build se ejecuta aunque build falle, y es deliberado: quieres publicar los informes de las pruebas precisamente cuando han fallado. Por eso el post_build de arriba comprueba CODEBUILD_BUILD_SUCCEEDING; sin esa comprobación publicarías en S3 el artefacto de una construcción cuyas pruebas fallaron, el error más peligroso de esta lección. Y el orden dentro de pre_build no es casual: ruff tarda 2 segundos, el escaneo de secretos 5, bandit 15 y pip-audit 20, así que un error de formato falla en 2 segundos en vez de en 40. Ordena siempre tus puertas por coste creciente.

Variables de entorno y secretos sin fugas en los registros

Los registros van a CloudWatch Logs y los lee cualquiera con permiso sobre ese grupo. Cualquier valor que imprimas ahí es visible, y un set -x o un echo de depuración pueden imprimir un secreto sin que nadie se dé cuenta.

Tipo Dónde se guarda ¿Enmascarado? Para qué
PLAINTEXT Definición del proyecto No Configuración pública
PARAMETER_STORE Parameter Store Sí (si es SecureString) Configuración por entorno
SECRETS_MANAGER Secrets Manager Contraseñas, claves de API

La sintaxis del bloque secrets-managerNOMBRE_VARIABLE: nombre-secreto:clave-json:etiqueta— es la que más se equivoca. Los dos puntos separan el nombre del secreto de la clave concreta dentro del JSON: sin :clave_api, la variable recibiría el JSON entero —{"clave_api": "...", "url": "..."}— y tu código fallaría con un error confuso. Y si el secreto tiene rotación de 04-03, AWSCURRENT garantiza que usas la versión vigente.

Cómo CodeBuild enmascara. Sustituye por *** los valores resueltos desde Parameter Store y Secrets Manager. Pero es una coincidencia de cadenas, no magia, y se escapa en tres casos: si transformas el valor (echo $CLAVE | base64), lo impreso ya no coincide y sale en claro; si el secreto acaba en un fichero que luego imprimes con cat; y si una herramienta lo incluye en un mensaje de error, como una cadena de conexión completa en la excepción de un driver.

Tres reglas prácticas: nunca set -x en un buildspec que maneje secretos; redirige a /dev/null lo que pueda llevar credenciales cuando no necesites la salida; y no metas secretos en variables PLAINTEXT, ni «temporalmente para probar», porque quedan en la definición del proyecto y en CloudTrail.

Pruebas unitarias e informes de pruebas y cobertura

Las pruebas de MercadoFresco viven en tests/ y prueban lo que importa. Un ejemplo real —la idempotencia del consumidor que construimos en 07-05, que es exactamente el tipo de propiedad que nadie verifica a mano:

# tests/unitarias/test_idempotencia.py
import pytest
from app.pedidos import procesar_mensaje_pedido

def test_mismo_mensaje_dos_veces_cobra_una_sola_vez(tabla_idempotencia, pasarela_falsa):
    """El escenario del modulo 7: SQS entrega dos veces el mismo pedido."""
    mensaje = {"id_pedido": "PED-2026-00841", "importe_eur": 48.20,
               "clave_idempotencia": "PED-2026-00841:cobro"}

    primera = procesar_mensaje_pedido(mensaje)
    segunda = procesar_mensaje_pedido(mensaje)   # el duplicado

    assert primera["estado"] == "cobrado"
    assert segunda["desde_cache"] is True
    assert pasarela_falsa.numero_de_cobros == 1          # LA asercion que importa
    assert pasarela_falsa.total_cobrado == pytest.approx(48.20)


def test_franja_invalida_se_rechaza_sin_tocar_aurora(aurora_falsa):
    with pytest.raises(ValueError, match="franja_entrega"):
        procesar_mensaje_pedido({"id_pedido": "X", "franja_entrega": "noche"})
    assert aurora_falsa.escrituras == 0    # rechazo ANTES de escribir

Fíjate en qué se afirma. No se comprueba «que la función devuelva algo»: se comprueba que la pasarela se llamó exactamente una vez y que un dato inválido no llegó a escribirse en Aurora. Una prueba que no puede fallar cuando el comportamiento se rompe no es una prueba, es decoración.

El bloque reports convierte el XML de JUnit en algo que CodeBuild entiende: en la consola verás por construcción cuántas pruebas pasaron, cuáles fallaron con su traza y la tendencia histórica. Sin él tendrías que buscar el fallo en 2.000 líneas de registro.

Sobre --cov-fail-under=75. La cobertura mide qué líneas ejecutaron las pruebas, no si lo hicieron bien: se puede tener 95 % sin una sola aserción útil. Aun así sirve para detectar código que nadie ejecuta, y un umbral que falla la construcción impide que baje sin que nadie lo note. El criterio de Marta es sensato: el umbral empieza en el valor actual y solo puede subir, nunca se baja para «desbloquear» una PR. Bajarlo una vez es bajarlo para siempre.

Qué hacer cuando una prueba falla

Es la sección más corta e importante del módulo, porque de ella depende que todo lo demás sirva.

Situación Reacción correcta Reacción que arruina el sistema
Falla por una prueba mal escrita Arreglar la prueba en la misma PR Marcarla @pytest.mark.skip
Falla de forma intermitente Investigar hoy: suele ser carrera real Reintentar hasta que pase
Falla por un servicio externo caído Simularlo; la unitaria no sale a la red Desactivar la prueba
Falla y hay prisa por desplegar Revertir el commit y desplegar lo anterior Saltarse la construcción

Las pruebas intermitentes son el mayor peligro, más que las que fallan siempre. Una que falla siempre se arregla; una que falla una de cada quince veces enseña al equipo a pulsar «reintentar», y a partir de ahí un fallo real también se reintenta hasta que pase. Suelen señalar algo verdadero: dependencia del orden de ejecución, una espera fija en vez de una condición, o una condición de carrera que también existe en producción. Trátala como fallo de prioridad alta o bórrala, pero no la dejes parpadeando. Y la regla que sostiene todo: una construcción en rojo bloquea la fusión; sin ella, el equipo aprende en dos semanas que el rojo es opcional. Esa puerta se cierra del todo en 08-04.

Pruebas de integración: simulación o clon de Aurora

Las pruebas unitarias no tocan la red. Las de integración sí, y hay dos estrategias con equilibrios muy distintos:

Estrategia Fidelidad Velocidad Coste Cuándo
Servicios simulados (moto, LocalStack) Media Segundos 0 La mayoría de casos
Contenedor real (Postgres en Docker) Alta para SQL 30-60 s 0 Consultas y esquema
Clon aurora-mf-pruebas-luis Total Minutos ENI + NAT Migraciones y rendimiento

Con moto, las llamadas a AWS se interceptan en memoria:

# tests/integracion/test_flujo_pedido.py
import boto3, json
from moto import mock_aws
from app.pedidos import confirmar_pedido

@mock_aws
def test_pedido_confirmado_encola_y_publica_evento():
    sqs = boto3.client("sqs", region_name="eu-west-1")
    sns = boto3.client("sns", region_name="eu-west-1")
    url_cola = sqs.create_queue(QueueName="cola-mercadofresco-pedidos")["QueueUrl"]
    tema = sns.create_topic(Name="mercadofresco-pedido-confirmado")["TopicArn"]

    resultado = confirmar_pedido(
        {"id_cliente": "CLI-4471", "lineas": [{"sku": "FRUT-0012", "uds": 3}],
         "franja_entrega": "tarde"}, url_cola=url_cola, tema_arn=tema)

    mensajes = sqs.receive_message(QueueUrl=url_cola, MaxNumberOfMessages=10)
    cuerpo = json.loads(mensajes["Messages"][0]["Body"])
    assert cuerpo["id_pedido"] == resultado["id_pedido"]
    assert cuerpo["franja_entrega"] == "tarde"
    assert cuerpo["version_evento"] == "2"     # ver 08-05

Esto verifica el flujo completo —confirmar, encolar, publicar— en menos de un segundo y sin coste, y para el 90 % de los casos es suficiente. El clon de Aurora se reserva para lo que la simulación no puede decirte: si una migración tarda 4 segundos o 40 minutos sobre 340 GB reales, si un índice nuevo se usa de verdad, si una consulta se degrada con el volumen de producción. Eso exige VPC, y la VPC tiene un coste.

Análisis estático, dependencias y secretos filtrados

Herramienta Qué busca Bloqueante Coste
ruff Errores de estilo, imports sin usar, bugs evidentes 0
git-secrets Patrones de credenciales en el código Sí, siempre 0
pip-audit CVE conocidos en dependencias Sí, con matices 0
bandit Patrones de código inseguro Informe 0
Amazon Inspector Vulnerabilidades en imágenes de ECR y Lambda Configurable Por recurso

git-secrets es la red que faltaba en 08-01. El .gitignore protege del descuido; esto protege también del git add -f:

- git secrets --register-aws                          # patrones de AWS: AKIA, claves secretas
- git secrets --add 'sk_live_[0-9a-zA-Z]{24}'         # claves de pasarela de pago
- git secrets --add 'postgres://[^:]+:[^@]+@'         # cadenas de conexion con contrasena
- git secrets --add --allowed 'AKIAIOSFODNN7EXAMPLE'  # el de la documentacion, permitido

Que sea bloqueante no se discute: un falso positivo cuesta añadir una excepción --allowed; un falso negativo cuesta rotar credenciales en producción.

pip-audit con matices. Falla si alguna dependencia tiene un CVE conocido, lo cual es correcto pero tiene un modo de fallo que hay que anticipar: se publica un CVE en una biblioteca popular un martes por la tarde y todas tus construcciones se ponen en rojo, incluida la de una corrección urgente que no tiene nada que ver. Evita la salida fácil de --ignore-vuln, que se convierte en una lista que solo crece; lo correcto es fallar y avisar por alertas-mercadofresco con el identificador de la construcción, de modo que exista un procedimiento y no una excepción permanente.

Amazon CodeGuru Reviewer merece una mención: analiza el código con modelos entrenados sobre el propio código de Amazon y detecta lo que un linter no ve —fugas de recursos, condiciones de carrera, uso incorrecto de las APIs de AWS—. Es de pago y sus recomendaciones son sugerencias, no puertas: para tres personas es un lujo prescindible; para cincuenta puede compensar.

Calidad como puerta, no como informe

Aquí está el criterio que separa un pipeline útil de un teatro de calidad. Una comprobación es o una puerta —falla la construcción y bloquea la fusión— o un informe que nadie va a leer. No hay punto intermedio: un informe que no bloquea nada se ignora al tercer día.

Comprobación ¿Puerta? Por qué
Pruebas unitarias Una prueba roja es un comportamiento roto
Cobertura < 75 % Impide que baje sin que nadie lo note
Secretos detectados El coste del falso negativo es rotar en producción
ruff Rápido y objetivo; discutir estilo en las PR es tiempo perdido
CVE en dependencias , con aviso Con procedimiento para el martes por la tarde
bandit No, informe Muchos falsos positivos; se revisa semanalmente
Complejidad ciclomática No, informe Umbral arbitrario; genera discusiones estériles

La regla para decidir: si no estás dispuesto a parar un despliegue por ello, no es una puerta. Y si no lo es, sé honesto sobre que casi nadie lo leerá: ponle un momento explícito de revisión —«los lunes se mira el informe de bandit»— o quítalo del buildspec.

Artefactos: el paquete de la aplicación y las imágenes

El resultado de la construcción es un artefacto inmutable y versionado, y este es el cambio conceptual más importante frente al scp de Luis: ya no se despliega «lo que hay en la carpeta», se despliega mercadofresco-tienda-1.5.0-a3f9c21.zip, un fichero que existe, tiene identificador, se puede descargar seis meses después y es exactamente lo que se probó.

El bucket mercadofresco-artefactos se crea con tres propiedades no negociables: versionado, que CodePipeline exige y que protege de una sobrescritura accidental; cifrado con alias/mercadofresco-datos y BucketKeyEnabled para reducir las llamadas a KMS, que se facturan aparte; y una regla de ciclo de vida que caduque los artefactos.

aws s3api put-bucket-versioning --bucket mercadofresco-artefactos \
  --versioning-configuration Status=Enabled --profile mercadofresco-dev

aws s3api put-bucket-lifecycle-configuration --bucket mercadofresco-artefactos \
  --lifecycle-configuration '{"Rules":[{
    "ID":"caducar-artefactos-antiguos","Status":"Enabled",
    "Filter":{"Prefix":"tienda/"},
    "Expiration":{"Days":90},
    "NoncurrentVersionExpiration":{"NoncurrentDays":30}}]}' \
  --profile mercadofresco-dev

Sin ciclo de vida, veinte artefactos diarios de 45 MB son 27 GB al año de ZIP que nadie desplegará jamás; con 90 días tienes de sobra para volver atrás y el coste se queda en céntimos.

Imágenes de contenedor. Si en lugar de un ZIP construyes una imagen, necesitas privilegedMode: true, un docker login contra ECR en pre_build con aws ecr get-login-password, un docker build etiquetando con ${VERSION_APP} y un docker push en post_build. Sobre esto, solo lo justo: ECR, las capas, el escaneo de imágenes y el despliegue en contenedores son el módulo 10. Aquí basta con saber que CodeBuild construye imágenes igual de bien que ZIP y que LOCAL_DOCKER_LAYER_CACHE reutiliza las capas.

Caché de dependencias y su efecto real

Instalar 87 paquetes desde PyPI tarda 45-70 segundos en cada construcción: por 20 construcciones diarias, 20 minutos al día descargando lo mismo.

Tipo de caché Dónde vive Ahorro medido Limitación
Local (LOCAL_CUSTOM_CACHE) Host de la construcción 40-55 s Solo si cae en el mismo host
S3 Bucket que indiques 25-40 s Se paga subir y bajar
Capas de Docker Host de la construcción 1-3 min Solo con privilegedMode

Los números reales: sin caché, 4 min 10 s; con acierto, 3 min 20 s; con fallo, 4 min 12 s. El ahorro es real pero modesto, y conviene decirlo porque hay expectativas exageradas: la caché local depende de que la construcción caiga en un host que la tenga, y con 20 diarias el acierto ronda el 60 %.

Dos advertencias. Una caché envenenada es peor que no tener caché: si guardas site-packages en lugar del caché de pip, arrastras versiones antiguas y depurarlo lleva horas —cachea siempre ~/.cache/pip—. Y si sospechas de la caché, invalídala: aws codebuild invalidate-project-cache es el primer comando a probar cuando una construcción falla de forma inexplicable.

Construcciones en lotes

Cuando la suite crece, el bloque batch del buildspec define un grafo de construcciones: varias —pruebas_unitarias, pruebas_integracion, escaneos— corren a la vez, y una etapa empaquetado con depend-on espera a las tres; fast-fail: true cancela las demás en cuanto una falla. Una construcción secuencial de 12 minutos puede bajar a 6, pero el coste total en minutos no baja —pagas los mismos, solo que en paralelo—; lo que compras es tiempo de espera del desarrollador, que suele valer mucho más que 0,04 USD. Empieza simple: no paralelices una construcción de 4 minutos.

CodeBuild dentro de la VPC, con su aviso de coste

Por defecto CodeBuild corre fuera de tu VPC, con salida a internet y sin acceso a tus subredes privadas: no puede hablar con aurora-mf-pruebas-luis, que está en snet-mercadofresco-datos-a.

aws codebuild update-project --name build-mercadofresco-tienda \
  --vpc-config '{"vpcId": "vpc-mercadofresco",
    "subnets": ["snet-mercadofresco-app-a", "snet-mercadofresco-app-b"],
    "securityGroupIds": ["sg-mercadofresco-construccion"]}' \
  --profile mercadofresco-dev --region eu-west-1

Tres cosas que hay que hacer bien. Subredes privadas, siempre: en una pública sin IP pública la construcción no tendrá salida a internet y pip install colgará hasta el timeout, y es el fallo más común y desconcertante. Salida por NAT para llegar a PyPI, o VPC endpoints para las APIs de AWS. Y un grupo de seguridad sg-mercadofresco-construccion con una regla de entrada en sg-mercadofresco-basedatos que permita el 5432 desde él: nunca abras la base de datos por CIDR.

Aviso de coste importante. Meter CodeBuild en la VPC tiene dos costes que sorprenden en la factura. Las ENI: cada construcción crea y destruye una interfaz de red, lo que añade 30-60 segundos de arranque a cada ejecución —con 20 diarias, 20 minutos al día facturados por no hacer nada—. Y el NAT gateway: 0,048 USD/hora más 0,048 USD por GB; descargar 300 MB de dependencias 20 veces al día son unos 9 GB al mes solo en tránsito. La recomendación práctica es tener dos proyectos: build-mercadofresco-tienda fuera de la VPC para el 90 % de las construcciones con servicios simulados, y build-mercadofresco-integracion dentro, solo en las PR hacia main o de forma nocturna. Y VPC endpoints de tipo Gateway para S3 y DynamoDB, gratuitos, que evitan que ese tráfico pase por el NAT.

Depurar una construcción que falla

1. Los registros de CloudWatch, en /aws/codebuild/build-mercadofresco-tienda. Para ir al grano sin leer 2.000 líneas, aws codebuild batch-get-builds --ids <id> con --query 'builds[0].phases[?phaseStatus==FAILED].[phaseType,contexts[0].message]' te da la fase que falló y su mensaje.

2. Reproducción local con la imagen oficial. La forma más rápida de iterar, y no cuesta nada:

./codebuild_build.sh -i public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:5.0 \
  -a /tmp/salida -s ~/proyectos/tienda -b buildspec.yml

El script está en el repositorio aws/aws-codebuild-docker-images y ejecuta el mismo buildspec en la misma imagen, en tu máquina: si falla igual, tienes un bucle de depuración de segundos en vez de minutos. Ojo: los secretos no se resuelven en local salvo que le pases credenciales, así que exporta valores de prueba.

3. La sesión de depuración interactiva. Cuando el fallo solo ocurre en CodeBuild —y ocurre—, pon codebuild-breakpoint como comando justo antes del que falla: la construcción se detiene ahí y espera. Lánzala con --debug-session-enabled y conecta con Session Manager desde la consola. Te encuentras dentro del contenedor, en el punto exacto, con el código clonado y las variables resueltas; codebuild-resume continúa. Requiere permisos de SSM en el rol de servicio, y la construcción sigue facturando mientras esperas: no dejes una sesión abierta a la hora de comer.

4. Las variables que CodeBuild te da, que suelen resolver la duda antes que nada: CODEBUILD_RESOLVED_SOURCE_VERSION (el SHA exacto construido), CODEBUILD_WEBHOOK_TRIGGER (qué lo disparó: branch/main, pr/42, tag/v1.5.0), CODEBUILD_BUILD_SUCCEEDING (imprescindible en post_build), CODEBUILD_BUILD_ID y CODEBUILD_SRC_DIR.

Métricas, alarmas y coste real

CodeBuild publica métricas en AWS/CodeBuild. Las tres que importan son FailedBuilds (alarma: ≥ 3 en 1 hora, o 1 sola si es main), Duration (p90 > 8 min) y SucceededBuilds para la tasa de éxito.

aws cloudwatch put-metric-alarm --alarm-name mercadofresco-construcciones-fallidas \
  --namespace AWS/CodeBuild --metric-name FailedBuilds --statistic Sum \
  --dimensions Name=ProjectName,Value=build-mercadofresco-tienda \
  --period 3600 --evaluation-periods 1 --threshold 3 \
  --comparison-operator GreaterThanOrEqualToThreshold --treat-missing-data notBreaching \
  --alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
  --profile mercadofresco-dev --region eu-west-1

--treat-missing-data notBreaching es importante: sin construcciones no hay datos, y no querrás una alarma sonando cada noche. Es la misma lección de 05-01.

Coste real de MercadoFresco al mes:

Concepto Cálculo Coste
Construcciones de PR y ramas 20/día × 4,2 min × 22 días × 0,010 18,48 USD
Integración nocturna (en VPC) + tránsito NAT 1/día × 9 min × 30 × 0,010 + 9 GB 3,13 USD
Artefactos en S3 con caducidad de 90 días ~4 GB 0,09 USD
Registros de CloudWatch ~2 GB ingeridos 1,15 USD
Total ≈ 22,85 USD/mes

Veintitrés dólares al mes por eliminar «funciona en mi portátil» del vocabulario del equipo. Compáralo con un despliegue roto un viernes a las 19:00 con 900 pedidos/hora en juego.

Limpieza: delete-project, delete-report-group --delete-reports y delete-log-group. Los grupos de registro no se borran al borrar el proyecto y siguen facturando almacenamiento; ponles retención con aws logs put-retention-policy --retention-in-days 30 desde el primer día.

Errores Comunes y Consejos

Publicar el artefacto sin comprobar CODEBUILD_BUILD_SUCCEEDING. post_build se ejecuta aunque build falle, así que subes a S3 el paquete de una construcción cuyas pruebas fallaron. Es el error más peligroso de la lección.

Poner secretos en variables PLAINTEXT, que quedan en la definición del proyecto y en CloudTrail; u olvidar :clave en la referencia a Secrets Manager, con lo que la variable recibe el JSON entero y la aplicación falla con un error que no apunta a la causa. Y usar set -x con secretos: el enmascarado es coincidencia de cadenas y en cuanto transformas el valor sale en claro.

CodeBuild en una subred pública. Sin IP pública no hay salida a internet y pip install cuelga hasta el timeout. Subredes privadas con NAT, siempre. Y no metas todas las construcciones en la VPC «por si acaso»: pagas 30-60 s de ENI en cada una más el tránsito del NAT.

Bajar el umbral de cobertura para desbloquear una PR. Se baja una vez y ya no vuelve a subir. Y reintentar una prueba intermitente hasta que pase enseña al equipo que el rojo no significa nada, así que el día que sea real también se reintentará.

Cachear site-packages en vez de ~/.cache/pip. Arrastras versiones antiguas y depurarlo lleva horas. Y no poner retención a los grupos de registro: facturan para siempre, incluso tras borrar el proyecto.

Consejo: ordena las puertas de más rápida a más lenta. Un fallo de formato debe fallar en 2 segundos, no después de 4 minutos de pruebas.

Consejo: haz el buildspec ejecutable en local con codebuild_build.sh y la imagen oficial; si solo funciona dentro de CodeBuild, cada iteración de depuración cuesta minutos. Exporta la versión con exported-variables para que CodePipeline (08-04) la use en las etapas siguientes, y pon un timeout-in-minutes ajustado: es el freno que evita pagar 60 minutos por una construcción colgada.

Ejercicios

Ejercicio 1: el artefacto que no debería existir

El viernes a las 18:40, Luis ve en mercadofresco-artefactos el fichero tienda/1.5.2-c7a91f3/mercadofresco-tienda-1.5.2-c7a91f3.zip, publicado a las 18:32. Pero en CodeBuild la construcción c7a91f3 aparece en rojo: fallaron 3 pruebas de tests/unitarias/test_pagos.py.

Responde: (a) cómo es posible que exista el artefacto si la construcción falló; (b) qué lo ha permitido y cómo se corrige; (c) por qué es peligroso más allá de este caso, teniendo en cuenta lo que hará CodePipeline en 08-04; (d) qué comprobación pondrías para que un artefacto así no se pueda desplegar; (e) qué alarma habría avisado a Marta a las 18:35.

Ejercicio 2: la construcción de 22 minutos

La construcción tarda 22 minutos: install 4 min (140 paquetes y psycopg2 compilado desde fuentes), pre_build 3 min (pip-audit, bandit, ruff y git-secrets en serie), build 14 min (620 pruebas unitarias en serie, 40 de integración contra el clon de Aurora y el empaquetado), post_build 1 min. El proyecto está en la VPC, en SMALL, sin caché. Luis ha empezado a saltarse la construcción local.

Propón un plan que baje el tiempo por debajo de 6 minutos. Para cada medida indica el ahorro estimado, el efecto en el coste y qué se pierde. Di explícitamente cuál aplicarías primero y por qué.

Ejercicio 3: el secreto que salió en los registros

Sara, revisando los registros de una construcción, encuentra esta línea en /aws/codebuild/build-mercadofresco-tienda:

[pre_build] Conectando a postgresql://mfadmin:V3rd3_Manzana_2026@aurora-mf-pruebas-luis...

El secreto está declarado correctamente en env.secrets-manager y CodeBuild lo enmascara en la salida directa. En pre_build hay estas dos líneas:

      - export CADENA_CONEXION="postgresql://mfadmin:${PASS_AURORA}@${HOST_AURORA_PRUEBAS}:5432/mf"
      - echo "Conectando a ${CADENA_CONEXION}"

Responde: (a) por qué el enmascarado no ha funcionado; (b) qué haces en los primeros 30 minutos, en orden; (c) cómo corriges el buildspec; (d) los registros llevan 6 meses con retención indefinida y esa línea aparece en 340 construcciones: cómo lo abordas; (e) qué control automático añadirías para detectarlo la próxima vez.

Soluciones

Solución 1

(a) Porque post_build se ejecuta aunque build falle. Es deliberado —permite publicar informes de pruebas fallidas— pero significa que los comandos de publicación que pongas ahí se ejecutan igualmente: las pruebas fallaron en build, la fase abortó, y post_build subió el ZIP tan tranquilo.

(b) Falta la guarda de CODEBUILD_BUILD_SUCCEEDING: el buildspec culpable tiene un aws s3 cp sin condición en post_build. La corrección es la que sí aparece en el buildspec de la lección:

  post_build:
    commands:
      - |
        if [ "${CODEBUILD_BUILD_SUCCEEDING}" != "1" ]; then
          echo "Construccion fallida: no se publica artefacto"; exit 1
        fi
      - aws s3 cp mercadofresco-tienda-*.zip s3://${BUCKET_ARTEFACTOS}/tienda/${VERSION_APP}/

(c) Porque rompe la garantía de la que depende todo lo demás: que un artefacto publicado es un artefacto que pasó las pruebas. En 08-04 el pipeline tomará el artefacto de una etapa y lo pasará a la siguiente; si además alguien despliega por nombre de fichero desde S3, se desplegaría código con tres pruebas de pagos rotas. Y sería silencioso: el ZIP existe, tiene buen aspecto y su nombre no dice nada de su procedencia. La confianza en el sistema se pierde entera con un solo caso así.

(d) Dos capas baratas. Metadatos en el objeto--metadata estado=exitosa,commit=...— y una comprobación en el despliegue que rechace lo que no los tenga. Y, más robusto, separar responsabilidades: que la construcción no publique nada y sea el mecanismo nativo (artifacts:) el que entregue el paquete a CodePipeline, que solo avanza si la etapa anterior terminó bien. La regla general: si la publicación es un efecto secundario de un script, algún día se ejecutará cuando no debía; si es una transición de estado del pipeline, no puede ocurrir fuera de orden.

(e) La alarma mercadofresco-construcciones-fallidas sobre FailedBuilds, con umbral bajo —1 en 5 minutos para main— y destino alertas-mercadofresco: el umbral de 3 en 1 hora sirve para detectar una tendencia, pero en main un solo fallo merece aviso inmediato. Complementariamente, una regla de EventBridge sobre CodeBuild Build State Change con build-status: FAILED avisa en segundos con el enlace directo a los registros.

Solución 2

# Medida Ahorro Coste Qué se pierde
1 Sacar el proyecto de la VPC; build-mercadofresco-integracion aparte, nocturno ~7 min Baja: menos NAT y ENI Feedback inmediato contra Aurora real
2 pytest -n auto con MEDIUM ~7 min Sube el precio/min, baja el total Nada, si las pruebas están aisladas
3 Caché de pip + psycopg2-binary en vez de compilar ~3 min Ninguno La rueda precompilada, aceptable
4 Escaneos en paralelo ~2 min Igual en minutos totales Registros más difíciles de leer
5 gitCloneDepth: 1 ~30 s Ninguno git describe para la versión
6 pytest-split en 3 construcciones en lotes ~2 min Mismos minutos, en paralelo Complejidad de configuración

Primero, la medida 1, y la justificación es la clave del ejercicio: es la que más tiempo ahorra y la única que además reduce el coste. Las 40 pruebas contra el clon de Aurora aportan valor, pero no en cada push: lo aportan antes de fusionar hacia main y en la ejecución nocturna. Con el 90 % de las construcciones fuera de la VPC desaparecen los 30-60 s de ENI, el tránsito del NAT y buena parte de la lentitud de red. Es también la de menor riesgo: no cambia ni una prueba.

Segundo, la medida 2, contraintuitiva y muy rentable: pasar de SMALL a MEDIUM duplica el precio por minuto pero reduce el total, porque 620 pruebas con -n auto sobre 4 vCPU casi dividen por cuatro el tiempo de la suite. El requisito es que estén aisladas: si comparten una SQLite o un directorio temporal fijo, la paralelización las hará fallar de forma intermitente —justo el problema del que hablábamos—, y entonces el arreglo es aislarlas, no volver a serie.

Resultado: install 4 → 1 min, pre_build 3 → 1, build 14 → 3, post_build 1. Unos 6 minutos, con coste por construcción casi igual y menos factura de NAT. Y la consecuencia que no está en la tabla y es la más importante: con 6 minutos, Luis deja de saltarse la construcción.

Solución 3

(a) Porque el enmascarado es una sustitución literal de la cadena del secreto en la salida, no un análisis semántico. El buildspec interpola ${PASS_AURORA} dentro de una cadena mayor; el resultado es un valor nuevo que CodeBuild no conoce, así que al hacer echo no encuentra coincidencia que enmascarar y lo imprime entero. Es el primero de los tres casos: transformar el valor rompe el enmascarado.

(b) Los primeros 30 minutos, en orden. Rotar la contraseña de mfadmin de pruebas, porque siempre se empieza por cortar la validez de lo filtrado. Determinar quién ha podido leerla, con CloudTrail buscando GetLogEvents y FilterLogEvents sobre ese grupo en los últimos 6 meses; el alcance depende de cuánta gente tenga lectura sobre CloudWatch Logs, que suele ser más de la que uno cree. Comprobar si afecta a producción —y aquí se ve el valor de la separación de prefijos del rol: CodeBuild no puede leer mercadofresco/produccion/rds/mfadmin, así que el incidente queda acotado a pruebas—. Y corregir el buildspec antes de la siguiente construcción, o el registro se seguirá escribiendo.

(c) No imprimir nunca la cadena completa: si necesitas trazar, traza solo lo no sensible (echo "Conectando a ${HOST_AURORA_PRUEBAS}:5432/mf como mfadmin"). La mejora de fondo es otra: si la aplicación lee el secreto de Secrets Manager con su propio rol, el valor nunca pasa por una variable de entorno del shell y desaparece toda esta clase de fugas. La variable de entorno es cómoda, pero es también la superficie por la que se escapan los secretos.

(d) Trata los registros históricos como datos comprometidos: exporta el grupo a S3 si hace falta conservarlo por auditoría, borra los flujos afectados —no hay edición parcial en CloudWatch Logs— y pon retención de 30 días. Como la contraseña ya está rotada en (b), la exposición residual no da acceso a nada: borrarlos es higiene y cumplimiento, no contención. Si el secreto hubiera sido de producción, además habría que valorar la notificación interna que vimos en 08-01.

(e) Un filtro de métrica de CloudWatch Logs sobre /aws/codebuild/* que cuente coincidencias de patrones como postgresql://*:*@, AKIA o sk_live_, con alarma hacia alertas-mercadofresco. Es mejor que una comprobación dentro del buildspec porque funciona aunque nadie se acuerde de mantenerla y cubre todos los proyectos, presentes y futuros. Es la idea de 05-01: si un dato no debería aparecer nunca en un registro, pon una métrica que lo cuente y alarma en cuanto pase de cero.

Conclusión

«Funciona en mi portátil» ya no significa nada en MercadoFresco. Cada push arranca un contenedor limpio que no sabe nada del psycopg2 que Luis instaló a mano en 2024: instala las dependencias declaradas, ejecuta 214 pruebas, comprueba que la cobertura no baja del 75 %, escanea las 87 dependencias en busca de CVE, busca credenciales filtradas y produce un artefacto versionado e inmutable en mercadofresco-artefactos. Lo que se despliegue será ese ZIP, con su commit y sus metadatos.

Conoces la anatomía de un proyecto —origen, entorno, rol de servicio, buildspec, artefactos, caché y registros— y sabes que el tamaño de cómputo hay que medirlo y no suponerlo, porque MEDIUM puede salir más barato que SMALL cuando la construcción sabe usar los cuatro núcleos. Sabes que el rol de servicio lo ejecuta código que cambia en cada commit, y que por eso su alcance se limita al prefijo mercadofresco/pruebas/: una construcción no necesita jamás credenciales de producción. Dominas el buildspec.yml, el orden de las fases y las dos trampas que pillan a todo el mundo: que post_build se ejecuta aunque build falle —de ahí la guarda de CODEBUILD_BUILD_SUCCEEDING, sin la cual publicas artefactos de construcciones rotas— y que las puertas se ordenan de más rápida a más lenta.

Sabes inyectar secretos con la sintaxis secreto:clave:etiqueta y —más importante— por qué el enmascarado se rompe en cuanto transformas el valor o lo interpolas en una cadena mayor. Tienes pruebas que verifican lo que de verdad importa, como que la pasarela se llamó exactamente una vez ante un mensaje duplicado, y los reports que convierten un XML de JUnit en un histórico consultable. Y tienes la disciplina que sostiene todo esto: una prueba intermitente se investiga o se borra, nunca se reintenta; el umbral de cobertura solo sube; y una comprobación que no estás dispuesto a que pare un despliegue no es una puerta, es decoración.

Sabes cuándo basta con moto —el 90 % de los casos, en un segundo y sin coste— y cuándo hace falta el clon aurora-mf-pruebas-luis, con la advertencia que trae: CodeBuild en la VPC cuesta 30-60 segundos de ENI por construcción y el tránsito del NAT, y por eso MercadoFresco tiene dos proyectos, uno rápido fuera y uno de integración dentro. Y sabes depurar sin adivinar: los registros, la reproducción local con la imagen oficial y codebuild-breakpoint para entrar en el contenedor en el punto exacto. Todo por unos 23 dólares al mes.

Pero hay algo que esta lección no ha cambiado. El artefacto está ahí, verificado, esperando en un bucket. Y para llevarlo a las instancias del ASG asg-mercadofresco-tienda, Luis sigue haciendo lo mismo que el primer día: entrar por SSH, copiar el ZIP, descomprimirlo, reiniciar el servicio y mirar la web con los dedos cruzados. Durante los cuarenta segundos que tarda, la instancia sirve errores; si hay dos y las hace en serie, la mitad del tráfico ve una versión y la otra mitad otra; y si el ZIP resulta estar roto, la vuelta atrás consiste en buscar el anterior y repetir el ritual con la tienda caída. El artefacto es bueno; la forma de ponerlo en producción sigue siendo un ritual manual sin red de seguridad.

En 08-03, «AWS CodeDeploy», eso se resuelve. Veremos los tres destinos —EC2, Lambda y ECS— y qué estrategias admite cada uno; los conceptos de aplicación, grupo de despliegue, revisión y configuración de despliegue, con el agente instalado en las instancias del ASG; el appspec.yml y sus ganchos del ciclo de vida uno a uno, con los scripts reales de MercadoFresco para parar el servicio, calentar la caché y comprobar /salud; las estrategias en el sitio con AllAtOnce, HalfAtATime y OneAtATime, el blue/green con los grupos de destino del ALB, y el canario y lineal para desplegar la Lambda mercadofresco-cobrar-pago; la advertencia sobre migraciones de esquema y por qué nunca deben ir en el mismo paso que el código; y sobre todo la pieza que cierra el cuarto problema del curso: la reversión automática cuando salta mercadofresco-alb-latencia-alta o falla un gancho, sin que nadie esté mirando.

Curso de AWS

Módulo 1: Introducción a AWS

Módulo 2: Servicios principales de AWS

Módulo 3: Redes y entrega de contenido

Módulo 4: Seguridad e identidad

Módulo 5: Monitorización y gestión

Módulo 6: Bases de datos

Módulo 7: Integración de aplicaciones

Módulo 8: Herramientas para desarrolladores

Módulo 9: Infraestructura como código y gobierno de cuentas

Módulo 10: Contenedores en AWS

Módulo 11: Mejores prácticas y gestión de costos

© Copyright 2026. Todos los derechos reservados