Un sistema que decide sueldos
El cliente calcula comisiones y bonos de los equipos de ventas de sus clientes. En la práctica, el sistema decide cuánto dinero va al bolsillo de cada vendedor a fin de mes.
La plataforma era una aplicación Ruby, con una app web y varios workers de procesamiento: cálculo de comisiones, limpieza y migración de datos, rutinas de sistema. No había entornos estandarizados ni una forma segura de actualizar. En un sistema así, un deploy a medias, con parte de los servicios en la versión nueva y parte en la antigua, no es solo inestabilidad. Puede significar comisiones mal calculadas.
Todo en código
Empecé contenerizando las aplicaciones, creando los repositorios de imágenes en ECR y ajustando el Dockerfile para que el build fuera reproducible. Escribí la infraestructura en Terraform e importé en él los recursos que ya existían, para que nada quedara fuera del código.
Migré seis workers a ECS, incluidos los tres motores de cálculo de comisiones, y adapté la gestión de secretos al modelo nuevo. La app web y el entorno compartido los migró otro ingeniero del equipo. También creé los entornos Demo y Beta, con el clúster aprovisionado por un módulo de Terraform y un capacity provider para escalar las instancias solo.
Para los deploys, armé un pipeline principal en GitHub Actions que dispara el pipeline de cada servicio. Como GitHub Actions no acepta subcarpetas de workflows, organicé los archivos por prefijo.
O suben todos, o vuelven todos
La parte más importante fue el deploy blue/green con CodeDeploy, diseñado para que cuatro servicios subieran como un único lote transaccional. Antes de instalar, corren las migrations y la validación de dependencias de la imagen, para fallar lo antes posible. Antes de liberar el tráfico, corren un smoke test HTTP en la web y un job sintético en los workers. Si cualquiera de los cuatro falla, sea en un health check, un hook o una alarma, los cuatro vuelven automáticamente. La versión anterior sigue en línea hasta que la nueva se promueve, y las migrations tienen que funcionar con las dos versiones, para que un rollback no pierda datos.
El clúster que "no se achicaba"
En el entorno Beta, el clúster parecía no reducir instancias cuando bajaba la carga. Investigué en dos pasos. Primero, encontré el capacity provider en un estado inconsistente. Como no puede reutilizar un Auto Scaling Group antiguo, recreé los dos, recreé el servicio web con CodeDeploy y los listeners del load balancer, e importé todo de vuelta a Terraform sin perder el estado.
Después entendí el comportamiento que había llamado la atención: no era un defecto. El scaling gestionado del capacity provider usa una ventana conservadora de unos 15 minutos antes de reducir instancias. Confirmé por la CLI que, solo con la web activa, el grupo baja a una instancia después de esa ventana.
Qué cambió
El blue/green está en producción. El cliente hace deploy sin sacar la plataforma de línea, y una falla en cualquiera de los cuatro servicios hace que todos vuelvan juntos, sin dejar el cálculo de comisiones en un estado mixto. Los workers corren en contenedores con imágenes versionadas, y los entornos nuevos se crean con Terraform, no a mano.
Lo que me llevo
En sistemas que manejan dinero, el deploy tiene que ser atómico entre los servicios: "medio actualizado" es peor que "nada actualizado". Y no todo comportamiento extraño es un bug. Antes de arreglar el scale down, valía la pena entender cómo fue diseñado el capacity provider.