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, unBUILD_GENERAL1_SMALLunos 0,005 USD/min y unMEDIUMunos 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
- Qué es la integración continua y qué problema resuelve
- Anatomía de un proyecto de CodeBuild
- El rol de servicio y sus permisos
- El
buildspec.ymlde MercadoFresco, comentado - Las fases, una a una
- Variables de entorno y secretos sin fugas en los registros
- Pruebas unitarias e informes de pruebas y cobertura
- Qué hacer cuando una prueba falla
- Pruebas de integración: simulación o clon de Aurora
- Análisis estático, dependencias y secretos filtrados
- Calidad como puerta, no como informe
- Artefactos, caché y construcciones en paralelo
- CodeBuild dentro de la VPC, con su aviso de coste
- Depurar una construcción que falla
- Métricas, alarmas y coste real
- Errores comunes y consejos
- Ejercicios
- 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-1Dos 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 | Sí | Contraseñas, claves de API |
La sintaxis del bloque secrets-manager —NOMBRE_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 escribirFí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-05Esto 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 | Sí | 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, permitidoQue 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 | Sí | Una prueba roja es un comportamiento roto |
| Cobertura < 75 % | Sí | Impide que baje sin que nadie lo note |
| Secretos detectados | Sí | El coste del falso negativo es rotar en producción |
ruff |
Sí | Rápido y objetivo; discutir estilo en las PR es tiempo perdido |
| CVE en dependencias | Sí, 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-devSin 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-1Tres 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-tiendafuera de la VPC para el 90 % de las construcciones con servicios simulados, ybuild-mercadofresco-integraciondentro, solo en las PR haciamaino 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.ymlEl 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:
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
- ¿Qué es AWS?
- Configuración de tu cuenta de AWS
- Infraestructura global de AWS
- Consola de administración de AWS
- AWS CLI y SDKs
Módulo 2: Servicios principales de AWS
Módulo 3: Redes y entrega de contenido
- Amazon VPC
- Grupos de seguridad y listas de control de acceso
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Módulo 4: Seguridad e identidad
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager y Parameter Store
- AWS Shield
- AWS WAF
Módulo 5: Monitorización y gestión
- Amazon CloudWatch
- AWS X-Ray y trazabilidad distribuida
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Módulo 6: Bases de datos
- Cómo elegir la base de datos adecuada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Módulo 7: Integración de aplicaciones
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrones de integración: idempotencia, reintentos y colas de mensajes fallidos
