En mercadofresco-artefactos hay un ZIP verificado: pasó 214 pruebas, tiene su commit en los metadatos y
lleva la versión en el nombre. Y para llevarlo a producción, Luis sigue haciendo lo de siempre: ssh a la
primera instancia del ASG, scp del ZIP, descomprimir, systemctl restart, mirar la web, repetir en la
segunda. Cuarenta segundos por instancia sirviendo errores, dos versiones conviviendo mientras dura, y si
algo va mal, buscar el ZIP anterior y repetir el ritual con la tienda caída.
AWS CodeDeploy convierte ese ritual en una operación gobernada. Sabe sacar una instancia del grupo de destino antes de tocarla, ejecutar tus scripts en un orden definido, comprobar que la aplicación responde antes de devolverle tráfico, avanzar poco a poco por la flota y —lo que de verdad importa— volver atrás sola cuando una alarma de CloudWatch salta durante el despliegue. Es la lección que cierra el cuarto problema del curso: los despliegues arriesgados.
Aviso de coste. CodeDeploy es gratuito para EC2, Lambda y ECS; solo cobra 0,02 USD por actualización de instancia en servidores locales. Lo que cuesta es lo de alrededor: en blue/green duplicas temporalmente las instancias del ASG —unos 0,09 USD por despliegue de 30 minutos con dos
t3.medium— y las versiones de Lambda no caducan solas. Al final tienes la limpieza. Datos ficticios.
Contenido
- Qué resuelve CodeDeploy y qué no
- Los tres destinos y las estrategias que admite cada uno
- Conceptos: aplicación, grupo de despliegue, revisión y configuración
- El agente en las instancias del ASG
- El
appspec.ymlpara EC2 y los ganchos del ciclo de vida - Los scripts reales de MercadoFresco
- Despliegue en el sitio:
AllAtOnce,HalfAtATimeyOneAtATime - Blue/green con el ASG y los grupos de destino
- El
appspecde Lambda y de ECS - Canario y lineal: desplegar
mercadofresco-cobrar-pago - Reversión automática: la pieza que resuelve el problema 4
- Comparación con la reversión manual y con Route 53 ponderado
- Migraciones de esquema: expansión y contracción
- Observabilidad del despliegue y diagnóstico de un fallo
- Coste y limpieza
- Errores comunes y consejos
- Ejercicios
- Conclusión
Qué resuelve CodeDeploy y qué no
CodeDeploy no construye, no decide cuándo desplegar y no define infraestructura. Hace una cosa: coge una revisión —un ZIP en S3 o una imagen— y la instala en un conjunto de destinos siguiendo un plan, con ganchos donde metes tus scripts y con condiciones de parada.
| Problema del despliegue manual | Qué aporta CodeDeploy |
|---|---|
| La instancia sirve errores mientras se actualiza | La saca del grupo de destino antes de tocarla |
| Dos versiones conviviendo sin control | Controla cuántas instancias hay en cada versión |
| «¿Se reinició bien el servicio?» | ValidateService lo comprueba y falla si no |
| Volver atrás es repetir el ritual a mano | Reversión automática en minutos |
| Nadie sabe qué se desplegó ni cuándo | Historial con revisión, autor y resultado |
| El despliegue es igual en dev que en prod | Grupos de despliegue con configuración distinta |
Y lo que no aporta: no coordina el orden entre servicios distintos —para eso está el pipeline de 08-04—, no migra tu base de datos y no compensa una aplicación que no puede convivir consigo misma en dos versiones. Esa última limitación es el tema de la sección sobre migraciones de esquema.
Los tres destinos y las estrategias que admite cada uno
| Destino | En el sitio | Blue/green | Canario | Lineal | Agente |
|---|---|---|---|---|---|
| EC2 / ASG | Sí | Sí | No | No | Sí |
| Servidores locales | Sí | No | No | No | Sí |
| AWS Lambda | No | Sí (por alias) | Sí | Sí | No |
| Amazon ECS | No | Sí | Sí | Sí | No |
Tres observaciones que evitan confusiones frecuentes. En EC2 no hay canario ni lineal: el
desplazamiento porcentual es cosa de Lambda y ECS, donde el tráfico se reparte por peso de alias o de grupo
de destino; en EC2 lo más parecido es OneAtATime, que es granularidad de instancia, no de porcentaje de
peticiones. En Lambda y ECS no hay despliegue en el sitio: siempre se crea la versión nueva junto a la
vieja y se desplaza tráfico, que es más seguro por construcción. Y el agente solo hace falta en EC2 y
servidores locales; en Lambda y ECS, CodeDeploy habla con la API del servicio.
Conceptos: aplicación, grupo de despliegue, revisión y configuración
Cuatro objetos, y confundirlos causa la mitad de las dudas iniciales:
| Objeto | Qué es | En MercadoFresco |
|---|---|---|
| Aplicación | Contenedor lógico: nombre y plataforma | app-mercadofresco-tienda, app-mercadofresco-cobrar-pago |
| Grupo de despliegue | Dónde y cómo: destinos, estrategia, alarmas, reversión | dg-mercadofresco-tienda-desarrollo / -produccion |
| Revisión | Qué se despliega: el ZIP con su appspec.yml |
s3://mercadofresco-artefactos/tienda/1.5.0-a3f9c21/ |
| Configuración de despliegue | El ritmo y cuánto debe seguir sano | CodeDeployDefault.HalfAtATime, o una propia |
Lo importante es el grupo de despliegue: la misma aplicación y la misma revisión se comportan de forma
muy distinta según el grupo. En desarrollo, AllAtOnce sin alarmas para que sea rápido; en producción,
blue/green con alarmas y reversión automática. Ese es el mecanismo con el que se separan los entornos.
aws deploy create-application --application-name app-mercadofresco-tienda \
--compute-platform Server --profile mercadofresco-dev --region eu-west-1
aws deploy create-deployment-group \
--application-name app-mercadofresco-tienda \
--deployment-group-name dg-mercadofresco-tienda-produccion \
--service-role-arn arn:aws:iam::111122223333:role/rol-codedeploy-mercadofresco \
--auto-scaling-groups asg-mercadofresco-tienda \
--deployment-config-name CodeDeployDefault.HalfAtATime \
--load-balancer-info '{"targetGroupInfoList":[{"name":"tg-mercadofresco-tienda"}]}' \
--alarm-configuration '{"enabled": true, "ignorePollAlarmFailure": false,
"alarms": [{"name":"mercadofresco-alb-latencia-alta"},
{"name":"mercadofresco-pedidos-fallidos"}]}' \
--auto-rollback-configuration '{"enabled": true,
"events": ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]}' \
--profile mercadofresco-dev --region eu-west-1Fíjate en ignorePollAlarmFailure: false: si CodeDeploy no consigue consultar una alarma, el
despliegue falla en lugar de continuar a ciegas. Es lo correcto —si el mecanismo de seguridad no responde,
no sigas— pero produce fallos desconcertantes cuando al rol le falta cloudwatch:DescribeAlarms.
El agente en las instancias del ASG
El agente es un proceso que sondea CodeDeploy, descarga la revisión y ejecuta los ganchos. Sin él, la
instancia se queda en Pending hasta el timeout y el despliegue falla con un mensaje poco explicativo.
Se puede instalar horneado en la AMI, desde el user data de lt-mercadofresco-tienda, o —lo recomendable—
como asociación de Systems Manager con actualización automática:
aws ssm create-association --name AWSCodeDeployAgentUpdate \
--targets Key=tag:Proyecto,Values=mercadofresco \
--schedule-expression "rate(14 days)" \
--profile mercadofresco-dev --region eu-west-1Esta última es la que usa MercadoFresco porque resuelve el problema real: un agente desactualizado deja de funcionar sin avisar, y actualizarlo a mano en instancias que el ASG crea y destruye es imposible.
Dos requisitos que se olvidan. El rol de la instancia necesita poder leer el artefacto:
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow",
"Action": ["s3:GetObject", "s3:GetObjectVersion", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::mercadofresco-artefactos",
"arn:aws:s3:::mercadofresco-artefactos/tienda/*"] },
{ "Effect": "Allow", "Action": ["kms:Decrypt"],
"Resource": "arn:aws:kms:eu-west-1:111122223333:key/*",
"Condition": {"StringEquals": {"kms:ViaService": "s3.eu-west-1.amazonaws.com"}} }
]
}El segundo bloque hace falta porque el bucket está cifrado con alias/mercadofresco-datos: sin permiso
de KMS, el agente descarga el objeto y falla al descifrarlo, con un error de acceso denegado que parece
un problema de S3. El segundo requisito es salida a internet o VPC endpoints: el agente habla con la
API de CodeDeploy y con S3, y una subred privada sin NAT ni endpoints deja el despliegue colgado. Para
diagnosticar, systemctl status codedeploy-agent y el log en
/var/log/aws/codedeploy-agent/codedeploy-agent.log.
El appspec.yml para EC2
El appspec.yml va en la raíz del ZIP, no en un subdirectorio, y dice al agente qué copiar y qué
ejecutar:
version: 0.0
os: linux
files:
- source: /app
destination: /opt/mercadofresco/app
- source: /scripts
destination: /opt/mercadofresco/scripts
- source: /VERSION
destination: /opt/mercadofresco
# Sin esto, un fichero que ya exista en destino hace fallar el despliegue entero
file_exists_behavior: OVERWRITE
permissions:
- object: /opt/mercadofresco/app
owner: mercadofresco
mode: 640
type: [file]
hooks:
ApplicationStop:
- { location: scripts/parar_servicio.sh, timeout: 60, runas: root }
BeforeInstall:
- { location: scripts/comprobar_espacio.sh, timeout: 30, runas: root }
AfterInstall:
- { location: scripts/instalar_dependencias.sh, timeout: 300, runas: mercadofresco }
- { location: scripts/cargar_configuracion.sh, timeout: 60, runas: mercadofresco }
ApplicationStart:
- { location: scripts/arrancar_servicio.sh, timeout: 120, runas: root }
ValidateService:
- { location: scripts/comprobar_salud.sh, timeout: 180, runas: mercadofresco }
- { location: scripts/calentar_cache.sh, timeout: 120, runas: mercadofresco }file_exists_behavior: OVERWRITE merece un aviso: sin él, el valor por defecto es DISALLOW y el
despliegue falla si un fichero ya existe en el destino. Como el destino casi siempre tiene la versión
anterior, es el primer fallo que se encuentra todo el mundo. RETAIN existe para ficheros que la
aplicación genera y no deben sobrescribirse.
Los ganchos del ciclo de vida, uno a uno
| Gancho | Cuándo | Revisión | Para qué sirve |
|---|---|---|---|
ApplicationStop |
Antes de nada | La ANTERIOR | Parar el servicio limpiamente |
BeforeInstall |
Antes de copiar | La nueva | Comprobaciones previas, copias |
AfterInstall |
Tras copiar | La nueva | Dependencias, permisos, configuración |
ApplicationStart |
Tras instalar | La nueva | Arrancar el servicio |
ValidateService |
El último | La nueva | Comprobar que funciona de verdad |
BeforeAllowTraffic |
Solo blue/green | La nueva | Calentar antes de recibir tráfico |
AfterAllowTraffic |
Solo blue/green | La nueva | Validar ya con tráfico real |
Tres cosas que hay que entender bien. ApplicationStop se ejecuta desde la revisión ANTERIOR, y es la
fuente de un problema clásico: si el despliegue anterior dejó un parar_servicio.sh roto, el despliegue
nuevo falla en el primer gancho aunque el código nuevo esté perfecto —y en el primer despliegue de una
instancia no se ejecuta, porque no hay revisión anterior—. Escríbelo a prueba de balas y haz que termine
con éxito aunque no encuentre el servicio. ValidateService es el gancho que justifica todo lo demás:
sin él, «desplegado» significa «los ficheros están copiados y systemctl no protestó», que no es lo mismo
que «la tienda funciona». Y los timeouts son por gancho, con máximo de una hora: un AfterInstall sin
caché puede pasarse de 300 segundos, y un ValidateService con 10 segundos falla siempre en la primera
instancia, que es la más lenta en calentar.
Los scripts reales de MercadoFresco
Parar el servicio sin fallar si no está:
#!/bin/bash
# scripts/parar_servicio.sh -> ApplicationStop
# OJO: se ejecuta desde la revision ANTERIOR. Tiene que ser a prueba de balas.
set -u # NO ponemos 'set -e': queremos controlar los fallos nosotros
systemctl list-unit-files | grep -q '^mercadofresco.service' || {
echo "El servicio no existe todavia (primer despliegue)."; exit 0; }
systemctl stop mercadofresco
for i in $(seq 1 30); do
systemctl is-active --quiet mercadofresco || {
echo "Parado limpiamente en ${i}s"; exit 0; }
sleep 1
done
echo "No paro en 30s. Forzando."
systemctl kill -s SIGKILL mercadofresco || true
exit 0 # NUNCA fallamos aqui: bloquearia todos los despliegues futurosEl exit 0 final es deliberado: un ApplicationStop que falla deja la instancia en un estado del que solo
se sale recreando el grupo de despliegue.
Comprobar la salud de verdad:
#!/bin/bash
# scripts/comprobar_salud.sh -> ValidateService
set -euo pipefail
for i in $(seq 1 30); do
RESPUESTA=$(curl -sf -m 5 http://localhost:8080/salud 2>/dev/null || echo '{}')
if [ "$(echo "$RESPUESTA" | jq -r '.estado // "ko"')" = "ok" ]; then
# No basta con un 200: comprobamos las dependencias criticas una a una
for dep in aurora redis sqs; do
SALUD=$(echo "$RESPUESTA" | jq -r ".dependencias.${dep} // \"ko\"")
[ "$SALUD" = "ok" ] || { echo "Dependencia ${dep} en estado ${SALUD}"; exit 1; }
done
# Y que la version viva sea la que acabamos de instalar
VERSION_VIVA=$(echo "$RESPUESTA" | jq -r '.version')
[ "$VERSION_VIVA" = "$(cat /opt/mercadofresco/VERSION)" ] || {
echo "Sirve ${VERSION_VIVA}, no la esperada"; exit 1; }
echo "Salud OK, version ${VERSION_VIVA}"; exit 0
fi
echo "Intento ${i}/30"; sleep 5
done
echo "No paso la comprobacion de salud en 150s"
exit 1Este script es el corazón de la seguridad del despliegue, y hace tres comprobaciones que un curl -f /salud simple no hace: que las dependencias críticas respondan —de nada sirve que el proceso esté vivo
si no llega a Aurora—, que la versión que responde sea la que acabas de instalar —detecta el caso en
que el servicio no llegó a reiniciarse— y que todo ocurra dentro de un plazo. Su exit 1 es lo que dispara
la reversión.
Calentar la caché antes de recibir tráfico:
#!/bin/bash
# scripts/calentar_cache.sh -> ValidateService (o BeforeAllowTraffic en blue/green)
set -euo pipefail
curl -sf -m 30 -X POST http://localhost:8080/interno/precargar-catalogo
for sku in FRUT-0012 VERD-0034 PESC-0007 CARN-0021; do
curl -sf -m 5 "http://localhost:8080/productos/${sku}" > /dev/null
done
echo "Cache caliente"Sin este paso, la primera oleada de tráfico encuentra la caché vacía, va a Aurora, y
TiempoConfirmacionPedido se dispara durante dos minutos. En el pico del viernes con 900 pedidos/hora, eso
es exactamente lo que hace saltar mercadofresco-alb-latencia-alta y revierte un despliegue que estaba
bien.
Despliegue en el sitio: AllAtOnce, HalfAtATime y OneAtATime
| Configuración | A la vez | Capacidad mínima | Duración (4 instancias) | Cuándo |
|---|---|---|---|---|
AllAtOnce |
Todas | 0 | ~3 min | Desarrollo. Nunca en producción |
HalfAtATime |
50 % | 50 % | ~6 min | Producción con margen de capacidad |
OneAtATime |
1 | N-1 | ~12 min | Máxima prudencia; flotas pequeñas |
AllAtOnce en producción es una caída, no un despliegue. Todas las instancias fuera a la vez es lo que
se pretende evitar; y si falla, no queda ninguna instancia con la versión buena a la que volver.
Se puede definir una configuración propia con aws deploy create-deployment-config y
--minimum-healthy-hosts type=FLEET_PERCENT,value=90. Pero cuidado: con ese 90 % y una flota de 2
instancias, el mínimo sano es 2 (redondea hacia arriba) y ningún despliegue puede empezar; con flotas
pequeñas usa HOST_COUNT en lugar de porcentajes.
Blue/green con el ASG y los grupos de destino
En blue/green no se toca ninguna instancia existente: se crean nuevas, se comprueban, se les pasa el tráfico y solo entonces se terminan las antiguas.
flowchart TB
ALB[alb-mercadofresco-tienda] -->|100% antes| TGA[tg-mercadofresco-tienda]
ALB -.->|100% despues| TGV[tg-mercadofresco-tienda-verde]
TGA --> AZUL[Azul: v1.4.3<br/>i-aaa1, i-aaa2]
TGV --> VERDE[Verde: v1.5.0<br/>i-vvv1, i-vvv2]
VERDE --> H[BeforeAllowTraffic:<br/>calentar cache]
H --> S{Estado OK?}
S -->|No| F[Terminar verde<br/>Azul sigue sirviendo]
S -->|Si| C[Cambiar oyente del ALB]
C --> W[Esperar 30 min<br/>vigilando alarmas]
W -->|Alarma| R[REVERSION:<br/>volver a azul en 90 s]
W -->|Todo bien| T[Terminar azul]
aws deploy update-deployment-group \
--application-name app-mercadofresco-tienda \
--current-deployment-group-name dg-mercadofresco-tienda-produccion \
--deployment-style '{"deploymentType":"BLUE_GREEN",
"deploymentOption":"WITH_TRAFFIC_CONTROL"}' \
--blue-green-deployment-configuration '{
"terminateBlueInstancesOnDeploymentSuccess": {
"action": "TERMINATE", "terminationWaitTimeInMinutes": 30 },
"deploymentReadyOption": { "actionOnTimeout": "CONTINUE_DEPLOYMENT" },
"greenFleetProvisioningOption": { "action": "COPY_AUTO_SCALING_GROUP" }}' \
--profile mercadofresco-dev --region eu-west-1terminationWaitTimeInMinutes: 30 es la ventana en la que el entorno azul sigue existiendo, apagado
pero íntegro, tras el cambio de tráfico: es lo que hace que la reversión sea de noventa segundos, porque
solo hay que devolver el oyente al grupo de destino azul —a 0 ahorras céntimos y volver atrás pasa a ser un
despliegue completo de diez minutos—. actionOnTimeout: CONTINUE_DEPLOYMENT hace el cambio de tráfico
automático; la alternativa STOP_DEPLOYMENT espera a que alguien pulse «reencaminar tráfico», útil el
primer día e insostenible como práctica. Y COPY_AUTO_SCALING_GROUP crea un ASG nuevo copiando
lt-mercadofresco-tienda, frente a DISCOVER_EXISTING con instancias ya etiquetadas.
| Aspecto | En el sitio | Blue/green |
|---|---|---|
| Coste durante el despliegue | Ninguno | Capacidad duplicada |
| Tiempo de reversión | Despliegue completo (~10 min) | ~90 segundos |
| Riesgo para la versión estable | Se sobrescribe | Intacta hasta el final |
| Duración total | 6-12 min | 15-40 min |
| Estado local en disco | Se conserva | Se pierde: instancias nuevas |
MercadoFresco usa en el sitio con HalfAtATime en desarrollo y blue/green en producción. Duplicar
dos t3.medium media hora son unos nueve céntimos por despliegue; noventa segundos de reversión valen
bastante más que eso.
El appspec de Lambda y de ECS
Para Lambda y ECS el appspec es otra cosa: no copia ficheros, declara qué versión debe recibir el
tráfico. Admite YAML o JSON.
# appspec-lambda.yml
version: 0.0
Resources:
- mercadofresco-cobrar-pago:
Type: AWS::Lambda::Function
Properties:
Name: mercadofresco-cobrar-pago
Alias: produccion
CurrentVersion: "7" # la que sirve ahora
TargetVersion: "8" # la nueva
Hooks:
- BeforeAllowTraffic: mercadofresco-validar-antes-de-trafico
- AfterAllowTraffic: mercadofresco-validar-despues-de-traficoDiferencia importante: en Lambda los ganchos son funciones Lambda, no scripts, y tienen una obligación
que se olvida siempre: comunicar su resultado con PutLifecycleEventHookExecutionStatus, o CodeDeploy
espera hasta el timeout y falla.
import boto3, json, os
cd = boto3.client("codedeploy")
lam = boto3.client("lambda")
def handler(event, context):
id_despliegue = event["DeploymentId"]
estado = "Failed"
try:
# Invocamos la version NUEVA directamente, antes de que reciba trafico real
r = lam.invoke(
FunctionName=f"mercadofresco-cobrar-pago:{os.environ['VERSION_NUEVA']}",
Payload=json.dumps({"modo": "prueba_humo", "importe_eur": 1.00,
"clave_idempotencia": f"humo-{id_despliegue}"}))
cuerpo = json.loads(r["Payload"].read())
if r["StatusCode"] == 200 and cuerpo.get("estado") == "cobrado":
estado = "Succeeded"
except Exception as e:
print(f"Prueba de humo fallida: {e}")
# SIN esta llamada, CodeDeploy espera hasta el timeout y falla el despliegue
cd.put_lifecycle_event_hook_execution_status(
deploymentId=id_despliegue,
lifecycleEventHookExecutionId=event["LifecycleEventHookExecutionId"],
status=estado)
return {"status": estado}Fíjate en la clave_idempotencia derivada del identificador del despliegue: la prueba de humo pasa por el
mismo camino que un cobro real, y sin una clave única cada despliegue reutilizaría la anterior. Es la
idempotencia de 07-05 aplicada al propio despliegue. En ECS el appspec referencia la definición de
tarea y el contenedor; el detalle de ECS es el módulo 10.
Canario y lineal: desplegar mercadofresco-cobrar-pago
Aquí sí hay desplazamiento porcentual de tráfico, la mejor red de seguridad que ofrece CodeDeploy.
| Configuración | Cómo desplaza | Duración | Cuándo |
|---|---|---|---|
AllAtOnce |
100 % de golpe | Segundos | Desarrollo |
Canary10Percent5Minutes |
10 %, espera 5 min, resto | ~5 min | Cobros: pocas peticiones malas |
Canary10Percent30Minutes |
10 %, espera 30 min, resto | ~30 min | Cambios de riesgo alto |
Linear10PercentEvery1Minute |
+10 % cada minuto | ~10 min | Degradación gradual |
Canario o lineal, con criterio. El canario mantiene un porcentaje pequeño un rato y luego salta al
100 %: si el fallo es evidente, solo el 10 % lo sufre y poco tiempo. El lineal sube poco a poco y detecta
mejor las degradaciones que dependen de la carga —una fuga de memoria, un pool que se agota— pero expone al
50 % antes de la mitad del despliegue. Para mercadofresco-cobrar-pago, Marta elige
Canary10Percent5Minutes: un fallo en el cobro es evidente en segundos, no gradual, y con 900
pedidos/hora en el pico, cinco minutos al 10 % son unos 7 pedidos expuestos.
El mecanismo se apoya en versiones y alias, que vimos en 02-05: mercadofresco-cobrar-pago:produccion
apunta a la versión 7, y CodeDeploy le añade un peso hacia la 8 subiéndolo según la configuración. Todo lo
que invoca a la función usa el alias y no se entera de nada.
Se lanza con aws deploy create-deployment y
--deployment-config-name CodeDeployDefault.LambdaCanary10Percent5Minutes, pasando el appspec en
--revision. La alarma que lo vigila va sobre los errores del alias, no de la función entera:
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-cobrar-pago-errores-canario \
--namespace AWS/Lambda --metric-name Errors --statistic Sum \
--dimensions Name=FunctionName,Value=mercadofresco-cobrar-pago \
Name=Resource,Value=mercadofresco-cobrar-pago:produccion \
--period 60 --evaluation-periods 1 --threshold 3 \
--comparison-operator GreaterThanThreshold --treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1El periodo de 60 segundos y un solo periodo de evaluación son deliberados: con una ventana de 5 minutos para el canario, una alarma de 3 periodos de 5 minutos saltaría cuando el despliegue ya está al 100 %. La alarma tiene que ser más rápida que el despliegue que vigila.
Reversión automática: la pieza que resuelve el problema 4
Esta es la sección por la que existe la lección. CodeDeploy revierte solo en tres situaciones, declaradas
en autoRollbackConfiguration: DEPLOYMENT_FAILURE cuando un gancho devuelve distinto de 0 o expira,
DEPLOYMENT_STOP_ON_ALARM cuando una alarma de alarmConfiguration pasa a ALARM durante el despliegue,
y DEPLOYMENT_STOP_ON_REQUEST cuando alguien lo para a mano.
Lo importante es entender qué significa «revertir» en cada modalidad, porque no es lo mismo. En el sitio, CodeDeploy lanza un despliegue nuevo con la revisión anterior: completo, con sus ganchos y sus minutos. En blue/green dentro de la ventana de espera, devuelve el oyente del ALB al grupo de destino azul: noventa segundos, y las instancias azules ni se enteraron. En Lambda o ECS, devuelve el peso del alias a la versión anterior: segundos.
| Alarma | Qué detecta | Por qué está aquí |
|---|---|---|
mercadofresco-alb-latencia-alta |
p95 > 1.500 ms, 2 periodos de 60 s | Una versión lenta es una versión rota |
mercadofresco-pedidos-fallidos |
DLQ > 5 mensajes en 5 min | El consumidor nuevo no procesa bien |
mercadofresco-tienda-degradada |
Compuesta (05-01) | Visión de conjunto |
Elegir las alarmas es la decisión de diseño de esta sección, y deben cumplir tres condiciones.
Detectar lo que un despliegue malo provoca: una alarma de coste no pinta nada aquí; una de latencia o
de 5xx, sí. Ser más rápidas que el despliegue, porque una de 3 periodos de 5 minutos no protege un
despliegue de 10. Y no saltar por causas ajenas: si mercadofresco-alb-latencia-alta salta cada viernes
a las 19:00 por el pico normal, revertirá despliegues correctos y el equipo acabará desactivando la
reversión. Míralas un mes antes de conectarlas.
El estado inicial de la alarma importa. Si al empezar ya está en ALARM, CodeDeploy no arranca: es
correcto —no despliegues sobre un sistema que ya está mal— pero desconcierta la primera vez.
Comparación con la reversión manual y con Route 53 ponderado
| Mecanismo | Tiempo de reversión | Automático | Granularidad |
|---|---|---|---|
| Manual por SSH (lo de hoy) | 10-30 min, con nervios | No | Instancia |
| CodeDeploy en el sitio | ~10 min | Sí | Instancia |
| CodeDeploy blue/green | ~90 s | Sí | Entorno completo |
| CodeDeploy canario (Lambda) | Segundos | Sí | % de invocaciones |
| Route 53 ponderado (03-05) | 60 s + TTL | No de serie | % de resoluciones DNS |
Merece la pena comparar con Route 53 ponderado, que en 03-05 también repartía tráfico y podría parecer alternativo. No lo es, por dos razones. El TTL del DNS: aunque pongas el peso a 0 al instante, los resolutores y navegadores siguen usando la respuesta cacheada durante el TTL, así que «revertir» tarda minutos y nunca es completo. Y que el reparto es de resoluciones, no de peticiones: un cliente que resuelve una vez y mantiene la conexión se queda donde cayó. Route 53 ponderado es excelente para mover tráfico entre regiones; CodeDeploy es el mecanismo correcto para desplegar una versión, porque opera sobre el balanceador y es inmediato y completo.
Migraciones de esquema: expansión y contracción
Aquí está la advertencia más importante de la lección, y no es un detalle técnico sino una regla de proceso: el despliegue del código y el cambio del esquema no deben ir en el mismo paso.
El razonamiento es sencillo. Durante cualquier despliegue gradual conviven dos versiones del código contra
una sola base de datos: si el AfterInstall de la nueva ejecuta ALTER TABLE pedidos DROP COLUMN direccion_antigua, la vieja —que sigue sirviendo la mitad del tráfico— empieza a fallar. Y si el
despliegue revierte, la reversión no deshace el ALTER TABLE: te quedas con el código viejo y el esquema
nuevo, el peor de los estados posibles. La respuesta es expansión y contracción, en tres despliegues:
| Fase | Qué se hace | Compatible con | Cuándo |
|---|---|---|---|
| 1. Expandir | Añadir lo nuevo sin quitar nada: columna NULLABLE |
Ambas versiones | Antes del código |
| 2. Migrar | Código que escribe en ambos y lee del nuevo con respaldo | Ambas versiones | Despliegue normal |
| 3. Contraer | Quitar lo viejo | Solo la nueva | Días o semanas después |
Aplicado a la franja horaria que Luis va a añadir:
-- FASE 1 (expandir): ANTES de desplegar el codigo. Sin NOT NULL, sin DEFAULT y sin
-- indice: no reescribe la tabla ni bloquea. La 1.4.3 no conoce la columna y la ignora.
ALTER TABLE pedidos ADD COLUMN franja_entrega VARCHAR(10) NULL;
-- FASE 3 (contraer): semanas despues, cuando NINGUNA instancia sirve ya la 1.4.3
ALTER TABLE pedidos ALTER COLUMN franja_entrega SET NOT NULL;
CREATE INDEX CONCURRENTLY idx_pedidos_franja ON pedidos(franja_entrega);Cuatro reglas prácticas que se derivan de esto. Nunca ejecutes migraciones en un gancho de CodeDeploy:
el gancho corre en cada instancia, así que con cuatro instancias tienes cuatro ALTER TABLE
simultáneos, y si falla a mitad el despliegue revierte pero el esquema no. La migración es un paso propio
del pipeline, antes del despliegue de la aplicación; eso es 08-04. Toda migración de expansión debe ser
reversible o inocua: una columna NULLABLE sobra pero no molesta si el código revierte. Y
CREATE INDEX CONCURRENTLY para no bloquear la tabla, fuera del despliegue. El recorrido completo, con
el consumidor de la cola y el contrato del evento, es la lección 08-05.
Observabilidad del despliegue y diagnóstico de un fallo
CodeDeploy publica en EventBridge cada cambio de estado, así que todo lo de 07-03 se aplica, con un
patrón sobre source: ["aws.codedeploy"], detail-type: ["CodeDeploy Deployment State-change Notification"] y detail.state: ["FAILURE", "STOP"].
Con destino a alertas-mercadofresco y una transformación de entrada que deje un mensaje legible: qué
aplicación, qué versión, qué estado y el enlace a la consola. Un despliegue que revierte solo a las 19:15
de un viernes es una buena noticia, pero solo si alguien se entera. El diagnóstico de un fallo:
# Que despliegue fallo y por que; luego, que instancia y que gancho concreto
aws deploy get-deployment --deployment-id d-A1B2C3D4E \
--query 'deploymentInfo.[status,errorInformation.code,errorInformation.message]' --output table
aws deploy get-deployment-target --deployment-id d-A1B2C3D4E --target-id i-0abc123def456 \
--query 'deploymentTarget.instanceTarget.lifecycleEvents[?status==`Failed`]'| Código de error | Qué significa | Dónde mirar |
|---|---|---|
HEALTH_CONSTRAINTS |
Menos instancias sanas de las exigidas | ¿Porcentaje con flota pequeña? |
SCRIPT_FAILED |
Un gancho devolvió distinto de 0 | El log del agente en esa instancia |
SCRIPT_TIMED_OUT |
Un gancho superó su timeout |
Sube el timeout o acelera el script |
NO_INSTANCES |
Ninguna instancia encaja | Etiquetas o nombre del ASG |
AGENT_ISSUE_* |
El agente no responde | ¿Está vivo? ¿Hay salida de red? |
ALARM_ACTIVE |
Una alarma estaba en ALARM al empezar |
Arregla el sistema antes de desplegar |
Coste y limpieza
| Concepto | Coste |
|---|---|
| CodeDeploy en EC2, Lambda y ECS | 0 USD (0,02 USD/instancia en servidores locales) |
| Capacidad duplicada en blue/green | ~0,09 USD por despliegue (2 × t3.medium, 30 min) |
| Almacenamiento de versiones de Lambda | Cuota de 75 GB por región |
| Total de MercadoFresco | ~2 USD/mes con 20 despliegues |
# Las versiones antiguas de Lambda NO se borran solas y consumen cuota
aws lambda list-versions-by-function --function-name mercadofresco-cobrar-pago \
--query 'Versions[?Version!=`$LATEST`].[Version,LastModified]' --output table
aws lambda delete-function --function-name mercadofresco-cobrar-pago:3
# ASG huerfanos de un blue/green fallido: instancias corriendo y facturando
aws autoscaling describe-auto-scaling-groups \
--query "AutoScalingGroups[?starts_with(AutoScalingGroupName,'CodeDeploy_')].[AutoScalingGroupName,DesiredCapacity]"Los ASG huérfanos son el coste oculto de esta lección. Un blue/green interrumpido a mitad puede dejar un
ASG con prefijo CodeDeploy_ y sus instancias vivas, sirviendo a nadie y facturando. Revísalos tras cada
despliegue fallido.
Errores Comunes y Consejos
Un ApplicationStop que puede fallar. Se ejecuta desde la revisión anterior y bloquea todos los
despliegues futuros de esa instancia. Termina siempre con exit 0.
Olvidar file_exists_behavior: OVERWRITE. El valor por defecto DISALLOW hace fallar el despliegue en
cuanto un fichero ya existe en destino, que es siempre a partir del segundo.
Un ValidateService que solo comprueba que el proceso está vivo. Uno que no llega a Aurora pasa la
comprobación y recibe tráfico: valida dependencias y versión.
Ejecutar migraciones de base de datos en un gancho. Corre en cada instancia, no es transaccional respecto al despliegue, y la reversión no lo deshace. Es el error más caro del módulo.
AllAtOnce en producción, que no es un despliegue sino una caída programada; y porcentajes de flota
sana con flotas pequeñas, porque FLEET_PERCENT=90 con 2 instancias exige 2 sanas y ningún despliegue
puede empezar. Usa HOST_COUNT.
terminationWaitTimeInMinutes: 0 en blue/green. Ahorras céntimos y pierdes la reversión de 90
segundos, que es la razón por la que elegiste blue/green.
Alarmas más lentas que el despliegue, que saltan cuando ya está todo desplegado; o alarmas con falsos positivos conectadas a la reversión, que revertirán despliegues buenos hasta que el equipo desactive la reversión entera. Míralas un mes antes de conectarlas.
Olvidar PutLifecycleEventHookExecutionStatus en un gancho de Lambda, que hace esperar al timeout con
un mensaje que no menciona la llamada que falta; o falta de permisos de KMS en el rol de la instancia,
que impide descifrar el ZIP con un error que parece de S3.
Consejo: prueba la reversión a propósito. Despliega una versión con ValidateService que devuelva 1 y
cronometra: una reversión que nadie ha ensayado es una hipótesis. Ten un /salud que diga la verdad,
con el estado de cada dependencia y la versión que sirve, porque es la pieza sobre la que se apoya todo lo
demás. Y despliega primero en desarrollo: los ganchos rotos se descubren igual de bien allí.
Ejercicios
Ejercicio 1: el despliegue que se quedó atascado
Luis despliega la 1.5.0 en dg-mercadofresco-tienda-produccion (en el sitio, HalfAtATime, 4 instancias).
La primera mitad va bien; la segunda falla con SCRIPT_FAILED en ApplicationStop. La reversión se lanza y
también falla en ApplicationStop. Resultado: 2 instancias con 1.5.0, 2 con 1.4.3 y ningún despliegue
posible. Investigando, ve que el parar_servicio.sh de la 1.4.3 hace set -e y systemctl stop mercadofresco && rm /var/run/mercadofresco.pid, y que ese fichero PID ya no existe desde la 1.4.2.
Responde: (a) por qué la reversión falla en el mismo sitio; (b) por qué las dos primeras instancias sí funcionaron; (c) cómo desbloqueas la situación ahora mismo, con los comandos; (d) cómo evitas que vuelva a ocurrir; (e) qué habría cambiado si el grupo fuera blue/green.
Ejercicio 2: elegir estrategia para tres componentes
Para cada componente indica destino, configuración de despliegue, alarmas conectadas a la reversión y
justificación. (a) La tienda web en asg-mercadofresco-tienda (2-4 instancias), con pico los viernes
de 17:00 a 21:00 y 900 pedidos/hora, donde un error 500 es una venta perdida. (b) La Lambda
mercadofresco-cobrar-pago, unas 900 invocaciones/hora en pico, ya idempotente (07-05), donde un fallo
puede cobrar dos veces o no cobrar. (c) Los trabajadores de asg-mercadofresco-trabajadores, que
consumen cola-mercadofresco-pedidos, no reciben tráfico del ALB y si se paran 5 minutos la cola crece y
se vacía después.
Ejercicio 3: la migración que rompió la reversión
El martes a las 11:00, Luis despliega la 1.6.0 en blue/green. La 1.6.0 renombra la columna direccion a
direccion_entrega, y el AfterInstall ejecuta
ALTER TABLE pedidos RENAME COLUMN direccion TO direccion_entrega;. El verde arranca bien, pasa
ValidateService y recibe el 100 % del tráfico. A los 8 minutos, mercadofresco-pedidos-fallidos salta: el
consumidor de la cola, que aún es 1.5.2, falla en cada mensaje. CodeDeploy revierte al azul en 90 segundos.
Y entonces la tienda entera deja de funcionar.
Responde: (a) por qué la reversión empeoró las cosas; (b) cuántas veces se ejecutó el ALTER TABLE
y qué pasó en la segunda; (c) el plan de recuperación inmediata; (d) cómo debería haberse hecho el
cambio, con las fases y qué se despliega en cada una; (e) qué control automático habría impedido que
esto llegara a producción.
Soluciones
Solución 1
(a) Porque ApplicationStop se ejecuta siempre desde la revisión anterior instalada en la
instancia, y en las instancias 3 y 4 la revisión anterior es la 1.4.3, cuyo script está roto. La
reversión es un despliegue nuevo de la 1.4.3, y su primer gancho vuelve a ser el ApplicationStop de lo
que hay instalado —que sigue siendo la 1.4.3 rota—. Se ejecuta el mismo script defectuoso y falla igual.
El bucle es exactamente por lo que este gancho debe ser a prueba de balas.
(b) Por casualidad, y merece la pena entenderla: en las instancias 1 y 2 el fichero PID existía
—eran más antiguas y arrastraban uno de la 1.4.1—, así que rm devolvió 0 y el script terminó bien. En las
3 y 4, recreadas por el ASG con la 1.4.3 limpia, no había fichero, rm devolvió 1 y set -e abortó. Un
despliegue que funciona en media flota y falla en la otra media casi siempre señala estado divergente entre
instancias, y eso ya es un problema en sí: las instancias de un ASG deberían ser indistinguibles.
(c) Desbloquear. El camino rápido, si la tienda está sirviendo mal, es arreglar el script a mano en
las instancias afectadas por Session Manager, editando el
deployment-root/<id>/deployment-archive/scripts/parar_servicio.sh, y relanzar. El limpio es saltarse el
gancho una sola vez: en la consola se puede lanzar un despliegue marcando la opción de omitir
ApplicationStop, BeforeBlockTraffic y AfterBlockTraffic, pensada exactamente para este caso. La
alternativa equivalente por CLI es borrar y recrear el grupo de despliegue, porque eso elimina la
referencia a la última revisión conocida y ApplicationStop deja de ejecutarse:
aws deploy delete-deployment-group --application-name app-mercadofresco-tienda \
--deployment-group-name dg-mercadofresco-tienda-produccion \
--profile mercadofresco-dev --region eu-west-1
# ...recrearlo, y desplegar la 1.4.3 de nuevo con create-deployment.(d) Tres medidas. En el script, quitar set -e, poner exit 0 incondicional al final y usar
rm -f, que no falla si el fichero no existe. En el proceso, una prueba de los ganchos en CodeBuild:
un contenedor que ejecute cada script contra un sistema limpio y compruebe que devuelven 0 en los casos
«servicio no existe», «servicio parado» y «servicio corriendo». Y desplegar siempre primero en
desarrollo, donde este fallo habría aparecido sin consecuencias.
(e) Con blue/green no habría pasado nada: las instancias verdes son nuevas y no tienen revisión
anterior, así que ApplicationStop ni se ejecuta. Si el despliegue fallara por otro motivo, el azul
—intacto, con la 1.4.3 funcionando— seguiría sirviendo el 100 % y la reversión sería devolver el oyente del
ALB. Es un argumento a favor de blue/green que no aparece en las tablas: elimina de raíz toda una clase de
fallos, los heredados del despliegue anterior.
Solución 2
(a) La tienda web. EC2 con el ASG, blue/green, terminationWaitTimeInMinutes: 30 y
actionOnTimeout: CONTINUE_DEPLOYMENT. Alarmas: mercadofresco-alb-latencia-alta y
mercadofresco-tienda-degradada. Justificación: con 2-4 instancias, un despliegue en el sitio deja la
mitad de la capacidad fuera justo cuando más falta hace; blue/green mantiene el 100 % durante todo el
proceso, y la reversión de 90 segundos es lo que permite desplegar sin miedo. Regla operativa añadida:
ningún despliegue de la tienda entre las 16:00 y las 22:00 del viernes, no por desconfiar del mecanismo
sino porque equivocarse en el pico sale desproporcionadamente caro. Ganchos: BeforeAllowTraffic calienta
la caché —clave, o la primera oleada dispara la latencia y revierte un despliegue bueno— y
AfterAllowTraffic hace una prueba de humo con tráfico real.
(b) La Lambda de cobros. Canary10Percent5Minutes, con
mercadofresco-cobrar-pago-errores-canario (periodo 60 s, 1 evaluación, umbral 3) y una sobre Duration
p99. Justificación: un fallo de cobro es evidente en segundos, no gradual, así que el canario detecta
antes y expone menos que el lineal. Con 900 invocaciones/hora, 5 minutos al 10 % son unos 7 pedidos
expuestos, y la idempotencia de 07-05 evita que un reintento tras la reversión duplique el cobro. Más un
BeforeAllowTraffic que invoca la versión nueva con un pago de prueba de 1,00 €. El detalle que no hay que
olvidar: la alarma debe llevar la dimensión Resource del alias, o mediría los errores de todas las
versiones juntas y se contaminaría con las invocaciones de la vieja.
(c) Los trabajadores. En el sitio con AllAtOnce, sin alarmas de latencia, con
mercadofresco-pedidos-fallidos conectada a la reversión. Justificación: aquí el razonamiento cambia
por completo, y ese es el interés del apartado. No reciben tráfico del ALB, así que no hay peticiones que
puedan fallar: si se paran cinco minutos, la cola crece y se vacía después, que es exactamente la
amortiguación que buscábamos en 07-01. Blue/green sería complejidad y coste sin beneficio. Lo que sí hay
que garantizar es un ApplicationStop limpio, que deje terminar el mensaje en curso y no lo borre a
medias, o quedarán mensajes en vuelo reapareciendo al expirar la visibilidad. Y vigilar
ApproximateAgeOfOldestMessage tras el despliegue: si no baja en diez minutos, el consumidor nuevo no
procesa aunque el proceso esté vivo.
Solución 3
(a) Porque la reversión devolvió el código a la 1.5.2 pero no deshizo el esquema. La 1.5.2
consulta direccion, que ya no existe. Antes de la reversión al menos la tienda funcionaba con la 1.6.0 y
solo fallaba el consumidor; después falla todo, porque el 100 % del tráfico lo sirve un código
incompatible con el esquema. Es la trampa que la lección advierte: la reversión revierte artefactos, no
bases de datos, y un cambio de esquema destructivo convierte tu red de seguridad en un acelerador del
incidente.
(b) Se ejecutó una vez por instancia verde, es decir 2 veces. La primera tuvo éxito; la segunda
falló con ERROR: column "direccion" does not exist, porque el renombrado ya estaba hecho. Que el
despliegue no se detuviera ahí depende de si el script tragaba el error. Este comportamiento es la
demostración de por qué las migraciones no van en ganchos: no son idempotentes y se ejecutan tantas
veces como instancias haya.
(c) Recuperación inmediata, con la tienda caída, así que rápido y sin elegancias. Converger hacia
adelante, no hacia atrás: el esquema ya está en el estado nuevo y volver a renombrarlo es otra migración
con riesgo, así que redesplegar la 1.6.0, que sí es compatible, y recuperar la tienda. Después,
desplegar la versión compatible del consumidor, que sigue en 1.5.2 y sigue fallando; mientras tanto los
mensajes se acumulan en cola-mercadofresco-pedidos y acabarán en la DLQ, pero no se pierden. Con la
tienda sirviendo, aplicar el runbook de la DLQ de 07-05 —contener, clasificar, reprocesar—. Y un post-mortem
sin buscar culpables, con la regla de proceso escrita.
(d) Cómo debería haberse hecho, en tres despliegues separados por días. Despliegue 1 (expandir),
lunes: solo esquema, sin código nuevo —ALTER TABLE pedidos ADD COLUMN direccion_entrega VARCHAR(255) NULL; más un UPDATE que copie los valores—; la 1.5.2 sigue funcionando porque no conoce la columna nueva
y no le molesta. Despliegue 2 (migrar), martes: el código 1.6.0 escribe en las dos columnas y lee
de direccion_entrega con respaldo en direccion, lo que lo hace compatible en ambos sentidos —convive
con la 1.5.2 durante el despliegue gradual y funciona si hay que revertir—; aquí el consumidor de la cola se
despliega antes que la tienda, para que sepa leer el campo nuevo antes de que empiece a llegar, que es
el orden productor/consumidor que cierra 08-05. Despliegue 3 (contraer), la semana siguiente, cuando
ninguna instancia sirve ya la 1.5.2: ALTER TABLE pedidos DROP COLUMN direccion;. Y cada fase de esquema
como paso propio del pipeline, ejecutado una sola vez desde una tarea dedicada, nunca desde un gancho.
(e) Tres controles, de más barato a más caro. Un análisis de las migraciones en CodeBuild que falle
la construcción si detecta DROP COLUMN, RENAME COLUMN, ALTER COLUMN ... NOT NULL o DROP TABLE sin
una etiqueta explícita de «contracción aprobada»: son treinta líneas de script y habrían parado esto en
seco. Una prueba de compatibilidad hacia atrás que arranque la versión anterior del código contra el
esquema nuevo y ejecute la suite de humo —simula directamente la reversión, y es sorprendentemente rara
en la práctica—. Y revisión obligatoria de todo cambio en migraciones/ mediante una regla de
propietarios de código, para que ningún cambio de esquema entre con una aprobación distraída.
Conclusión
El ritual de Luis ha terminado. Ya no hay ssh, ni scp, ni cuarenta segundos de errores por instancia,
ni dos versiones conviviendo sin control. Hay una aplicación app-mercadofresco-tienda, dos grupos de
despliegue que hacen que la misma revisión se comporte de forma distinta en desarrollo y en producción, un
artefacto identificado en mercadofresco-artefactos y un plan que CodeDeploy ejecuta igual todas las
veces. Y sobre todo, algo que no existía: una vuelta atrás que no depende de que alguien esté mirando.
Conoces los tres destinos y qué admite cada uno —en EC2 no hay canario ni lineal; en Lambda y ECS no hay
despliegue en el sitio— y sabes que el agente solo hace falta en EC2, instalado como asociación de Systems
Manager para que se actualice solo. Dominas el appspec.yml con sus files, permissions y hooks,
incluido file_exists_behavior: OVERWRITE, el primer fallo que encuentra todo el mundo. Y conoces los
ganchos uno a uno, con las dos verdades que más incidentes evitan: que ApplicationStop se ejecuta desde
la revisión anterior —y por eso debe terminar siempre con exit 0— y que ValidateService es el gancho
que justifica todo lo demás, comprobando dependencias y versión en lugar de conformarse con un proceso
vivo.
Sabes elegir el ritmo: AllAtOnce solo en desarrollo, HalfAtATime con margen de capacidad, OneAtATime
cuando la prudencia importa más que el reloj, y por qué un FLEET_PERCENT alto con dos instancias impide
arrancar cualquier despliegue. Tienes el blue/green con su terminationWaitTimeInMinutes de 30 minutos
—literalmente el precio de la reversión de 90 segundos— y su ventaja escondida: al ser instancias nuevas,
elimina de raíz los fallos heredados del despliegue anterior. Y el canario de mercadofresco-cobrar-pago
con Canary10Percent5Minutes, su alarma sobre la dimensión Resource del alias y periodos más cortos que
el propio despliegue, porque una alarma más lenta que lo que vigila no protege nada.
La pieza central es la reversión automática, y ahora sabes qué significa en cada caso: un despliegue completo en el sitio, noventa segundos en blue/green dentro de la ventana, segundos en Lambda. Sabes que elegir las alarmas es una decisión de diseño con tres condiciones —que detecten lo que un despliegue malo provoca, que sean más rápidas que el despliegue y que no tengan falsos positivos, porque una alarma ruidosa acaba con el equipo desactivando la reversión entera—. Y por qué Route 53 ponderado no sirve para esto: el TTL del DNS hace que revertir sea lento e incompleto.
Y te llevas la advertencia más importante del módulo, que no es técnica sino de proceso: el despliegue de
la aplicación y el cambio de esquema no van en el mismo paso. Porque conviven dos versiones contra una
sola base de datos, porque un gancho se ejecuta una vez por instancia, y sobre todo porque la reversión
revierte artefactos, no bases de datos —un RENAME COLUMN mal colocado convierte tu red de seguridad en
un acelerador del incidente—. La respuesta es expansión y contracción en tres despliegues separados.
Y ahí está lo que falta. Cada pieza funciona, pero nadie las ha encadenado. Luis todavía tiene que
acordarse de lanzar la construcción al fusionar, copiar la ruta del artefacto, escribir
aws deploy create-deployment con el bucket y la clave correctos, esperar, mirar si fue bien y repetirlo
para el entorno siguiente. El paso de esquema se ejecuta a mano. Nadie comprueba que lo que se despliega en
producción sea exactamente lo que se validó en preproducción, ni existe un momento en el que Marta apruebe
formalmente que un cambio salga. Herramientas excelentes y cero orquestación: seguimos dependiendo de
que una persona recuerde la secuencia un viernes por la tarde.
En 08-04, «AWS CodePipeline», se encadena todo. Veremos la diferencia entre entrega continua y
despliegue continuo y por qué MercadoFresco elige la primera, con aprobación manual antes de producción;
la estructura de pipelines, etapas, acciones y artefactos que fluyen entre ellas; los tipos V1 y V2, los
disparadores por rama, etiqueta o ruta de fichero, y las variables de pipeline; la aprobación manual
con la información exacta que Marta necesita para decidir en treinta segundos; las etapas de desarrollo,
preproducción y producción; las puertas de calidad con una acción Lambda que consulta las métricas de
MercadoFresco/Tienda y detiene el pipeline si algo se degrada; los reintentos, los permisos por acción y
el acceso entre cuentas; y las notificaciones hacia alertas-mercadofresco y Slack.
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
