Um sistema que decide salários
O cliente calcula comissões e bônus das equipes de vendas dos clientes dele. Na prática, o sistema decide quanto dinheiro vai para o bolso de cada vendedor no fim do mês.
A plataforma era uma aplicação Ruby, com um app web e vários workers de processamento: cálculo de comissões, limpeza e migração de dados, rotinas de sistema. Não havia ambientes padronizados nem uma forma segura de atualizar. Num sistema assim, um deploy pela metade, com parte dos serviços na versão nova e parte na antiga, não é só instabilidade. Pode significar comissão calculada errada.
Tudo em código
Comecei containerizando as aplicações, criando os repositórios de imagem no ECR e ajustando o Dockerfile para que o build fosse reproduzível. Escrevi a infraestrutura em Terraform e importei para ele os recursos que já existiam, para que nada ficasse de fora do código.
Migrei seis workers para o ECS, incluindo os três motores de cálculo de comissão, e adaptei a gestão de segredos para o modelo novo. O app web e o ambiente compartilhado foram migrados por outro engenheiro do time. Também criei os ambientes Demo e Beta, com o cluster provisionado por um módulo Terraform e um capacity provider para escalar as instâncias sozinho.
Para os deploys, montei uma pipeline principal no GitHub Actions que dispara a pipeline de cada serviço. Como o GitHub Actions não aceita subpastas de workflows, organizei os arquivos por prefixo.
Ou todos sobem, ou todos voltam
A parte mais importante foi o deploy blue/green com o CodeDeploy, desenhado para que quatro serviços subissem como um lote transacional. Antes de instalar, rodam as migrations e a validação de dependências da imagem, para falhar o mais cedo possível. Antes de liberar o tráfego, rodam um smoke test HTTP no web e um job sintético nos workers. Se qualquer um dos quatro falhar, seja no health check, num hook ou num alarme, os quatro voltam automaticamente. A versão anterior continua no ar até a nova ser promovida, e as migrations precisam funcionar com as duas versões, para que um rollback não perca dado.
O cluster que "não reduzia"
No ambiente Beta, o cluster parecia não diminuir as instâncias quando a carga caía. Investiguei em dois momentos. Primeiro, encontrei o capacity provider num estado inconsistente. Como ele não pode reaproveitar um Auto Scaling Group antigo, recriei os dois, recriei o serviço web com o CodeDeploy e os listeners do load balancer, e importei tudo de volta para o Terraform sem perder o estado.
Depois, entendi a causa do comportamento que tinha chamado atenção: não era defeito. O scaling gerenciado do capacity provider usa uma janela conservadora de cerca de 15 minutos para reduzir instâncias. Confirmei pela CLI que, só com o web ativo, o grupo cai para uma instância depois dessa janela.
O que mudou
O blue/green está em produção. O cliente faz deploy sem tirar a plataforma do ar, e uma falha em qualquer um dos quatro serviços faz todos voltarem juntos, sem deixar o cálculo de comissões num estado misto. Os workers rodam em containers com imagens versionadas, e ambientes novos sobem pelo Terraform, não à mão.
O que eu levo disso
Em sistemas que mexem com dinheiro, o deploy precisa ser atômico entre os serviços: "metade atualizado" é pior que "nada atualizado". E nem todo comportamento estranho é bug. Antes de consertar o scale down, valia entender como o capacity provider foi feito para funcionar.