Tractar la despesa al núvol com l’uptime: alertes de costos en una AWS Organization
Resum
Vam fer que la despesa a AWS de l’organització fos observable i generés alertes, igual que ja fèiem amb l’uptime: llindars, previsions, encaminament i un responsable per a cada alerta. Les alertes de previsió ens avisen ara uns 9 dies abans que se superi un pressupost.
La feina té dues capes. Una capa d’alertes, construïda com un mòdul reutilitzable de Terraform sobre AWS Budgets, ens diu quan la despesa va cap on no hauria d’anar. Una capa de visibilitat, un mètode d’auditoria que creua les dades de facturació amb l’inventari de recursos, ens diu per què.
Aquest mateix enfocament va condicionar després una decisió dins de la mateixa observabilitat: si els logs d’ECS van a CloudWatch Logs o a Loki a través de FireLens.
El repte
L’AWS Organization té un compte de gestió i comptes membres. L’uptime tenia alertes, dashboards i guàrdies. La despesa no tenia res de tot això: el primer senyal d’un problema de costos era la factura mensual, setmanes després que els diners ja haguessin volat.
Aquest buit té una forma previsible. Un NAT Gateway oblidat, un grup de logs sense retenció o un entorn de proves que ningú no va desmuntar costen una mica cada dia. Res d’això no és prou cridaner per veure’s a la consola, i tot es va acumulant. Un mes, un entorn de proves de càrrega que es va quedar engegat en un compte de staging va afegir uns 1.100 $ a una factura d’uns 9.500 $, i ningú no se’n va adonar fins que va arribar la factura.
L’objectiu era tractar el cost com un senyal operatiu: mesurat de manera contínua, amb alertes abans de superar el límit i amb un responsable.
Restriccions
- Avisar abans, no després. Una alerta que salta quan el pressupost ja s’ha gastat és un post-mortem, no una alerta. La previsió era un requisit imprescindible.
- Un mòdul, múltiples comptes. Els pressupostos s’havien de definir una vegada a Terraform i replicar-se per compte, servei o etiqueta, no muntar-se a cop de clic a la consola.
- Encaminament sense refer res. Afegir un nou subscriptor, com un canal de xat o una eina de guàrdies, no ha d’implicar tocar ni recrear el pressupost.
- Plans sense soroll. El mòdul havia de produir una sortida neta de
terraform planmes rere mes, perquè el drift real continués sent visible. - Primer el que és natiu d’AWS. Fer servir Budgets, Cost Explorer, les APIs d’etiquetatge i SNS abans de recórrer a eines FinOps de tercers.
Arquitectura
El sistema té dues capes unides per un únic bucle de retroalimentació. La capa d’alertes indica quan la despesa es desvia del pla; la capa de visibilitat indica per què, i les seves etiquetes tornen a la capa d’alertes com a filtres de pressupost.
Tot el que fa referència a les alertes és a Terraform. L’auditoria s’executa contra les APIs de Cost Explorer i d’etiquetatge, i el seu resultat durador és un esquema d’etiquetes, no un informe.
La capa d’alertes: un mòdul de Budgets reutilitzable
La capa d’alertes és un únic mòdul de Terraform construït sobre AWS Budgets, instanciat per compte, per servei o per etiqueta.
Per què Budgets i no alarmes de facturació de CloudWatch
L’enfocament clàssic és una alarma de CloudWatch sobre la mètrica EstimatedCharges. Té dos problemes. Les mètriques de facturació només existeixen a us-east-1, així que totes les alarmes hi han de viure, s’executin on s’executin les càrregues de treball. I l’alarma només salta quan els càrrecs reals creuen la línia; no té previsió.
AWS Budgets admet llindars tant reals com de previsió, filtra per servei, etiqueta o compte vinculat, i la monitorització i les notificacions bàsiques són gratuïtes.
Llindars: tres de reals i un de previsió
| Notificació | Llindar | Què vol dir |
|---|---|---|
| Real | 50% | Control a mig mes; sobretot informatiu |
| Real | 80% | La despesa va per davant del pla; algú ho hauria de mirar |
| Real | 100% | Pressupost superat |
| Previsió | 100% | AWS preveu que se superarà a final de mes; cal actuar ja |
La notificació de previsió és la que canvia el comportament. Converteix el cost en una cosa de la qual t’avisen, no una cosa que revises, mentre encara hi ha temps per actuar.
Fan-out amb SNS, i la política que la majoria de mòduls fan malament
Budgets publica en un topic de SNS, i els subscriptors pengen d’aquest topic. Budgets no pot publicar en un topic tret que la política del topic permeti explícitament el principal de servei budgets.amazonaws.com. Sense ella, el pressupost es crea sense problemes, la notificació queda configurada i no s’entrega mai res.
Posar el topic entre el pressupost i els seus subscriptors també els desacobla: una nova llista de correu, un webhook de xat o una integració de guàrdies és una nova subscripció, i el recurs del pressupost no es toca mai.
Abast
El mòdul accepta un mapa de filtres de cost, així que el mateix codi cobreix un compte vinculat sencer, un únic servei com Amazon EC2 o una etiqueta d’assignació de costos com un equip o un entorn.
Una versió simplificada dels recursos principals:
data "aws_iam_policy_document" "billing_topic" {
statement {
sid = "AllowBudgetsPublish"
actions = ["sns:Publish"]
resources = [aws_sns_topic.billing.arn]
principals {
type = "Service"
identifiers = ["budgets.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [data.aws_caller_identity.current.account_id]
}
}
}
resource "aws_sns_topic_policy" "billing" {
arn = aws_sns_topic.billing.arn
policy = data.aws_iam_policy_document.billing_topic.json
}
locals {
notifications = [
{ type = "ACTUAL", threshold = 50 },
{ type = "ACTUAL", threshold = 80 },
{ type = "ACTUAL", threshold = 100 },
{ type = "FORECASTED", threshold = 100 },
]
}
resource "aws_budgets_budget" "this" {
name = var.name
budget_type = "COST"
limit_amount = var.limit_amount
limit_unit = "USD"
time_unit = "MONTHLY"
time_period_start = "2024-01-01_00:00" # fixat en el passat, vegeu compromisos
dynamic "cost_filter" {
for_each = var.cost_filters # p. ex. { LinkedAccount = ["111111111111"] }
content {
name = cost_filter.key
values = cost_filter.value
}
}
dynamic "notification" {
for_each = local.notifications
content {
comparison_operator = "GREATER_THAN"
threshold = notification.value.threshold
threshold_type = "PERCENTAGE"
notification_type = notification.value.type
subscriber_sns_topic_arns = [aws_sns_topic.billing.arn]
}
}
depends_on = [aws_sns_topic_policy.billing]
}
La capa de visibilitat: la facturació es creua amb l’inventari
Les alertes diuen que la despesa s’ha desviat, però no per què. El mètode d’auditoria que hi respon llegeix dues fonts i les compara.
- El que pagues: Cost Explorer i la seva API, agrupats per compte, servei i tipus d’ús.
- El que existeix: la Resource Groups Tagging API, que llista els recursos etiquetats a tots els serveis i regions.
Cap de les dues vistes no n’hi ha prou per si sola, perquè AWS factura per ús, no per inventari. La transferència de dades, el processament del NAT i la ingesta de logs apareixen a la factura, però no són recursos que es puguin llistar. Un volum sense associar o un balancejador de càrrega inactiu és un recurs, però el seu cost es dilueix en una línia de servei que sembla normal. Les troballes útils són on les dues vistes no coincideixen.
Els sospitosos habituals
| Malbaratament | Per què passa desapercebut | Com el vam trobar | Solució |
|---|---|---|---|
| NAT Gateways inactius | Càrrec per hora més processament, facturat com a EC2-Other | Tipus d’ús NatGateway-Hours amb poc NatGateway-Bytes | Eliminar-los, o compartir-ne un per VPC on no calgui alta disponibilitat |
| Volums EBS sense associar | Es queden enrere quan es terminen les instàncies | Volums en estat available | Snapshot si cal i després esborrar-los |
| Elastic IPs òrfenes | Les adreces IPv4 públiques es facturen per hora | Adreces sense associació | Alliberar-les |
| Buckets S3 sense polítiques de cicle de vida | Els objectes antics i les pujades multipart incompletes es continuen acumulant | Buckets sense configuració de cicle de vida | Regles d’expiració i transició |
| Grups de logs sense retenció | La retenció per defecte és indefinida | Grups de logs amb la retenció configurada per no caducar mai | Definir la retenció de cada grup de logs |
Les etiquetes com a governança contínua
L’auditoria és una foto fixa; les etiquetes mantenen la imatge al dia. Un conjunt petit i obligatori d’etiquetes d’assignació de costos abarateix la resposta a qualsevol pregunta futura: de quin equip és això i és de producció? Les etiquetes activades també es converteixen en filtres de pressupost al mòdul anterior, així que un equip pot tenir el seu propi pressupost sense necessitat d’un compte nou.
L’esquema té quatre claus: owner, environment, service i cost-center. Els default_tags del provider de Terraform les apliquen a tot el que creem, una tag policy d’AWS Organizations informa dels recursos que no compleixen, i una comprovació a CI fa fallar qualsevol plan que hagués de crear un recurs sense etiquetes.
L’observabilitat també té una corba de costos
Tan bon punt la despesa és visible, comença a condicionar les decisions sobre la mateixa observabilitat. El cas més clar va ser on envien els logs els serveis d’ECS.
- CloudWatch Logs no requereix infraestructura pròpia i s’integra de manera nativa amb ECS. El seu cost és gairebé del tot per GB ingerit, així que creix de manera lineal amb el volum de logs.
- FireLens cap a Loki envia els logs a través d’un sidecar de Fluent Bit fins a Loki, que ja funciona com a part del nostre stack LGTM i desa els chunks a S3. El seu cost és majoritàriament fix: el que ocupa Loki, l’emmagatzematge d’objectes barat i un petit peatge de CPU i memòria per tasca per al sidecar.
Amb poc volum, CloudWatch és més barat i més senzill. A partir de cert volum d’ingesta diària, el cost fix de Loki és més petit que la factura per GB. Vam prendre la decisió servei per servei segons aquest punt d’encreuament, no com una regla general.
El punt d’encreuament va quedar en uns 3 GB de logs per servei i dia. Amb el preu d’ingesta de Frankfurt, d’uns 0,63 $ per GB, això són uns 57 $ al mes per servei a CloudWatch, davant d’uns pocs dòlars de sidecar i S3 en un stack de Loki que ja teníem en marxa. Tres dels nostres 12 serveis d’ECS eren per sobre d’aquesta línia i van passar a FireLens; els altres nou es van quedar a CloudWatch Logs.
La classe de logs Infrequent Access de CloudWatch redueix la diferència per als logs que gairebé no es consulten, a canvi de perdre funcions com els filtres de mètriques. Val la pena calcular-ne el cost com a tercera opció abans de migrar un servei.
Decisions clau i compromisos
On viuen els pressupostos
Els pressupostos poden estar centralitzats al compte de gestió, un per compte membre mitjançant un filtre LinkedAccount, o dins de cada compte membre. Els pressupostos centralitzats ofereixen un únic lloc per veure la cobertura i un únic estat de Terraform, però els equips dels comptes membres no els poden veure. Els pressupostos per compte posen la responsabilitat on es produeix la despesa, a costa de múltiples desplegaments que cal mantenir coherents.
El preu no és el factor decisiu. Els pressupostos que només es fan servir per monitoritzar i notificar són gratuïts; només es cobren els pressupostos amb accions, a partir del tercer.
Fem servir un model híbrid. Cada compte membre té un pressupost centralitzat al compte de gestió amb un filtre LinkedAccount, cosa que dona cobertura completa des d’un únic estat de Terraform. Els tres comptes de producció tenen a més els seus propis pressupostos per servei, a càrrec dels equips que gestionen aquests serveis.
El retard de les etiquetes
Una etiqueta d’assignació de costos no serveix de res en el moment que l’afegeixes. Una nova clau d’etiqueta pot trigar fins a 24 hores a estar disponible per activar-la al compte de gestió, i unes 24 hores més després d’activar-la a aparèixer a Cost Explorer i als filtres de pressupost. Per això els pressupostos filtrats per etiqueta comencen a cegues. El compte de gestió pot aplicar l’activació d’etiquetes amb caràcter retroactiu fins a dotze mesos, cosa que recupera l’historial, però no les alertes que no van arribar.
Llindars fixos o Cost Anomaly Detection
Els llindars fixos responen a una sola pregunta: superarem el pla? Se’ls escapa un pic que es queda per sota del pressupost, com un servei el cost diari del qual es triplica en un mes en què el compte acaba igualment al 70 %. Cost Anomaly Detection modela la despesa normal per servei o per compte i assenyala les desviacions amb independència del pressupost. Tots dos es complementen: els pressupostos per al pla, la detecció d’anomalies per a la forma.
Drift: fixar time_period_start
Si time_period_start es deixa sense definir, AWS l’omple amb l’inici del període actual. El mes següent, Terraform veu un valor diferent i proposa un canvi a cada plan. Fixar-lo en una data passada manté els plans nets, de manera que un diff en un pressupost sempre vol dir que alguna cosa ha canviat.
Què va fallar
- Notificacions silencioses. Un pressupost amb una notificació que apunta a un topic de SNS sembla complet tant a Terraform com a la consola. Sense la política del topic per a
budgets.amazonaws.com, no s’entrega res i no apareix cap error. La correcció es va incorporar al mòdul, amb undepends_onexplícit perquè la política existeixi abans que el pressupost. - Un plan que no s’estabilitzava mai. Deixar
time_period_starten mans d’AWS feia que cada nou mes generés un diff a tots els pressupostos. Fixar la data ho va resoldre, però només després que el soroll hagués acostumat la gent a passar per alt els diffs dels pressupostos. - Pressupostos per etiqueta que marcaven zero. Els pressupostos filtrats per una etiqueta acabada d’activar no mostraven despesa durant el primer dia o els dos primers dies. No passava res; simplement l’etiqueta encara no era a les dades de facturació.
Cap dels tres no va costar diners directament, però el primer va fer que els nostres primers pressupostos no enviessin alertes, sense que ningú ho sabés, durant unes dues setmanes.
Resultats
Ara el cost es comporta com qualsevol altre senyal de producció: té llindars, previsió, una ruta d’alertes i un responsable.
| Mesura | Resultat | Com es va mesurar |
|---|---|---|
| Antelació de les alertes de previsió | De 6 a 12 dies, uns 9 de mitjana | Data de la notificació de previsió davant de la data en què la despesa real va superar el 100 % |
| Cobertura de pressupostos | Tots els comptes | Comptes amb almenys un pressupost actiu i un subscriptor que funciona |
| Incidents detectats per les alertes | 3 en els quatre primers mesos | Historial d’alertes del topic de SNS |
| Cost de les mateixes alertes | Gratuït | Els pressupostos només de notificació no tenen cost a AWS |
El cas més clar va ser en un compte de staging, un compte petit dins de l’organització. Després d’un incident, es va quedar activat el nivell de log debug en un servei d’ECS, i la ingesta a CloudWatch Logs va passar de menys d’1 GB a uns 40 GB al dia. El dia 9 del mes va saltar l’alerta de previsió d’aquell compte al canal compartit, i aquella mateixa tarda es va restablir el nivell de log. Si s’hagués deixat fins a la factura, hauria afegit uns 550 $ al mes.
Què faria diferent
- Enviar les alertes de pressupost a Mattermost des del primer dia. Les alertes de seguretat de CloudTrail ja arriben a Mattermost. Posar les alertes de costos al mateix lloc, com un subscriptor més de SNS, les convertiria en part de la imatge operativa diària en lloc d’una carpeta de correu.
- Activar Cost Anomaly Detection juntament amb els pressupostos. Els llindars fixos eren el primer pas adequat, però no poden veure un pic que es queda per sota del pla.
- Configurar aviat Cost and Usage Reports amb Athena. Cost Explorer respon les preguntes habituals; CUR amb Athena respon les incòmodes, com el cost per tasca o per client, amb SQL i tot l’historial.
- Acordar l’esquema d’etiquetes abans de l’auditoria. Pel retard de les etiquetes, cada dia sense etiquetes activades és un dia de despesa que després no podràs atribuir sense un procés retroactiu.
