El mòdul 4 va acabar amb una frase incòmoda: està tot muntat, i ningú no ho està mirant. MercadoFresco té una botiga que escala sola, una base de dades replicada, una CDN que serveix el 96 % del trànsit, identitats mínimes, dades xifrades i dos tallafocs d'aplicació. I tanmateix, si divendres a les 19:10 la botiga comença a retornar errors, la primera persona a assabentar-se'n serà un client per Twitter.
Amazon CloudWatch és el servei que resol això. No és «els gràfics d'AWS»: és el repositori
central on tots els serveis que has muntat escriuen el que els passa, el lloc on
s'emmagatzemen els registres de la teva aplicació, i el motor que decideix quan despertar algú. La Marta el
fa servir de reüll des de 02-01 —cada vegada que hem mirat CPUUtilization o hem creat una alarma
érem a CloudWatch— però mai no l'hem muntat de debò.
Aquesta lliçó el munta de debò. Al final, MercadoFresco tindrà un tauler que la Marta obre cada matí amb el cafè, mètriques de negoci pròpies, tots els seus registres en un sol lloc consultables amb un llenguatge tipus SQL i —el més important— la certesa comprovada que un avís arriba a un telèfon a les quatre de la matinada.
Avís de cost. CloudWatch és dels serveis d'AWS que més fàcilment es descontrolen, i gairebé mai per les mètriques: pels registres. Un grup de registres amb retenció infinita, o una aplicació amb
DEBUGactivat en producció, poden costar més que les instàncies EC2 que els generen. Cada secció d'aquesta lliçó n'indica el cost, i hi ha una secció sencera dedicada a això.
Contingut
- Què és CloudWatch i què no és
- Els tres pilars
- Mètriques: espais de noms i dimensions
- Resolució estàndard enfront d'alta resolució
- Estadístiques: per què la mitjana menteix
- Períodes, agregació i retenció
- Les mètriques que importen de debò del que ja està muntat
- Per què EC2 no publica l'ús de memòria
- Publicar mètriques personalitzades amb boto3
PutMetricDataper lots i valors estadístics- L'agent unificat de CloudWatch a les instàncies de l'ASG
- Embedded Metric Format des de Lambda
- Registres: grups, fluxos i retenció
- Els registres que MercadoFresco té dispersos
- Logs Insights: sintaxi i consultes reals
- Respondre a «per què va trigar vuit segons aquella comanda?»
- Filtres de mètriques: d'un patró de registre a una alarma
- Subscripcions i exportació a S3
- Alarmes: llindars, avaluació i dades absents
- Alarmes compostes
- Detecció d'anomalies
- Accions d'alarma
- Comprovar que l'avís arriba de debò
- El tauler
mercadofresco-produccion - ServiceLens i Synthetics
- Cost de CloudWatch i com es dispara
- Neteja
Què és CloudWatch i què no és
CloudWatch és un servei regional d'observabilitat que fa tres coses: emmagatzemar sèries temporals numèriques (mètriques), emmagatzemar text amb marca de temps (registres), i avaluar condicions sobre això per disparar accions (alarmes).
Allò que no és, i convé fixar-ho des del principi perquè evita molta confusió:
| CloudWatch no és | Això ho fa | Es veu a |
|---|---|---|
| Un registre de qui ha cridat l'API d'AWS | CloudTrail | 05-03 |
| Un rastrejador de peticions entre serveis | X-Ray | 05-02 |
| Un avaluador del compliment de configuracions | AWS Config | 05-04 |
| Un bus d'esdeveniments per automatitzar reaccions | EventBridge | 07-03 |
| Un magatzem analític per a consultes històriques grans | Athena / Redshift | 05-03, 06-04 |
La confusió més habitual és la primera. Si el Luis busca «qui ha esborrat el bucket», això no és a CloudWatch: és a CloudTrail. CloudWatch desa el que la teva aplicació i els serveis diuen d'ells mateixos, no qui ha parlat amb l'API d'AWS.
Històricament el bus d'esdeveniments es va dir «CloudWatch Events» i encara veuràs aquest nom en documentació antiga i en algunes respostes de la CLI. Avui és EventBridge i s'estudia a 07-03; aquesta lliçó no el toca.
Els tres pilars
flowchart LR
subgraph Origenes["Origens"]
EC2["EC2 / ASG"]
ALB["ALB"]
RDS["RDS"]
LAM["Lambda"]
CF["CloudFront"]
APP["Codi de la botiga"]
end
EC2 --> M["METRIQUES<br/>series temporals"]
ALB --> M
RDS --> M
LAM --> M
CF --> M
APP --> M
EC2 --> L["REGISTRES<br/>text amb marca de temps"]
LAM --> L
APP --> L
ALB -.->|"a S3, no a Logs"| S3["S3"]
L -->|"filtre de metriques"| M
M --> A["ALARMES<br/>llindar + avaluacio"]
A --> SNS["SNS alertas-mercadofresco"]
A --> ASG["Politica d'autoescalat"]
A --> EC2ACT["Accio sobre la instancia"]
Fixa't en la fletxa que va de registres a mètriques: un filtre de mètriques converteix un patró de
text en una sèrie numèrica. És el pont que permet alarmar sobre alguna cosa que només apareix en un
registre, com ERROR pago rechazado. I fixa't també que l'ALB escriu els registres d'accés a S3,
no a CloudWatch Logs: això tindrà conseqüències a la secció de Logs Insights.
Mètriques: espais de noms i dimensions
Una mètrica és una sèrie temporal identificada per tres coses:
- Espai de noms (namespace): el contenidor. Els d'AWS comencen per
AWS/(AWS/EC2,AWS/ApplicationELB,AWS/RDS,AWS/Lambda,AWS/S3,AWS/CloudFront). El teu no pot començar perAWS/: MercadoFresco fa servirMercadoFresco/Tienda. - Nom de la mètrica:
CPUUtilization,TargetResponseTime,PedidosConfirmados. - Dimensions: fins a 30 parells clau/valor que identifiquen de què és la mesura.
InstanceId=i-0abc...,LoadBalancer=app/alb-mercadofresco-tienda/...,DBInstanceIdentifier=mercadofresco-pedidos.
El detall que més despista al principi: cada combinació diferent de dimensions és una mètrica diferent i es factura per separat. Aquestes són tres mètriques, no una:
MercadoFresco/Tienda PedidosConfirmados (sin dimensiones) MercadoFresco/Tienda PedidosConfirmados Entorno=produccion MercadoFresco/Tienda PedidosConfirmados Entorno=produccion, Provincia=Madrid
I CloudWatch no agrega automàticament entre elles. Si publiques només la tercera i després consultes la primera, no hi haurà dades. Això té dues conseqüències pràctiques:
- Si vols el total i també el desglossament, publica'ls tots dos.
- No facis servir mai com a dimensió res de cardinalitat alta. Posar
PedidoIdcom a dimensió crea una mètrica nova per comanda: 900 mètriques a l'hora, 650.000 al mes, a 0,30 USD cadascuna. Això són més de 190.000 USD mensuals per un descuit d'una línia. La identitat d'una comanda va en un registre o en una anotació d'X-Ray (05-02), mai en una dimensió.
Regla de MercadoFresco: les dimensions són categories tancades i petites —entorn, component, tipus de pagament, província— mai identificadors.
Resolució estàndard enfront d'alta resolució
| Estàndard | Alta resolució | |
|---|---|---|
| Granularitat mínima | 60 segons | 1 segon |
| Com es demana | Per defecte | StorageResolution=1 en publicar |
| Períodes d'alarma | 60 s i múltiples | 10 s i 30 s també |
| Cost de la mètrica | 0,30 USD/mes | 0,30 USD/mes (igual) |
| Cost de l'alarma | 0,10 USD/mes | 0,30 USD/mes |
| Retenció de les dades d'1 s | — | 3 hores, després s'agrega |
L'alta resolució serveix per a processos que canvien en segons: una cua que s'omple, un pic de latència de 20 segons que a resolució de minut queda diluït en la mitjana. MercadoFresco no la fa servir: els seus problemes es veuen perfectament a un minut, i les alarmes de 10 segons generen soroll nocturn. És una eina de diagnòstic puntual, no un valor per defecte.
Les mètriques bàsiques d'EC2 són de 5 minuts llevat que activis el monitoratge detallat
(2,10 USD per instància i mes a eu-west-1, aproximadament), que les baixa a 1 minut. Per a l'ASG
de MercadoFresco sí que val la pena: amb dades de 5 minuts, l'escalat reacciona tard al pic dels
divendres.
Estadístiques: per què la mitjana menteix
CloudWatch no desa cada punt individual: desa agregats per període. En consultar tries quin agregat vols:
| Estadística | Què respon | Quan fer-la servir |
|---|---|---|
Sum |
Quant en total? | Comptadors: RequestCount, PedidosConfirmados, Errors |
Average |
Quant de mitjana? | Utilització: CPUUtilization, CacheHitRate |
Minimum / Maximum |
L'extrem | Capacitat: FreeStorageSpace mínim, connexions màximes |
SampleCount |
Quants punts? | Diagnòstic de buits |
p50, p90, p95, p99 |
Què experimenta el percentil N? | Latència, sempre |
TM(5%:95%), TC, WM |
Mitjana retallada, ignorant extrems | Anàlisi fina |
L'error clàssic —i el que amaga més incidents— és alarmar sobre l'Average d'una latència. Un
minut real de la botiga de MercadoFresco:
| Peticions | Temps de resposta |
|---|---|
| 950 | 0,08 s (pàgines a la memòria cau) |
| 40 | 0,4 s (fitxes de producte) |
| 10 | 9,5 s (confirmació de comanda) |
Average = 0,17 s. Perfecte. Verd. No salta cap alarma.
p99 = 9,4 s. Deu clients per minut estan veient una roda girar durant gairebé deu segons
mentre intenten pagar. Són exactament els clients que importen: els que estan comprant.
Regla: per a qualsevol mètrica de latència, alarma sobre
p95op99, mai sobreAverage. La mitjana et diu com va el servidor; el percentil et diu com ho viu el client.
Un matís important: els percentils només estan disponibles si la mètrica es publica amb dades
suficients. Les mètriques de l'ALB els admeten de manera nativa. Per a mètriques pròpies, cal
publicar valors individuals o fer servir StatisticValues amb compte (ho veurem: els StatisticValues
no permeten percentils, perquè ja arriben agregats).
Períodes, agregació i retenció
El període és la finestra d'agregació de la consulta o de l'alarma: 60 s, 300 s, 3600 s… És independent de la freqüència amb què publiques. Si publiques cada 10 segons i consultes amb període 300, CloudWatch agrega 30 punts en un.
La retenció és automàtica, gratuïta i no configurable:
| Antiguitat de la dada | Resolució conservada |
|---|---|
| 0 – 3 hores | 1 segon (només alta resolució) |
| 0 – 15 dies | 1 minut |
| 15 – 63 dies | 5 minuts |
| 63 – 455 dies | 1 hora |
| Més de 15 mesos | S'esborra |
Dues conseqüències operatives:
- No pots consultar amb període 60 un dia de fa dos mesos. Les dades ja estan agregades a 5 minuts. Si necessites comparar el Black Friday de l'any passat al minut, l'has d'haver exportat abans.
- La Marta exporta cada mes les mètriques de negoci (
PedidosConfirmados) amercadofresco-informes-analiticaambget-metric-data, precisament perquè la Sara pugui comparar campanyes d'anys diferents.
Les mètriques que importen de debò del que ja està muntat
Aquesta és la taula que la Marta s'ha imprès. Dels centenars de mètriques que publiquen els serveis de MercadoFresco, aquestes són les que de debò decideixen alguna cosa:
| Servei | Mètrica | Estadística | Què significa | Llindar de MercadoFresco |
|---|---|---|---|---|
| EC2 / ASG | CPUUtilization |
Average |
Ús de CPU | Escalat al 60 % (política cpu-objetivo-60) |
| EC2 / ASG | StatusCheckFailed_Instance |
Maximum |
La instància està trencada | ≥ 1 durant 2 períodes |
| EC2 / ASG | GroupInServiceInstances |
Maximum |
Instàncies actives | = 4 → mercadofresco-asg-al-maximo |
| ALB | TargetResponseTime |
p95 |
Latència vista pel client | > 2 s durant 3 min |
| ALB | HTTPCode_ELB_5XX_Count |
Sum |
Errors del balancejador | > 10 en 5 min |
| ALB | HTTPCode_Target_5XX_Count |
Sum |
Errors de la teva aplicació | > 25 en 5 min |
| ALB | HealthyHostCount |
Minimum |
Instàncies sanes per grup | < 2 |
| ALB | UnHealthyHostCount |
Maximum |
Instàncies que fallen /salud |
≥ 1 |
| ALB | RejectedConnectionCount |
Sum |
L'ALB rebutja per manca de capacitat | > 0 |
| RDS | DatabaseConnections |
Maximum |
Connexions obertes | > 160 de 200 |
| RDS | FreeStorageSpace |
Minimum |
Disc lliure en bytes | < 20 GB |
| RDS | ReadLatency / WriteLatency |
Average |
Latència de disc en segons | > 0,02 s |
| RDS | CPUUtilization |
Average |
CPU de la instància | > 80 % |
| RDS | ReplicaLag |
Maximum |
Retard de la rèplica de lectura | > 30 s |
| RDS | FreeableMemory |
Minimum |
Memòria disponible | < 500 MB |
| Lambda | Errors |
Sum |
Invocacions fallides | > 5 en 5 min |
| Lambda | Duration |
p99 |
Temps d'execució | > 80 % del timeout |
| Lambda | Throttles |
Sum |
Invocacions rebutjades per concurrència | > 0 |
| Lambda | ConcurrentExecutions |
Maximum |
Execucions simultànies | > 70 % de la quota |
| Lambda | IteratorAge |
Maximum |
Retard en orígens de flux | > 60 s |
| S3 | BucketSizeBytes |
Average |
Mida (diària) | Tendència, no alarma |
| S3 | NumberOfObjects |
Average |
Objectes (diari) | Tendència |
| S3 | 4xxErrors / 5xxErrors |
Sum |
Requereix mètriques de petició (de pagament) | > 1 % |
| CloudFront | CacheHitRate |
Average |
% servit des de la vora | < 50 % → alarma |
| CloudFront | 5xxErrorRate |
Average |
Errors retornats | > 1 % |
| CloudFront | OriginLatency |
p95 |
El que triga el teu origen | > 1 s |
| WAF | BlockedRequests |
Sum |
Peticions bloquejades | Anomalia |
Tres observacions que separen qui entén això de qui copia llindars:
HTTPCode_ELB_5XX_CountiHTTPCode_Target_5XX_Countno són el mateix. El primer significa que el balancejador no va poder lliurar la petició a ningú (no hi ha destins sans, temps d'espera exhaurit). El segon significa que la teva aplicació ha respost 500. Confondre'ls fa perdre hores buscant al lloc equivocat.FreeStorageSpaced'RDS està en bytes. El llindar de 20 GB s'escriu21474836480. Un zero de més i l'alarma no salta mai.Throttlesde Lambda amb llindar > 0, no > 10. Un sol estrangulament significa que has tocat el sostre de concurrència i hi ha clients veient errors. No hi ha un «poc de throttling acceptable».
Per què EC2 no publica l'ús de memòria
És la pregunta que fa tothom en arribar aquí, i la resposta explica com funciona el núvol.
L'hipervisor d'AWS veu la màquina virtual des de fora. Pot mesurar CPU consumida, trànsit de xarxa,
operacions de disc del volum EBS: tot això passa per la seva capa. Però la memòria lliure és un
concepte del sistema operatiu convidat. Només Linux sap quanta RAM hi ha a buff/cache i es podria
alliberar, i això l'hipervisor no ho pot saber sense entrar-hi.
El mateix passa amb l'espai lliure del disc: EBS és un dispositiu de blocs, i només el sistema de fitxers de dins sap què està ocupat.
Conseqüència pràctica: per a memòria i disc d'EC2 necessites instal·lar un agent dins de la instància. Es fa més avall. I conseqüència de disseny: per això Lambda, Fargate i RDS —on AWS gestiona el sistema operatiu— sí que publiquen memòria sense que facis res.
Publicar mètriques personalitzades amb boto3
Les mètriques d'infraestructura diuen com va la màquina. Les de negoci diuen com va l'empresa,
i són les que de debò detecten incidents. A 04-04 vam veure per què: si arriben 5.000 peticions per
segon i PedidosPorHora no es mou, no són clients.
MercadoFresco ja publicava PedidosPorHora. Ara hi afegim les dues que falten:
| Mètrica | Unitat | Estadística útil | Per a què |
|---|---|---|---|
PedidosConfirmados |
Count |
Sum |
Volum de negoci en temps real |
TiempoConfirmacionPedido |
Milliseconds |
p95, p99 |
Experiència de compra |
El codi, a la botiga:
"""Publicacio de metriques de negoci de MercadoFresco.
S'executa dins de les instancies de l'ASG, que assumeixen el rol
rol-mercadofresco-tienda. Aquest rol te cloudwatch:PutMetricData
restringit a l'espai de noms MercadoFresco/Tienda (04-01).
"""
import time
import boto3
from botocore.config import Config
# Reintents en mode adaptatiu: PutMetricData es idempotent per al
# nostre cas i no volem que una fallada de xarxa tombi una compra.
cw = boto3.client(
"cloudwatch",
region_name="eu-west-1",
config=Config(retries={"max_attempts": 3, "mode": "adaptive"}),
)
ESPAI = "MercadoFresco/Tienda"
def registrar_comanda_confirmada(import_eur, milisegons, metode_pagament, provincia):
"""Publica les metriques d'una comanda acabada de confirmar."""
dimensions_base = [
{"Name": "Entorno", "Value": "produccion"},
{"Name": "Componente", "Value": "tienda"},
]
cw.put_metric_data(
Namespace=ESPAI,
MetricData=[
# 1. Comptador global, sense mes dimensions que les base.
{
"MetricName": "PedidosConfirmados",
"Dimensions": dimensions_base,
"Value": 1,
"Unit": "Count",
"Timestamp": time.time(),
},
# 2. Desglossament per metode de pagament: cardinalitat baixa i tancada.
{
"MetricName": "PedidosConfirmados",
"Dimensions": dimensions_base + [
{"Name": "MetodoPago", "Value": metode_pagament},
],
"Value": 1,
"Unit": "Count",
},
# 3. Latencia de negoci: el temps que el client ha esperat.
{
"MetricName": "TiempoConfirmacionPedido",
"Dimensions": dimensions_base,
"Value": milisegons,
"Unit": "Milliseconds",
},
# 4. Import, per detectar comandes anomales.
{
"MetricName": "ImportePedido",
"Dimensions": dimensions_base,
"Value": import_eur,
"Unit": "None",
},
],
)Detalls que importen i que no són obvis:
Timestampés opcional; si no el poses, CloudWatch fa servir l'hora de recepció. Pots publicar dades de fins a 2 setmanes d'antiguitat i fins a 2 hores en el futur. Això permet recuperar mètriques d'un procés per lots que ha fallat.- La unitat importa poc a CloudWatch però molt a les alarmes. Si publiques en
Millisecondsi l'alarma esperaSeconds, l'alarma no troba dades i es queda enINSUFFICIENT_DATAper sempre. Sigues coherent. - Publicar
Value: 1repetidament és correcte. CloudWatch suma els valors del període quan consultes ambSum. No cal que l'aplicació mantingui un comptador. provinciano es fa servir com a dimensió en aquest exemple. Espanya té 52 províncies: seria acceptable, però multiplicaria per 52 el nombre de mètriques (15,60 USD/mes només per això). La Marta va decidir que aquest desglossament viu als informes de la Sara sobremercadofresco-informes-analitica, no a CloudWatch.
L'error de rendiment que gairebé ningú no veu venir
El codi anterior fa una crida HTTPS a l'API de CloudWatch dins de la ruta crítica d'una compra. Amb 900 comandes/hora funciona; amb un pic seriós, aquesta crida de 40 ms se suma al temps de resposta que el client percep, i si l'API de CloudWatch s'alenteix, la teva botiga s'alenteix.
Tres solucions, de pitjor a millor:
- Embolcallar-ho en un
try/excepti no fallar mai. Mínim indispensable: una mètrica no ha de tombar una venda. - Acumular en memòria i publicar per lots cada 20 segons des d'un fil a part (secció següent).
- Escriure la mètrica al registre en format EMF i deixar que CloudWatch l'extregui (secció de Lambda). Cost zero a la ruta crítica.
PutMetricData per lots i valors estadístics
PutMetricData accepta fins a 1.000 punts per crida (amb un límit d'1 MB de cos). Publicar
per lots redueix el cost d'API (0,01 USD per cada 1.000 crides) i treu la latència de la ruta
crítica.
"""Publicador per lots: acumula en memoria i buida cada 20 segons."""
import threading
import queue
import boto3
cw = boto3.client("cloudwatch", region_name="eu-west-1")
_cua = queue.Queue(maxsize=10000)
def encuar(nom, valor, unitat="Count", dimensions=None):
"""No bloqueja mai: si la cua es plena, es descarta el punt."""
try:
_cua.put_nowait({
"MetricName": nom,
"Value": valor,
"Unit": unitat,
"Dimensions": dimensions or [
{"Name": "Entorno", "Value": "produccion"},
{"Name": "Componente", "Value": "tienda"},
],
})
except queue.Full:
pass # Perdre una metrica es preferible a bloquejar una compra.
def _buidar():
while True:
lot = []
# Espera bloquejant per al primer element; la resta sense esperar.
lot.append(_cua.get())
while len(lot) < 1000:
try:
lot.append(_cua.get_nowait())
except queue.Empty:
break
try:
for i in range(0, len(lot), 1000):
cw.put_metric_data(
Namespace="MercadoFresco/Tienda",
MetricData=lot[i:i + 1000],
)
except Exception as e: # noqa: BLE001
print(f"No s'han pogut publicar metriques: {e}")
threading.Thread(target=_buidar, daemon=True).start()Valors estadístics: 900 punts en un
Si només necessites Sum, Average, Min i Max, pots preagregar-ho tu i enviar un únic punt amb
StatisticValues. Una crida en lloc de nou-centes:
cw.put_metric_data(
Namespace="MercadoFresco/Tienda",
MetricData=[{
"MetricName": "TiempoConfirmacionPedido",
"Dimensions": [{"Name": "Entorno", "Value": "produccion"}],
"StatisticValues": {
"SampleCount": 900, # 900 comandes en l'ultima hora
"Sum": 1_620_000.0, # suma de milisegons
"Minimum": 410.0,
"Maximum": 9_480.0,
},
"Unit": "Milliseconds",
}],
)El preu d'aquesta optimització: amb StatisticValues perds els percentils. CloudWatch només
rep quatre números; no pot calcular un p99 a partir d'ells. Com que el p95 de
TiempoConfirmacionPedido és precisament el que la Marta vol vigilar, MercadoFresco no fa servir
StatisticValues per a aquesta mètrica. Sí que la fa servir per a ImportePedido, on el Sum i el
Maximum són tot el que interessa.
Una tercera opció, intermèdia, és el camp Values + Counts, que envia valors diferents amb la seva
multiplicitat i sí que conserva percentils:
cw.put_metric_data(
Namespace="MercadoFresco/Tienda",
MetricData=[{
"MetricName": "TiempoConfirmacionPedido",
"Values": [410.0, 520.0, 610.0, 9480.0],
"Counts": [300.0, 450.0, 140.0, 10.0], # 900 mostres en total
"Unit": "Milliseconds",
}],
)L'agent unificat de CloudWatch a les instàncies de l'ASG
Per a memòria i disc cal un agent dins de la instància. L'agent unificat de CloudWatch
(amazon-cloudwatch-agent) fa dues feines alhora: publica mètriques del sistema i envia fitxers
de registre a CloudWatch Logs. Substitueix els antics scripts de Perl i l'awslogs, ja en desús.
- Permisos
El rol rol-mercadofresco-tienda necessita això afegit a pol-mercadofresco-tienda:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicarMetricasDelAgente",
"Effect": "Allow",
"Action": "cloudwatch:PutMetricData",
"Resource": "*",
"Condition": {
"StringEquals": { "cloudwatch:namespace": "MercadoFresco/Sistema" }
}
},
{
"Sid": "EscribirRegistros",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:eu-west-1:111122223333:log-group:/mercadofresco/*"
},
{
"Sid": "LeerLaConfiguracionDelAgente",
"Effect": "Allow",
"Action": "ssm:GetParameter",
"Resource": "arn:aws:ssm:eu-west-1:111122223333:parameter/mercadofresco/produccion/cloudwatch-agent"
}
]
}Fixa't en dues coses. cloudwatch:PutMetricData no admet ARN de recurs —per això "Resource": "*"
amb la condició d'espai de noms, exactament el patró que vam estudiar a 04-01—. I la
configuració de l'agent es desa a Parameter Store (04-03), que és la manera neta que totes
les instàncies de l'ASG en comparteixin la mateixa sense coure-la a l'AMI.
- El fitxer de configuració
{
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "cwagent",
"region": "eu-west-1"
},
"metrics": {
"namespace": "MercadoFresco/Sistema",
"append_dimensions": {
"AutoScalingGroupName": "${aws:AutoScalingGroupName}",
"InstanceId": "${aws:InstanceId}"
},
"aggregation_dimensions": [
["AutoScalingGroupName"],
[]
],
"metrics_collected": {
"mem": {
"measurement": [
{ "name": "mem_used_percent", "rename": "MemoriaUsadaPorcentaje", "unit": "Percent" },
{ "name": "mem_available", "unit": "Bytes" }
],
"metrics_collection_interval": 60
},
"disk": {
"resources": ["/", "/var/log"],
"measurement": [
{ "name": "used_percent", "rename": "DiscoUsadoPorcentaje", "unit": "Percent" },
{ "name": "inodes_free" }
],
"ignore_file_system_types": ["sysfs", "devtmpfs", "tmpfs", "overlay"],
"metrics_collection_interval": 300
},
"swap": {
"measurement": ["swap_used_percent"]
},
"procstat": [
{
"pattern": "gunicorn",
"measurement": ["cpu_usage", "memory_rss"]
}
]
}
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/mercadofresco/aplicacion.log",
"log_group_name": "/mercadofresco/tienda/aplicacion",
"log_stream_name": "{instance_id}",
"retention_in_days": 30,
"timestamp_format": "%Y-%m-%d %H:%M:%S",
"timezone": "UTC",
"multi_line_start_pattern": "{timestamp_format}"
},
{
"file_path": "/var/log/nginx/access.log",
"log_group_name": "/mercadofresco/tienda/nginx-acceso",
"log_stream_name": "{instance_id}",
"retention_in_days": 14
},
{
"file_path": "/var/log/nginx/error.log",
"log_group_name": "/mercadofresco/tienda/nginx-error",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
}
]
}
}
}
}Repàs de les decisions preses allà, perquè cadascuna té el seu motiu:
namespacepropiMercadoFresco/Sistema, separat deMercadoFresco/Tienda. Mètriques de màquina i mètriques de negoci no es barregen: facilita els permisos i els taulers.append_dimensionsamb${aws:AutoScalingGroupName}: l'agent resol aquestes variables sol, consultant les metadades de la instància. No cal generar un fitxer per instància.aggregation_dimensionsamb[](llista buida) publica també la mètrica agregada de tot el grup. És la que es fa servir al tauler: a la Marta li interessa «memòria mitjana de l'ASG», no la de la instànciai-0abc.multi_line_start_pattern: sense això, una traça de Python de 30 línies es converteix en 30 esdeveniments de registre inconnexos i les consultes d'Insights no troben res.retention_in_daysa la configuració mateixa: l'agent crea el grup amb retenció finita des del primer dia. És la línia que més diners estalvia de tot el fitxer.procstatvigila el procésgunicornconcret. Si el procés mor isystemdel reinicia en bucle, la CPU de la instància sembla normal peròprocstatel delata.
- Desar la configuració i desplegar-la amb
user data
user data# Desar la configuracio a Parameter Store (04-03)
aws ssm put-parameter \
--name /mercadofresco/produccion/cloudwatch-agent \
--type String \
--tier Standard \
--value file://cloudwatch-agent.json \
--overwrite \
--profile mercadofresco-dev --region eu-west-1I el fragment que s'afegeix al user data de la plantilla lt-mercadofresco-tienda (02-01):
#!/bin/bash
set -euo pipefail
# Amazon Linux 2023 porta el paquet als seus repositoris.
dnf install -y amazon-cloudwatch-agent
mkdir -p /var/log/mercadofresco
# Arrencar l'agent llegint la configuracio des de Parameter Store.
# El prefix ssm: diu a l'agent que l'argument es un parametre, no un fitxer.
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config \
-m ec2 \
-c ssm:/mercadofresco/produccion/cloudwatch-agent \
-s
# Comprovacio: si l'agent no arrenca, que la instancia falli el health check
# i l'ASG la reemplaci, en lloc de quedar-se cega en silenci.
systemctl is-active amazon-cloudwatch-agent || exit 1Després d'actualitzar el user data cal crear una nova versió de la plantilla de
llançament i fer un instance refresh de l'ASG:
aws ec2 create-launch-template-version \
--launch-template-name lt-mercadofresco-tienda \
--source-version '$Latest' \
--launch-template-data file://datos-plantilla.json \
--profile mercadofresco-dev
aws autoscaling start-instance-refresh \
--auto-scaling-group-name asg-mercadofresco-tienda \
--preferences '{"MinHealthyPercentage": 50, "InstanceWarmup": 180}' \
--profile mercadofresco-devCost de l'agent: el programari és gratuït; es paga pel que publica. Amb 4 mètriques del sistema
agregades a nivell d'ASG més les de cada instància, MercadoFresco publica unes 20 mètriques
personalitzades: 6 USD al mes. La verificació importa: si deixes les dimensions per instància sense
agregar i l'ASG rota instàncies tot el dia, cada InstanceId nou crea mètriques noves. Són
mètriques que deixen de rebre dades —i per tant de facturar-se després del període de facturació—, però
embruten els taulers.
Embedded Metric Format des de Lambda
A Lambda, cridar PutMetricData és especialment car: la funció està facturada per
mil·lisegon, així que esperar 40 ms l'API de CloudWatch es paga literalment. I si la Lambda
s'invoca 50.000 vegades al dia, són 50.000 crides d'API.
L'Embedded Metric Format (EMF) resol això de manera elegant: escrius un JSON amb una
estructura especial a stdout, i CloudWatch Logs extreu les mètriques automàticament en
ingerir-lo. Cost a la funció: el d'un print.
"""EMF a mercadofresco-generar-miniaturas.
Escriure el JSON a stdout ja n'hi ha prou: CloudWatch Logs detecta el bloc _aws
i publica les metriques a l'espai MercadoFresco/Miniaturas.
"""
import json
import time
import os
def emetre_metriques(milisegons, bytes_origen, clau_s3, correcte):
document = {
"_aws": {
"Timestamp": int(time.time() * 1000), # en milisegons
"CloudWatchMetrics": [
{
"Namespace": "MercadoFresco/Miniaturas",
"Dimensions": [["Entorno"], ["Entorno", "Formato"]],
"Metrics": [
{"Name": "TiempoProceso", "Unit": "Milliseconds"},
{"Name": "TamanoOrigen", "Unit": "Bytes"},
{"Name": "MiniaturasGeneradas", "Unit": "Count"},
],
}
],
},
# Dimensions: han d'apareixer tambe com a camps de primer nivell.
"Entorno": "produccion",
"Formato": clau_s3.rsplit(".", 1)[-1].lower(),
# Valors de les metriques.
"TiempoProceso": milisegons,
"TamanoOrigen": bytes_origen,
"MiniaturasGeneradas": 1 if correcte else 0,
# Propietats: NO es converteixen en metriques; queden al registre
# i son consultables amb Logs Insights. Aqui va el de cardinalitat alta.
"claveS3": clau_s3,
"funcion": os.environ.get("AWS_LAMBDA_FUNCTION_NAME"),
"peticionId": os.environ.get("_X_AMZN_TRACE_ID", ""),
}
print(json.dumps(document))La clau conceptual és a les dues últimes seccions del document:
| Camp | Es converteix en mètrica? | Consultable amb Insights? | Cardinalitat admesa |
|---|---|---|---|
Llistat a Metrics |
Sí | Sí | — |
Llistat a Dimensions |
És dimensió | Sí | Baixa |
Qualsevol altre (claveS3) |
No | Sí | Qualsevol |
És a dir: EMF et permet tenir la clau de l'objecte d'S3 i l'ID de la petició al costat de la mètrica,
sense pagar el preu de la cardinalitat. Quan el p99 de TiempoProceso es dispari, una consulta
d'Insights et dirà exactament quins fitxers van ser els lents. Això amb PutMetricData és impossible.
Nota pràctica: la biblioteca aws-embedded-metrics d'AWS fa tot això per tu amb un decorador. Aquí
s'ha escrit el JSON a mà perquè es vegi l'estructura, que és el que cal entendre.
Cost d'EMF: es paga la ingesta del registre (uns 0,63 USD/GB a eu-west-1) i les mètriques
extretes (0,30 USD cadascuna). No es paguen crides d'API. Per a funcions d'alta freqüència surt
clarament a compte.
Registres: grups, fluxos i retenció
La jerarquia és simple i convé tenir-la clara:
- Grup de registres (log group): el contenidor. Aquí es configura la retenció, el
xifratge amb KMS i les subscripcions. Exemple:
/mercadofresco/tienda/aplicacion. - Flux de registres (log stream): una seqüència d'esdeveniments d'una sola font. Una
instància, una execució de Lambda. Exemple:
i-0abc123def456. - Esdeveniment: una línia amb marca de temps i missatge. Màxim 256 KB.
flowchart TD
G["/mercadofresco/tienda/aplicacion<br/>retencio 30 dies, KMS"]
G --> S1["i-0abc123<br/>esdeveniments"]
G --> S2["i-0def456<br/>esdeveniments"]
G --> S3["i-0ghi789<br/>esdeveniments"]
G -.->|filtre de metriques| M["MercadoFresco/Tienda<br/>PedidosFallidos"]
G -.->|subscripcio| F["Lambda / Firehose /<br/>OpenSearch"]
G -.->|exportacio| B["s3://mercadofresco-registros-web"]
La retenció infinita, l'error més car de CloudWatch
Per defecte, un grup de registres es crea amb retenció Never expire. Ningú no ho canvia. Tres
anys després, MercadoFresco tindria 400 GB de registres d'nginx de 2023 costant 0,03 USD/GB/mes —12 USD
mensuals per dades que ningú no llegirà mai— i creixent.
I hi ha una cosa pitjor que el cost: una consulta de Logs Insights escaneja el que li demanis, i a 0,0063 USD per GB escanejat, una cerca descuidada sobre tres anys de registres costa més que la cerca mateixa.
La política de MercadoFresco:
| Grup de registres | Origen | Retenció | Motiu |
|---|---|---|---|
/mercadofresco/tienda/aplicacion |
Agent | 30 dies | Diagnòstic operatiu |
/mercadofresco/tienda/nginx-acceso |
Agent | 14 dies | Volum alt, poc valor passada la setmana |
/mercadofresco/tienda/nginx-error |
Agent | 30 dies | |
/aws/lambda/mercadofresco-generar-miniaturas |
Lambda | 14 dies | |
/aws/lambda/mercadofresco-estado-pedido |
Lambda | 30 dies | Toca comandes |
/aws/rds/instance/mercadofresco-pedidos/postgresql |
RDS | 7 dies | Consultes lentes |
aws-waf-logs-mercadofresco |
WAF (04-05) | 30 dies | Anàlisi de falsos positius |
flowlogs-mercadofresco |
VPC (03-01) | 7 dies | Enorme; s'arxiva a S3 |
I l'ordre que cal executar el mateix dia que es crea un grup:
aws logs put-retention-policy \
--log-group-name /mercadofresco/tienda/aplicacion \
--retention-in-days 30 \
--profile mercadofresco-dev --region eu-west-1Una auditoria útil que la Marta executa un cop al mes, per trobar els grups que se li han colat sense retenció:
aws logs describe-log-groups \
--query "logGroups[?retentionInDays==null].[logGroupName,storedBytes]" \
--output table \
--profile mercadofresco-dev --region eu-west-1A 05-04 veurem com AWS Config converteix aquesta auditoria manual en una regla que salta sola.
Classes de registre: Standard enfront d'Infrequent Access
CloudWatch Logs té dues classes d'emmagatzematge:
Standard |
Infrequent Access |
|
|---|---|---|
| Ingesta | ~0,63 USD/GB | ~0,32 USD/GB (la meitat) |
| Logs Insights | Sí | Sí |
| Live Tail, filtres de mètriques, alarmes | Sí | No |
| Subscripcions | Sí | Limitades |
Per a flowlogs-mercadofresco, que es consulta només quan hi ha una investigació de xarxa, la classe
Infrequent Access estalvia la meitat. Per a /mercadofresco/tienda/aplicacion, del qual depenen
filtres de mètriques i alarmes, cal quedar-se a Standard.
Els registres que MercadoFresco té dispersos
Aquesta era una de les cinc preguntes obertes del mòdul 4: «els registres de WAF, VPC, ALB i Lambda s'acumulen en cinc llocs sense correlacionar». Vegem el mapa real:
| Registre | On és | És a CloudWatch Logs? | Com es consulta |
|---|---|---|---|
| Aplicació de la botiga | Agent → Logs | Sí | Logs Insights |
nginx accés/error |
Agent → Logs | Sí | Logs Insights |
| Lambdes | Automàtic | Sí | Logs Insights |
| WAF (04-05) | aws-waf-logs-mercadofresco |
Sí | Logs Insights |
| VPC Flow Logs (03-01) | flowlogs-mercadofresco |
Sí | Logs Insights |
| PostgreSQL d'RDS | Exportat a Logs | Sí (cal activar-ho) | Logs Insights |
| Registres d'accés de l'ALB | S3 | No | Athena (05-03) |
| Registres de CloudFront | S3 | No (o Logs v2) | Athena |
| Registres d'accés d'S3 | S3 | No | Athena |
| Crides a l'API d'AWS | CloudTrail → S3 | Opcional | 05-03 |
L'honestedat importa aquí: CloudWatch Logs no ho centralitza tot. Els registres d'accés de l'ALB i de CloudFront van a S3 per disseny, perquè el seu volum faria prohibitiva la ingesta a Logs. La centralització real de MercadoFresco és a dos nivells:
- Diagnòstic en calent (últims dies): CloudWatch Logs Insights.
- Anàlisi històrica i forense: S3 + Athena, que es veu a 05-03.
Activar els registres de PostgreSQL a la instància RDS, que sí que falta:
aws rds modify-db-instance \
--db-instance-identifier mercadofresco-pedidos \
--cloudwatch-logs-export-configuration '{"EnableLogTypes":["postgresql","upgrade"]}' \
--apply-immediately \
--profile mercadofresco-dev --region eu-west-1
# I al grup de parametres, registrar les consultes de mes d'un segon
aws rds modify-db-parameter-group \
--db-parameter-group-name pg-mercadofresco-pedidos \
--parameters "ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate" \
--profile mercadofresco-dev --region eu-west-1Aquest log_min_duration_statement=1000 serà decisiu a la secció de la comanda de vuit segons.
Logs Insights: sintaxi i consultes reals
CloudWatch Logs Insights és un llenguatge de consulta sobre els registres. No és SQL, tot i que
s'hi assembla: és una canonada d'ordres separades per |.
| Ordre | Per a què |
|---|---|
fields |
Triar/crear camps |
filter |
Filtrar esdeveniments |
stats |
Agregar (count, avg, sum, pct, min, max) |
sort |
Ordenar |
limit |
Limitar resultats (per defecte 1.000) |
parse |
Extreure camps de text no estructurat |
dedup |
Eliminar duplicats |
display |
Triar què es mostra |
Els camps @timestamp, @message, @logStream i @log existeixen sempre. Si el teu registre és JSON,
Insights el descompon sol i pots referir-te a nivel, pedido_id, duracion_ms directament. Aquesta
és la raó número u per registrar en JSON des del primer dia.
Consulta 1: errors de la botiga agrupats
fields @timestamp, @message, nivel, pedido_id, duracion_ms | filter nivel = "ERROR" | stats count() as errors by bin(5m), tipo_error | sort errors desc
bin(5m) agrupa per finestres de cinc minuts: és el que converteix una llista d'errors en un gràfic
de tendència. En executar-la, Insights ofereix «Visualization» i aquest resultat es pot afegir
directament al tauler.
Consulta 2: els registres de WAF (04-05)
fields @timestamp, httpRequest.clientIp as ip, httpRequest.uri as ruta,
terminatingRuleId as regla, action
| filter action = "BLOCK"
| stats count() as bloquejos by regla, ruta
| sort bloquejos desc
| limit 25Aquesta és la consulta que a 04-05 decidia el pas de Count a Block. Ara saps exactament on
s'executa.
Consulta 3: la Lambda d'estat de comanda
fields @timestamp, @message, @duration, @billedDuration, @maxMemoryUsed
| filter @type = "REPORT"
| stats count() as invocacions,
avg(@duration) as mitjana_ms,
pct(@duration, 95) as p95_ms,
pct(@duration, 99) as p99_ms,
max(@maxMemoryUsed) / 1000000 as memoria_max_mb
by bin(1h)Els camps @duration, @billedDuration, @maxMemoryUsed i @initDuration els genera Lambda
automàticament a la línia REPORT de cada invocació. @maxMemoryUsed enfront de la memòria
configurada és la manera més directa d'ajustar la mida d'una funció: si en fa servir 80 MB de 512
configurats, estàs pagant de més.
Consulta 4: flow logs de la VPC (03-01)
fields @timestamp, srcAddr, dstAddr, dstPort, action, bytes | filter action = "REJECT" and dstPort = 5432 | stats count() as intents, sum(bytes) as total by srcAddr | sort intents desc
Algú intentant arribar al port 5432 i sent rebutjat per sg-mercadofresco-basedatos. Si aquesta
IP és interna, és un error de configuració. Si és externa, és un escaneig.
Consulta 5: la més útil de totes, parse sobre registres sense estructura
parse @message /(?<ip>\d+\.\d+\.\d+\.\d+) .* "(?<metode>\w+) (?<ruta>\S+).*" (?<estat>\d{3}) (?<bytes>\d+) (?<temps>[\d.]+)/
| filter estat >= 500
| stats count() as errors by ruta, estat
| sort errors descparse amb expressió regular i grups amb nom converteix el registre d'accés d'nginx en camps
consultables. És el rescat per a tot el programari que no registra en JSON.
Executar Insights des de la CLI
ID_CONSULTA=$(aws logs start-query \
--log-group-names /mercadofresco/tienda/aplicacion \
--start-time $(date -d '2 hours ago' +%s) \
--end-time $(date +%s) \
--query-string 'fields @timestamp, @message | filter nivel = "ERROR" | limit 50' \
--query 'queryId' --output text \
--profile mercadofresco-dev --region eu-west-1)
sleep 5
aws logs get-query-results --query-id "$ID_CONSULTA" \
--profile mercadofresco-dev --region eu-west-1Avís de cost: cada execució cobra per GB escanejats, no per resultats retornats. Acotar el rang temporal és el que decideix la factura: la mateixa consulta sobre 1 hora o sobre 30 dies costa 720 vegades més en el segon cas. Redueix sempre la finestra abans de refinar la consulta.
Respondre a «per què va trigar vuit segons aquella comanda?»
Aquesta és la segona pregunta que va deixar oberta el mòdul 4. Un client escriu: «dijous a la tarda vaig trigar uns vuit segons a tenir la comanda confirmada». La Marta té el seu correu i l'hora aproximada.
Requisit previ: un identificador de correlació que travessi tots els components. Sense ell, això
és impossible. MercadoFresco afegeix a nginx una capçalera X-Peticion-Id que l'aplicació propaga a
la Lambda i escriu a cada línia de registre.
Pas 1. Confirmar que el problema existeix i acotar-lo.
fields @timestamp, duracion_ms, ruta, peticion_id, cliente_hash | filter ruta = "/api/pedidos/confirmar" and duracion_ms > 5000 | sort @timestamp desc | limit 50
Apareixen 34 peticions lentes dijous entre les 18:40 i les 19:20. No era cosa d'un client.
Pas 2. Trobar la petició concreta.
fields @timestamp, peticion_id, duracion_ms, pedido_id | filter cliente_hash = "a3f8c1e9" and duracion_ms > 5000
En surt una: peticion_id = 7f3a9b2c, duracion_ms = 8140.
Pas 3. Seguir aquest identificador per tots els grups alhora. Insights permet consultar fins a 50 grups de registres en una sola consulta, i aquesta és la resposta real a «els registres són en cinc llocs sense correlacionar»:
aws logs start-query \
--log-group-names \
/mercadofresco/tienda/aplicacion \
/mercadofresco/tienda/nginx-acceso \
/aws/lambda/mercadofresco-estado-pedido \
/aws/rds/instance/mercadofresco-pedidos/postgresql \
--start-time $(date -d '2026-07-30 18:55' +%s) \
--end-time $(date -d '2026-07-30 19:05' +%s) \
--query-string 'fields @timestamp, @log, @message
| filter @message like /7f3a9b2c/
| sort @timestamp asc' \
--profile mercadofresco-dev --region eu-west-1Pas 4. Llegir la cronologia.
| Hora | Grup | Missatge | Δ |
|---|---|---|---|
| 18:58:12,004 | nginx-acceso |
POST /api/pedidos/confirmar |
— |
| 18:58:12,031 | aplicacion |
inicio confirmacion pedido=48213 |
+27 ms |
| 18:58:12,088 | aplicacion |
validacion de stock ok |
+57 ms |
| 18:58:12,140 | aplicacion |
llamada a lambda estado-pedido |
+52 ms |
| 18:58:12,690 | estado-pedido |
REPORT Duration: 480 ms |
+550 ms |
| 18:58:12,700 | aplicacion |
insertando lineas de pedido |
+10 ms |
| 18:58:20,110 | postgresql |
duration: 7402.115 ms statement: SELECT ... |
+7.410 ms |
| 18:58:20,144 | aplicacion |
pedido confirmado en 8140 ms |
+34 ms |
Trobat. No era la botiga (27 + 57 + 52 + 10 + 34 = 180 ms), ni la Lambda (550 ms): eren
7,4 segons dins de PostgreSQL. Els registres de log_min_duration_statement que vam activar
abans són els que han cantat.
Pas 5. Veure quina consulta era.
I apareix una consulta que fa un SELECT de preu per cada línia de la comanda, en un bucle, en lloc
d'un sol SELECT ... WHERE id IN (...). És el patró N+1: una comanda amb 38 productes executa 39
consultes.
Què acabem de fer i què no. Hem localitzat el problema amb registres correlacionats, i ha funcionat perquè MercadoFresco tenia un identificador de petició propagat a mà. Però fixa't en l'esforç: quatre consultes, una cronologia muntada manualment, i tot recolzat en el fet que algú es va recordar de propagar la capçalera. Existeix una eina dissenyada exactament per a això, que fa el pas 4 automàticament i en un gràfic: és X-Ray, i és la lliçó 05-02. Tornarem a aquesta mateixa comanda de 8,14 segons allà.
Filtres de mètriques: d'un patró de registre a una alarma
No es pot alarmar sobre un registre; sí sobre una mètrica. Un filtre de mètriques és la conversió.
aws logs put-metric-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name filtro-pedidos-fallidos \
--filter-pattern '{ $.nivel = "ERROR" && $.tipo_error = "PAGO_RECHAZADO" }' \
--metric-transformations \
metricName=PedidosFallidos,\
metricNamespace=MercadoFresco/Tienda,\
metricValue=1,\
defaultValue=0 \
--profile mercadofresco-dev --region eu-west-1Tres detalls de camp:
- La sintaxi de
--filter-patternno és la d'Insights. Per a registres JSON fes servir{ $.camp = "valor" }amb&&,||,=,!=,>,<. Per a text pla, cometes i termes:"ERROR" -"ERROR de prueba". defaultValue=0és imprescindible. Sense ell, quan no hi ha errors no es publica cap dada, l'alarma es queda enINSUFFICIENT_DATAi la mètrica té buits que trenquen la detecció d'anomalies. AmbdefaultValue=0la sèrie és contínua.- Els filtres només s'apliquen a esdeveniments nous. No hi ha efecte retroactiu sobre el ja ingerit.
També es pot extreure un valor numèric del registre en lloc de comptar:
aws logs put-metric-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name filtro-duracion-checkout \
--filter-pattern '{ $.ruta = "/api/pedidos/confirmar" && $.duracion_ms > 0 }' \
--metric-transformations \
metricName=DuracionCheckout,\
metricNamespace=MercadoFresco/Tienda,\
metricValue='$.duracion_ms',\
unit=Milliseconds \
--profile mercadofresco-dev --region eu-west-1I provar el patró abans de crear-lo, contra esdeveniments reals, cosa que evita el clàssic filtre que no coincideix mai:
aws logs test-metric-filter \
--filter-pattern '{ $.nivel = "ERROR" && $.tipo_error = "PAGO_RECHAZADO" }' \
--log-event-messages \
'{"nivel":"ERROR","tipo_error":"PAGO_RECHAZADO","pedido_id":48213}' \
'{"nivel":"INFO","mensaje":"pedido ok"}' \
--profile mercadofresco-dev --region eu-west-1Subscripcions i exportació a S3
Dues maneres de treure registres de CloudWatch, amb propòsits diferents:
| Filtre de subscripció | Exportació a S3 | |
|---|---|---|
| Latència | Temps real (segons) | Per lots, fins a 12 h |
| Destins | Lambda, Firehose, OpenSearch, Kinesis | Només S3 |
| Ús típic | Processar, reenviar, alertar | Arxivar barat, Athena |
| Cost | Del destí | Només S3 |
| Límit | 2 filtres per grup de registres | — |
Subscripció en temps real cap a una Lambda que reenvia a Slack el que és crític:
aws logs put-subscription-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name suscripcion-criticos \
--filter-pattern '{ $.nivel = "CRITICAL" }' \
--destination-arn arn:aws:lambda:eu-west-1:111122223333:function:mercadofresco-reenviar-criticos \
--profile mercadofresco-dev --region eu-west-1Exportació a S3 per a arxiu barat dels flow logs, que es miraran un cop l'any:
aws logs create-export-task \
--task-name export-flowlogs-2026-07 \
--log-group-name flowlogs-mercadofresco \
--from $(date -d '2026-07-01' +%s)000 \
--to $(date -d '2026-08-01' +%s)000 \
--destination mercadofresco-registros-web \
--destination-prefix flowlogs/2026/07 \
--profile mercadofresco-dev --region eu-west-1El bucket necessita una política que permeti escriure a logs.eu-west-1.amazonaws.com. I compte: només
pot haver-hi una tasca d'exportació activa per compte alhora, i no és incremental. Per a arxivat
continu, l'opció moderna és una subscripció cap a Data Firehose amb destí S3.
Alarmes: llindars, avaluació i dades absents
Una alarma té tres estats:
| Estat | Significa |
|---|---|
OK |
La condició no es compleix. Tot bé. |
ALARM |
La condició es compleix. |
INSUFFICIENT_DATA |
No hi ha dades suficients per decidir. |
I quatre paràmetres que gairebé ningú no configura bé a la primera:
--period: la finestra d'agregació. 60, 300…--evaluation-periods: quants períodes es miren.--datapoints-to-alarm: quants d'aquests han d'incomplir. Si s'omet, són tots.--treat-missing-data: què fer amb els buits.
La combinació evaluation-periods + datapoints-to-alarm s'anomena «M de N» i és l'eina
contra el soroll. Compara:
| Configuració | Comportament | Quan |
|---|---|---|
| 1 d'1, període 60 | Salta al primer minut dolent | Només per al que és binari i greu |
| 3 de 3, període 60 | Tres minuts seguits dolents | Sostingut, triga 3 min |
| 2 de 3, període 60 | 2 minuts dolents dels últims 3 | Equilibri recomanat |
| 5 de 5, període 300 | 25 minuts | Tendències lentes: disc |
Amb «2 de 3», un pic d'un sol minut —un desplegament, un garbage collector, una còpia de
seguretat— no desperta ningú, però un problema real que oscil·la sí que es detecta. La configuració
«3 de 3» es perd exactament aquests problemes intermitents.
I --treat-missing-data, quatre opcions:
| Valor | Comportament | Quan fer-lo servir |
|---|---|---|
missing (defecte) |
El buit no compta | Gairebé mai; genera confusió |
notBreaching |
El buit es tracta com a OK |
Mètriques amb trànsit intermitent |
breaching |
El buit es tracta com a ALARM |
Quan l'absència de dades ÉS el problema |
ignore |
L'estat no canvia | Mantenir l'últim estat conegut |
El cas que ho explica tot: l'alarma sobre PedidosConfirmados. Si la botiga cau del tot, deixa
de publicar-se la mètrica. Amb notBreaching, l'alarma es queda en OK mentre la botiga està
morta. És la fallada silenciosa més habitual de CloudWatch, i per això aquesta alarma concreta porta
--treat-missing-data breaching.
Les alarmes que MercadoFresco crea en aquesta lliçó:
# 1. Latencia real vista pel client: p95, no mitjana.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-alb-latencia-alta \
--alarm-description "El p95 de resposta de l'ALB supera 2 s" \
--namespace AWS/ApplicationELB --metric-name TargetResponseTime \
--dimensions Name=LoadBalancer,Value=app/alb-mercadofresco-tienda/50dc6c495c0c9188 \
--extended-statistic p95 \
--period 60 --evaluation-periods 3 --datapoints-to-alarm 2 \
--threshold 2 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--ok-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 2. Destins sans: si baixa de 2, hem perdut la tolerancia a fallades d'AZ.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-alb-destinos-sanos \
--namespace AWS/ApplicationELB --metric-name HealthyHostCount \
--dimensions Name=LoadBalancer,Value=app/alb-mercadofresco-tienda/50dc6c495c0c9188 \
Name=TargetGroup,Value=targetgroup/tg-mercadofresco-tienda/73e2d6bc24d8a067 \
--statistic Minimum --period 60 --evaluation-periods 2 --datapoints-to-alarm 2 \
--threshold 2 --comparison-operator LessThanThreshold \
--treat-missing-data breaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 3. Connexions d'RDS: 160 de les 200 del grup de parametres.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-rds-conexiones-altas \
--namespace AWS/RDS --metric-name DatabaseConnections \
--dimensions Name=DBInstanceIdentifier,Value=mercadofresco-pedidos \
--statistic Maximum --period 60 --evaluation-periods 3 --datapoints-to-alarm 2 \
--threshold 160 --comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 4. Espai lliure d'RDS: en BYTES. 20 GiB = 21474836480.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-rds-disco-bajo \
--namespace AWS/RDS --metric-name FreeStorageSpace \
--dimensions Name=DBInstanceIdentifier,Value=mercadofresco-pedidos \
--statistic Minimum --period 300 --evaluation-periods 2 \
--threshold 21474836480 --comparison-operator LessThanThreshold \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 5. Estrangulament de Lambda: llindar 0, sense tolerancia.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-lambda-estrangulada \
--namespace AWS/Lambda --metric-name Throttles \
--dimensions Name=FunctionName,Value=mercadofresco-estado-pedido \
--statistic Sum --period 60 --evaluation-periods 1 \
--threshold 0 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 6. Memoria de les instancies, gracies a l'agent instal-lat abans.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-tienda-memoria-alta \
--namespace MercadoFresco/Sistema --metric-name MemoriaUsadaPorcentaje \
--dimensions Name=AutoScalingGroupName,Value=asg-mercadofresco-tienda \
--statistic Average --period 300 --evaluation-periods 3 --datapoints-to-alarm 2 \
--threshold 85 --comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 7. Comandes fallides, sobre la metrica del filtre de registres.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-pedidos-fallidos \
--namespace MercadoFresco/Tienda --metric-name PedidosFallidos \
--statistic Sum --period 300 --evaluation-periods 2 --datapoints-to-alarm 2 \
--threshold 10 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1
# 8. La mes important: la botiga ha deixat de vendre.
# treat-missing-data BREACHING perque l'absencia de dada ES l'incident.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-sin-pedidos \
--alarm-description "No hi ha comandes confirmades: la botiga pot estar caiguda" \
--namespace MercadoFresco/Tienda --metric-name PedidosConfirmados \
--dimensions Name=Entorno,Value=produccion Name=Componente,Value=tienda \
--statistic Sum --period 900 --evaluation-periods 1 \
--threshold 1 --comparison-operator LessThanThreshold \
--treat-missing-data breaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1Nota sobre --ok-actions a l'alarma 1: enviar també la recuperació. Sense ella, algú rep un
avís de matinada i no té manera de saber si continua passant. Amb --ok-actions, el segon missatge
diu «ja està bé». És un detall petit que canvia molt l'experiència d'estar de guàrdia.
I una advertència sobre l'alarma 8: PedidosConfirmados < 1 en 15 minuts és correcte per a
l'horari comercial, però a les 4 del matí un diumenge pot ser perfectament normal que no hi hagi
comandes. Aquesta alarma genera falsos positius nocturns. La manera correcta de resoldre-ho no és pujar
el període: és la detecció d'anomalies, dues seccions més avall.
Alarmes compostes
Una alarma composta s'avalua sobre altres alarmes, amb AND, OR i NOT. Serveix per a dues coses
diferents:
- Reduir soroll: avisar només si diversos senyals coincideixen.
- Suprimir alarmes filles durant un manteniment conegut.
El problema real de MercadoFresco: quan la base de dades se satura un divendres, salten alhora
mercadofresco-rds-conexiones-altas, mercadofresco-alb-latencia-alta i mercadofresco-pedidos-fallidos.
Tres SMS a les 19:15 descrivint el mateix incident.
aws cloudwatch put-composite-alarm \
--alarm-name mercadofresco-tienda-degradada \
--alarm-description "La botiga esta degradada: latencia alta I errors de comanda" \
--alarm-rule "ALARM(mercadofresco-alb-latencia-alta) AND (ALARM(mercadofresco-pedidos-fallidos) OR ALARM(mercadofresco-rds-conexiones-altas))" \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--actions-enabled \
--profile mercadofresco-dev --region eu-west-1I ara el pas que la majoria oblida: treure l'acció d'SNS de les alarmes filles. Si no, continues rebent quatre avisos en lloc de tres.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-alb-latencia-alta \
--namespace AWS/ApplicationELB --metric-name TargetResponseTime \
--dimensions Name=LoadBalancer,Value=app/alb-mercadofresco-tienda/50dc6c495c0c9188 \
--extended-statistic p95 --period 60 --evaluation-periods 3 --datapoints-to-alarm 2 \
--threshold 2 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions "" \
--profile mercadofresco-dev --region eu-west-1L'alarma filla continua existint, continua canviant d'estat i continua veient-se al tauler: simplement ja no notifica. L'avís el dona la composta, que conté el diagnòstic.
Supressió durant manteniment: el paràmetre --actions-suppressor permet indicar una altra alarma
—per exemple, una que la Marta posa en ALARM a mà durant un desplegament— que silencia la composta
mentre estigui activa.
Cost: 0,50 USD per alarma composta i mes. Sol sortir a compte només per deixar de despertar algú tres vegades.
Detecció d'anomalies
En lloc d'un llindar fix, CloudWatch entrena un model amb fins a dues setmanes d'història i aprèn el patró: els pics dels divendres, les valls de la matinada, el cicle setmanal. L'alarma salta quan el valor surt de la banda esperada.
És exactament el que resol el fals positiu nocturn de mercadofresco-sin-pedidos: a les 4 de la
matinada el model espera 3 comandes, i 0 és anòmal; a les 19:00 n'espera 900, i 400 també ho és. Un
llindar fix no pot expressar això.
# 1. Crear el detector, que comenca a entrenar.
aws cloudwatch put-anomaly-detector \
--namespace MercadoFresco/Tienda \
--metric-name PedidosConfirmados \
--dimensions Name=Entorno,Value=produccion Name=Componente,Value=tienda \
--stat Sum \
--profile mercadofresco-dev --region eu-west-1
# 2. L'alarma sobre la banda, amb expressio de metrica.
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-pedidos-anomalos \
--alarm-description "Les comandes surten de la banda esperada per a aquesta hora" \
--comparison-operator LessThanLowerThreshold \
--evaluation-periods 2 --datapoints-to-alarm 2 \
--threshold-metric-id ad1 \
--treat-missing-data breaching \
--metrics '[
{
"Id": "m1",
"MetricStat": {
"Metric": {
"Namespace": "MercadoFresco/Tienda",
"MetricName": "PedidosConfirmados",
"Dimensions": [
{"Name": "Entorno", "Value": "produccion"},
{"Name": "Componente", "Value": "tienda"}
]
},
"Period": 300,
"Stat": "Sum"
},
"ReturnData": true
},
{
"Id": "ad1",
"Expression": "ANOMALY_DETECTION_BAND(m1, 2)",
"Label": "PedidosConfirmados (banda esperada)",
"ReturnData": true
}
]' \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1Detalls clau:
LessThanLowerThreshold: només avisa si hi ha menys comandes de les esperades. Que n'hi hagi més és una bona notícia, no una alarma. També existeixenGreaterThanUpperThresholdiLessThanLowerOrGreaterThanUpperThreshold.- El
2d'ANOMALY_DETECTION_BAND(m1, 2)és el nombre de desviacions. Més alt = banda més ampla = menys avisos. Comença a 2 i ajusta amb dades reals. - Necessita història. Els primers dies la banda és enorme i l'alarma no serveix de res. Cal crear-la i esperar.
- Es pot excloure un període de l'entrenament amb
--configurationiExcludedTimeRanges: si hi ha hagut un incident de 6 hores, no vols que el model aprengui que això és normal.
Cost: 0,30 USD per mètrica analitzada i mes, més 0,30 USD per alarma d'anomalia.
I una advertència honesta: la detecció d'anomalies no és màgia. Detecta desviacions del patró històric, no problemes. Una degradació lenta i constant durant tres setmanes es converteix en el nou «normal» i deixa d'avisar. Complementa els llindars fixos; no els substitueix.
Accions d'alarma
| Acció | ARN / forma | Ús a MercadoFresco |
|---|---|---|
| Notificar per SNS | arn:aws:sns:...:alertas-mercadofresco |
Totes les alarmes |
| Autoescalat | ARN de política d'escalat | cpu-objetivo-60 de l'ASG |
| Acció sobre EC2 | arn:aws:automate:eu-west-1:ec2:reboot |
recover, stop, terminate, reboot |
| Acció de Systems Manager | ARN d'OpsItem o incident | Crear tiquets automàtics |
| Cap | — | Alarmes filles d'una composta |
Les accions automàtiques d'EC2 són útils però perilloses. arn:aws:automate:eu-west-1:ec2:recover
sobre StatusCheckFailed_System migra la instància a un altre amfitrió quan falla el maquinari
subjacent, conservant IP i volums: això és clarament bo. En canvi
arn:aws:automate:eu-west-1:ec2:reboot sobre memòria alta és una mala idea: reinicies en bucle una
instància amb una fuita de memòria i amagues el problema en lloc d'arreglar-lo.
Per a les instàncies de l'ASG, a més, l'acció correcta gairebé mai és reiniciar: és deixar que fallin la
comprovació de salut /salud i que el mateix ASG les reemplaci, que és el que vam muntar a 02-01 i 03-03.
Comprovar que l'avís arriba de debò
Aquí hi ha la primera pregunta oberta del mòdul 4, i la més important d'aquesta lliçó: ningú no ha comprovat que un avís d'SNS arribi a un telèfon a les quatre de la matinada.
Una alarma que dispara cap a un tema d'SNS sense subscriptors confirmats és pitjor que no tenir alarma: dona una falsa sensació de cobertura.
Pas 1. Veure qui està subscrit de debò.
aws sns list-subscriptions-by-topic \
--topic-arn arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--query 'Subscriptions[].[Protocol,Endpoint,SubscriptionArn]' \
--output table \
--profile mercadofresco-dev --region eu-west-1Si a la columna SubscriptionArn apareix PendingConfirmation, aquesta subscripció no rep
res. És la troballa més freqüent quan es fa aquesta comprovació per primera vegada: algú va crear la
subscripció fa mesos, el correu de confirmació va anar a la carpeta de correu brossa, i ningú no ho
va saber.
Pas 2. Subscriure els destins que falten.
# Correu de l'equip (cal confirmar l'enllac del missatge rebut)
aws sns subscribe \
--topic-arn arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--protocol email --notification-endpoint [email protected] \
--profile mercadofresco-dev --region eu-west-1
# SMS al telefon de guardia: es confirma sol, no hi ha enllac que calgui premer
aws sns subscribe \
--topic-arn arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--protocol sms --notification-endpoint +34600111222 \
--profile mercadofresco-dev --region eu-west-1Nota sobre SMS. Des de fa uns anys, enviar SMS amb SNS a moltes regions exigeix sortir del «sandbox» d'SMS i, per a números espanyols, registrar remitent i cas d'ús. Si el teu SMS de prova no arriba, revisa l'estat del sandbox abans de donar per trencada l'alarma. El detall complet d'SNS —temes FIFO, polítiques de lliurament, filtres de subscripció— és la lliçó 07-02.
Pas 3. El simulacre. Forçar l'estat de l'alarma.
Aquesta és l'ordre que tanca la pregunta del mòdul 4:
aws cloudwatch set-alarm-state \
--alarm-name mercadofresco-alb-latencia-alta \
--state-value ALARM \
--state-reason "SIMULACRE $(date -u +%FT%TZ): comprovacio trimestral d'avisos" \
--profile mercadofresco-dev --region eu-west-1Això no toca la mètrica: força l'estat de l'alarma i dispara les seves accions de debò. L'SMS i el correu surten. És l'única manera honesta de saber que el canal funciona.
Després es torna a la realitat:
aws cloudwatch set-alarm-state \
--alarm-name mercadofresco-alb-latencia-alta \
--state-value OK \
--state-reason "Fi del simulacre" \
--profile mercadofresco-dev --region eu-west-1Al següent període d'avaluació, CloudWatch recalcula l'estat real amb les dades de la mètrica i el corregeix sol si calgués.
Pas 4. El protocol que adopta MercadoFresco.
| Quan | Què es comprova | Qui |
|---|---|---|
| Cada trimestre | Simulacre amb set-alarm-state sobre 3 alarmes crítiques |
Marta |
| Cada trimestre | Que no hi hagi subscripcions en PendingConfirmation |
Marta |
| En incorporar algú | Se'l subscriu i es fa un simulacre amb el seu telèfon | Marta |
| Quan algú marxa | S'elimina la seva subscripció | Marta |
| Després de cada incident | Va saltar l'alarma correcta? A temps? Hi havia algú? | Post-mortem |
I una comprovació més, que sol descobrir el problema real: fer el simulacre a les 4 de la matinada de debò, una vegada. La Marta ho va fer. Va descobrir que el mòbil de guàrdia estava en «no molestar» i que els SMS de números curts no trencaven el silenci. La solució no va ser d'AWS: va ser configurar una excepció al telèfon. La cadena d'avís inclou el telèfon, i el telèfon també s'ha de provar.
El tauler mercadofresco-produccion
Un tauler de CloudWatch és un document JSON amb una quadrícula de 24 columnes d'ample. Els ginys es
posicionen amb x, y, width, height.
El criteri amb què la Marta l'ha dissenyat —i que és el que cal copiar, més que el JSON— és: a dalt allò que respon «va bé el negoci?», al mig «quin component falla?», a baix el detall. Quan sona el telèfon, es mira de dalt a baix i en 30 segons se sap on anar.
{
"start": "-PT3H",
"periodOverride": "auto",
"widgets": [
{
"type": "text",
"x": 0, "y": 0, "width": 24, "height": 1,
"properties": {
"markdown": "# MercadoFresco - Produccio (eu-west-1) | Guardia: [email protected]"
}
},
{
"type": "metric",
"x": 0, "y": 1, "width": 6, "height": 4,
"properties": {
"title": "Comandes confirmades (ultima hora)",
"view": "singleValue",
"region": "eu-west-1",
"sparkline": true,
"stat": "Sum",
"period": 3600,
"metrics": [
["MercadoFresco/Tienda", "PedidosConfirmados",
"Entorno", "produccion", "Componente", "tienda"]
]
}
},
{
"type": "metric",
"x": 6, "y": 1, "width": 6, "height": 4,
"properties": {
"title": "Temps de confirmacio p95 (ms)",
"view": "singleValue",
"region": "eu-west-1",
"sparkline": true,
"stat": "p95",
"period": 300,
"metrics": [
["MercadoFresco/Tienda", "TiempoConfirmacionPedido",
"Entorno", "produccion", "Componente", "tienda"]
]
}
},
{
"type": "alarm",
"x": 12, "y": 1, "width": 12, "height": 4,
"properties": {
"title": "Estat de les alarmes critiques",
"alarms": [
"arn:aws:cloudwatch:eu-west-1:111122223333:alarm:mercadofresco-tienda-degradada",
"arn:aws:cloudwatch:eu-west-1:111122223333:alarm:mercadofresco-alb-latencia-alta",
"arn:aws:cloudwatch:eu-west-1:111122223333:alarm:mercadofresco-alb-destinos-sanos",
"arn:aws:cloudwatch:eu-west-1:111122223333:alarm:mercadofresco-rds-conexiones-altas",
"arn:aws:cloudwatch:eu-west-1:111122223333:alarm:mercadofresco-pedidos-anomalos"
]
}
},
{
"type": "metric",
"x": 0, "y": 5, "width": 12, "height": 6,
"properties": {
"title": "ALB - latencia i errors",
"view": "timeSeries",
"stacked": false,
"region": "eu-west-1",
"period": 60,
"yAxis": {
"left": { "label": "segons", "showUnits": false },
"right": { "label": "errors", "showUnits": false }
},
"metrics": [
["AWS/ApplicationELB", "TargetResponseTime",
"LoadBalancer", "app/alb-mercadofresco-tienda/50dc6c495c0c9188",
{ "stat": "p50", "label": "p50" }],
["...", { "stat": "p95", "label": "p95" }],
["...", { "stat": "p99", "label": "p99" }],
["AWS/ApplicationELB", "HTTPCode_Target_5XX_Count",
"LoadBalancer", "app/alb-mercadofresco-tienda/50dc6c495c0c9188",
{ "stat": "Sum", "yAxis": "right", "label": "5XX aplicacio", "color": "#d62728" }],
["AWS/ApplicationELB", "HTTPCode_ELB_5XX_Count",
"LoadBalancer", "app/alb-mercadofresco-tienda/50dc6c495c0c9188",
{ "stat": "Sum", "yAxis": "right", "label": "5XX balancejador", "color": "#ff7f0e" }]
],
"annotations": {
"horizontal": [
{ "label": "Objectiu p95", "value": 2, "color": "#d62728", "fill": "above" }
]
}
}
},
{
"type": "metric",
"x": 12, "y": 5, "width": 12, "height": 6,
"properties": {
"title": "Base de dades - mercadofresco-pedidos",
"view": "timeSeries",
"region": "eu-west-1",
"period": 60,
"metrics": [
["AWS/RDS", "DatabaseConnections",
"DBInstanceIdentifier", "mercadofresco-pedidos",
{ "stat": "Maximum", "label": "Connexions" }],
["AWS/RDS", "CPUUtilization",
"DBInstanceIdentifier", "mercadofresco-pedidos",
{ "stat": "Average", "label": "CPU %", "yAxis": "right" }],
["AWS/RDS", "ReplicaLag",
"DBInstanceIdentifier", "mercadofresco-pedidos-lectura",
{ "stat": "Maximum", "label": "Retard replica (s)", "yAxis": "right" }]
],
"annotations": {
"horizontal": [
{ "label": "Limit de connexions", "value": 200, "color": "#d62728" },
{ "label": "Alarma", "value": 160, "color": "#ff7f0e" }
]
}
}
},
{
"type": "metric",
"x": 0, "y": 11, "width": 8, "height": 6,
"properties": {
"title": "ASG i sistema",
"view": "timeSeries",
"region": "eu-west-1",
"period": 60,
"metrics": [
["AWS/EC2", "CPUUtilization",
"AutoScalingGroupName", "asg-mercadofresco-tienda",
{ "stat": "Average", "label": "CPU mitjana %" }],
["MercadoFresco/Sistema", "MemoriaUsadaPorcentaje",
"AutoScalingGroupName", "asg-mercadofresco-tienda",
{ "stat": "Average", "label": "Memoria %" }],
["AWS/AutoScaling", "GroupInServiceInstances",
"AutoScalingGroupName", "asg-mercadofresco-tienda",
{ "stat": "Maximum", "label": "Instancies", "yAxis": "right" }]
],
"annotations": {
"horizontal": [
{ "label": "Maxim de l'ASG", "value": 4, "color": "#d62728", "yAxis": "right" }
]
}
}
},
{
"type": "metric",
"x": 8, "y": 11, "width": 8, "height": 6,
"properties": {
"title": "Lambda",
"view": "timeSeries",
"region": "eu-west-1",
"period": 60,
"metrics": [
["AWS/Lambda", "Duration", "FunctionName", "mercadofresco-estado-pedido",
{ "stat": "p99", "label": "estado-pedido p99 (ms)" }],
["AWS/Lambda", "Errors", "FunctionName", "mercadofresco-estado-pedido",
{ "stat": "Sum", "label": "Errors", "yAxis": "right", "color": "#d62728" }],
["AWS/Lambda", "Throttles", "FunctionName", "mercadofresco-estado-pedido",
{ "stat": "Sum", "label": "Estrangulaments", "yAxis": "right", "color": "#ff7f0e" }],
["AWS/Lambda", "Errors", "FunctionName", "mercadofresco-generar-miniaturas",
{ "stat": "Sum", "label": "Miniatures: errors", "yAxis": "right" }]
]
}
},
{
"type": "metric",
"x": 16, "y": 11, "width": 8, "height": 6,
"properties": {
"title": "Vora: CloudFront i WAF",
"view": "timeSeries",
"region": "us-east-1",
"period": 300,
"metrics": [
["AWS/CloudFront", "CacheHitRate", "DistributionId", "E2QWERTY123ABC",
"Region", "Global", { "stat": "Average", "label": "Encerts de memoria cau %" }],
["AWS/WAFV2", "BlockedRequests", "WebACL", "waf-mercadofresco-cdn",
"Rule", "ALL", "Region", "Global",
{ "stat": "Sum", "label": "Bloquejos WAF", "yAxis": "right" }]
]
}
},
{
"type": "log",
"x": 0, "y": 17, "width": 24, "height": 7,
"properties": {
"title": "Ultims errors de la botiga",
"region": "eu-west-1",
"view": "table",
"query": "SOURCE '/mercadofresco/tienda/aplicacion'\n| fields @timestamp, nivel, tipo_error, ruta, pedido_id, duracion_ms\n| filter nivel = \"ERROR\"\n| sort @timestamp desc\n| limit 20"
}
}
]
}I la creació:
aws cloudwatch put-dashboard \
--dashboard-name mercadofresco-produccion \
--dashboard-body file://panel-produccion.json \
--profile mercadofresco-dev --region eu-west-1Detalls del JSON que mereixen explicació:
"start": "-PT3H"fixa la finestra per defecte en les últimes 3 hores. Sintaxi ISO 8601 de durada:-PT1H,-P1D,-P7D.["...", { "stat": "p95" }]repeteix la mètrica anterior canviant només l'estadística. Estalvia repetir l'ARN llarg tres vegades.- El giny de la vora porta
"region": "us-east-1". Les mètriques de CloudFront i de la Web ACL de CloudFront viuen allà, el mateix parany de 03-04 i 04-05. Un tauler pot barrejar regions giny a giny, i això el converteix en l'únic lloc on MercadoFresco ho veu tot junt. - Les
annotationshoritzontals dibuixen la línia del llindar. Veure el gràfic acostar-se a la línia vermella abans que salti l'alarma és la meitat del valor d'un tauler. - El giny
logexecuta una consulta d'Insights cada vegada que es carrega el tauler. Compte: això costa diners cada vegada, i si el tauler està en una pantalla de l'oficina refrescant cada minut, són 1.440 consultes al dia. La Marta li va posarlimit 20i una finestra curta per aquesta raó.
Cost dels taulers: els 3 primers són gratuïts; a partir d'aquí, 3 USD per tauler i mes.
MercadoFresco en té dos: mercadofresco-produccion i mercadofresco-negocio (el de la Sara, amb comandes
i imports). Cost: 0 USD.
ServiceLens i Synthetics
Dues funcions de CloudWatch que s'esmenten aquí i s'aprofiten a 05-02:
ServiceLens uneix mètriques, registres i traces d'X-Ray en un mapa de serveis únic. Necessita que X-Ray estigui actiu, així que es veu a la lliçó següent.
CloudWatch Synthetics executa «canaris»: petits scripts que es comporten com un client, des de fora, cada N minuts. És monitoratge sintètic: no espera que hi hagi un client real per detectar que la botiga està trencada. Amb 900 comandes/hora això no sembla important, però a les 3 de la matinada un diumenge poden passar dues hores sense una sola compra, i aquest és exactament el forat pel qual s'escola una caiguda.
El canari de MercadoFresco, canario-mercadofresco-compra, s'executa cada 5 minuts i fa el
recorregut complet: portada, cercar «tomàquet», obrir la fitxa, afegir a la cistella, arribar al formulari
de pagament (sense pagar). Si algun pas falla, dispara alertas-mercadofresco.
aws synthetics create-canary \
--name canario-mercadofresco-compra \
--artifact-s3-location s3://mercadofresco-registros-web/canarios/ \
--execution-role-arn arn:aws:iam::111122223333:role/rol-canario-mercadofresco \
--runtime-version syn-nodejs-puppeteer-9.0 \
--schedule Expression="rate(5 minutes)" \
--code S3Bucket=mercadofresco-registros-web,S3Key=canarios/compra.zip,Handler=compra.handler \
--run-config MemoryInMB=1024,TimeoutInSeconds=120,ActiveTracing=true \
--profile mercadofresco-dev --region eu-west-1Dues advertències pràctiques:
- Un canari que compra de debò genera comandes de debò. El de MercadoFresco s'atura abans de confirmar, i l'usuari de prova està marcat perquè la Sara l'exclogui dels seus informes.
- Cost: 0,0012 USD per execució. Cada 5 minuts són 8.640 execucions al mes: 10,37 USD, més les captures de pantalla a S3 i les traces. No és menyspreable; és la partida més cara d'aquesta lliçó després dels registres.
Cost de CloudWatch i com es dispara
Preus de referència a eu-west-1 (aproximats; consulta sempre la calculadora oficial):
| Concepte | Preu | Nota |
|---|---|---|
Mètriques d'AWS (AWS/*) |
Gratis | Resolució de 5 min |
| Monitoratge detallat d'EC2 | ~2,10 USD/instància/mes | Baixa a 1 min |
| Mètrica personalitzada | 0,30 USD/mes cadascuna | Primeres 10.000 |
PutMetricData |
0,01 USD per 1.000 crides | Per això els lots |
Ingesta de registres Standard |
~0,63 USD/GB | La partida gran |
Ingesta Infrequent Access |
~0,32 USD/GB | Sense filtres ni alarmes |
| Emmagatzematge de registres | 0,03 USD/GB/mes | Comprimit |
| Logs Insights | 0,0063 USD/GB escanejat | Per consulta |
| Alarma estàndard | 0,10 USD/mes | |
| Alarma d'alta resolució | 0,30 USD/mes | |
| Alarma composta | 0,50 USD/mes | |
| Detecció d'anomalies | 0,30 USD/mètrica/mes | Més l'alarma |
| Tauler | 3 gratis, després 3 USD/mes | |
| Canari de Synthetics | 0,0012 USD/execució | |
| Contributor Insights | 0,50 USD/regla + consultes |
El càlcul de MercadoFresco:
| Concepte | Quantitat | Cost mensual |
|---|---|---|
| Mètriques personalitzades de negoci | 8 | 2,40 USD |
| Mètriques de l'agent (sistema, agregades) | 20 | 6,00 USD |
| Mètriques d'EMF de les miniatures | 6 | 1,80 USD |
| Monitoratge detallat d'EC2 | 2-4 instàncies | ~6,30 USD |
| Ingesta de registres | ~22 GB/mes | 13,86 USD |
| Emmagatzematge de registres | ~12 GB mitjans | 0,36 USD |
| Logs Insights | ~30 GB escanejats | 0,19 USD |
| Alarmes estàndard (13) | 13 | 1,30 USD |
| Alarma composta | 1 | 0,50 USD |
| Detecció d'anomalies | 1 mètrica + 1 alarma | 0,60 USD |
| Taulers | 2 (gratis els 3 primers) | 0,00 USD |
| Canari cada 5 min | 8.640 execucions | 10,37 USD |
| Total | ~43,68 USD/mes |
Les cinc maneres de disparar aquesta factura, per ordre de freqüència real:
- Retenció infinita als grups de registres. Creix sense parar i mai no ho notes de cop.
DEBUGactivat en producció. Multiplica la ingesta per deu d'un dia per l'altre. La ingesta és la partida cara: 0,63 USD/GB.- Una dimensió de cardinalitat alta.
PedidoIdcom a dimensió: desenes de milers d'USD. És l'error més car que es pot cometre a CloudWatch, i es comet en una línia. - Consultes d'Insights sobre 30 dies per depurar. Cada refinament de la consulta torna a escanejar-ho tot.
- Un tauler amb ginys de registre en una pantalla d'oficina. Executa consultes cada minut per sempre.
I la defensa: un pressupost d'AWS Budgets filtrat pel servei CloudWatch (mòdul 11) amb avís al 80 %. A 05-04 veurem a més com AWS Config detecta automàticament els grups de registres sense retenció.
Neteja
# Alarmes
aws cloudwatch delete-alarms --alarm-names \
mercadofresco-alb-latencia-alta mercadofresco-alb-destinos-sanos \
mercadofresco-rds-conexiones-altas mercadofresco-rds-disco-bajo \
mercadofresco-lambda-estrangulada mercadofresco-tienda-memoria-alta \
mercadofresco-pedidos-fallidos mercadofresco-sin-pedidos \
mercadofresco-pedidos-anomalos \
--profile mercadofresco-dev --region eu-west-1
# La composta no es borra DESPRES de les filles: abans.
aws cloudwatch delete-alarms --alarm-names mercadofresco-tienda-degradada \
--profile mercadofresco-dev --region eu-west-1
# Detector d'anomalies
aws cloudwatch delete-anomaly-detector \
--namespace MercadoFresco/Tienda --metric-name PedidosConfirmados \
--dimensions Name=Entorno,Value=produccion Name=Componente,Value=tienda \
--stat Sum --profile mercadofresco-dev --region eu-west-1
# Tauler
aws cloudwatch delete-dashboards --dashboard-names mercadofresco-produccion \
--profile mercadofresco-dev --region eu-west-1
# Canari: primer aturar, despres esborrar
aws synthetics stop-canary --name canario-mercadofresco-compra \
--profile mercadofresco-dev --region eu-west-1
aws synthetics delete-canary --name canario-mercadofresco-compra \
--profile mercadofresco-dev --region eu-west-1
# Filtres de metriques
aws logs delete-metric-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name filtro-pedidos-fallidos \
--profile mercadofresco-dev --region eu-west-1
# Grups de registres (aixo ESBORRA les dades: irreversible)
aws logs delete-log-group --log-group-name /mercadofresco/tienda/nginx-acceso \
--profile mercadofresco-dev --region eu-west-1Dos avisos:
- Les mètriques personalitzades no es poden esborrar. Deixen de facturar-se quan deixen de rebre dades (després del període corresponent), però romanen visibles un temps. És una raó més per no crear dimensions a la lleugera.
- Esborrar un grup de registres n'elimina les dades de manera irreversible. Si hi pot haver una obligació de conservació, exporta'l a S3 primer i consulta-ho amb qui porti el compliment.
Errors Habituals i Consells
1. Alarmar sobre la mitjana d'una latència. És l'error número u. L'Average de
TargetResponseTime es queda pla mentre el p99 és a 9 segons. Fes servir --extended-statistic p95 o p99 per a tot el que sigui temps.
2. Deixar la retenció de registres en «Never expire». És el valor per defecte. És l'error més
car a mitjà termini. Posa retenció el mateix dia que crees el grup, i audita mensualment amb
describe-log-groups.
3. Fer servir un identificador com a dimensió. PedidoId, UserId, RequestId en una dimensió creen
una mètrica nova cada vegada. Milers d'USD. Aquestes dades van al registre o en una anotació d'X-Ray.
4. --treat-missing-data mal triat. Per a una mètrica l'absència de la qual és el problema
—PedidosConfirmados, HealthyHostCount— cal posar breaching. Amb notBreaching l'alarma
està tranquil·la mentre la botiga està morta.
5. Confondre HTTPCode_ELB_5XX_Count amb HTTPCode_Target_5XX_Count. El primer és del
balancejador (no hi ha destins sans, temps d'espera); el segon és la teva aplicació. Buscar al lloc
equivocat costa hores.
6. Llindars en unitats equivocades. FreeStorageSpace està en bytes; Duration de Lambda en
mil·lisegons; TargetResponseTime en segons. Un zero de més o de menys i l'alarma no salta mai.
7. Crear una alarma sense comprovar la subscripció d'SNS. Si l'ARN és correcte però ningú no ha
confirmat la subscripció, l'alarma dispara al buit. Comprova-ho amb list-subscriptions-by-topic i
fes un simulacre amb set-alarm-state.
8. No configurar --ok-actions. Qui rep un avís de matinada necessita saber quan s'ha
recuperat. Sense la notificació de tornada a OK, algú s'aixeca per no res o, pitjor, no s'aixeca
quan ho hauria de fer.
9. Buscar a CloudWatch qui ha esborrat un recurs. Això és a CloudTrail (05-03). CloudWatch registra el que la teva aplicació diu d'ella mateixa.
10. Mètriques de CloudFront buscades a eu-west-1. Són a us-east-1, sempre. Igual que els
certificats d'ACM (03-04) i la Web ACL de CloudFront (04-05).
11. Consultes d'Insights sobre rangs enormes mentre es depura. Comença per 15 minuts, afina la consulta, i només llavors amplia la finestra. Es paga per GB escanejat a cada execució.
12. Registrar text pla en lloc de JSON. Amb JSON, Insights descompon els camps sol i les
consultes són trivials. Amb text pla cal escriure expressions regulars amb parse. Canviar el
format del registre el primer dia costa una hora; fer-ho tres anys després, setmanes.
13. Un filtre de mètriques sense defaultValue=0. La sèrie té buits, l'alarma es queda en
INSUFFICIENT_DATA i la detecció d'anomalies no funciona.
14. Oblidar treure les accions de les alarmes filles en crear-ne una de composta. S'acaba rebent un avís més, no menys.
Consell final: la mètrica més valuosa d'un sistema gairebé mai és tècnica. PedidosConfirmados
detecta més incidents reals que CPUUtilization, perquè pot haver-hi mil maneres que la botiga
falli amb la CPU al 30 %, però només n'hi ha una que les comandes caiguin a zero.
Exercicis
Exercici 1: dissenyar l'alarma que detecta una fallada parcial
MercadoFresco desplega una versió nova de la botiga un dimarts a les 11:00. La versió té un error que només afecta les comandes pagades amb targeta d'un banc concret: aproximadament el 12 % de les compres. La resta funciona amb normalitat.
Dades de l'incident: CPUUtilization sense canvis (42 %), HealthyHostCount a 2,
TargetResponseTime p95 estable a 0,6 s, HTTPCode_Target_5XX_Count amb 3 errors cada 5 minuts
(abans: 1). PedidosConfirmados passa de 900/h a 792/h. Als registres apareixen línies
{"nivel":"ERROR","tipo_error":"PAGO_RECHAZADO","pasarela":"banco-x"}.
No salta cap alarma actual. L'error es descobreix 6 hores després per una queixa a les xarxes socials.
Dissenya la detecció: quina mètrica o mètriques faries servir, com les obtindries (filtre de mètriques, mètrica personalitzada, expressió), quin tipus d'alarma i amb quins paràmetres exactes de període, avaluació i dades absents. Escriu les ordres. Justifica per què la teva proposta hauria saltat en menys de 30 minuts sense generar falsos positius la resta del mes.
Exercici 2: la consulta de Logs Insights i el filtre de mètriques
La Sara pregunta si el cercador de la botiga funciona bé. L'aplicació escriu una línia JSON per cerca:
{"nivel":"INFO","evento":"busqueda","termino":"tomate rama","resultados":24,"duracion_ms":180,"cliente_hash":"a3f8c1e9"}Escriu:
- a) Una consulta d'Insights que doni, per hora, el nombre de cerques, la durada mitjana, el p95 i el percentatge de cerques sense resultats.
- b) Una consulta que llisti els 20 termes més cercats que retornen zero resultats (són productes que MercadoFresco hauria de tenir al catàleg: valor directe per a la Sara).
- c) Un filtre de mètriques i una alarma que avisin si el percentatge de cerques sense resultats supera el 20 % durant 15 minuts, que és el senyal que l'índex de cerca s'ha corromput. Compte: un filtre de mètriques compta, no calcula percentatges. Necessitaràs una expressió de mètrica a l'alarma.
Exercici 3: l'auditoria de cost
La Marta rep la factura i CloudWatch ha passat de 44 a 610 USD en un mes. El desglossament:
| Partida | Import |
|---|---|
Ingesta de registres (Standard) |
412 USD |
| Mètriques personalitzades | 96 USD |
| Logs Insights | 71 USD |
| Alarmes i taulers | 4 USD |
| Synthetics | 27 USD |
Pistes de la investigació:
- El grup
/mercadofresco/tienda/aplicacionha passat de 8 GB a 620 GB en el mes. - L'espai
MercadoFresco/Tiendaté ara 320 mètriques; el mes passat en tenia 8. - Hi ha 11.200 execucions d'Insights, totes amb finestra de 30 dies.
- Hi ha un canari nou,
canario-mercadofresco-admin, executant-se cada minut. - El Luis va desplegar fa tres setmanes una funció de «comandes recomanades» que registra l'activitat de cada client.
Diagnostica cada partida, digues exactament què va fer malament el Luis en cada cas, escriu les accions correctives amb les ordres, i proposa tres controls preventius perquè no torni a passar. Estima la factura resultant.
Solucions
Solució 1
Per què no va saltar res. Totes les alarmes actuals miren la infraestructura, i la infraestructura està perfecta. Una fallada del 12 % en una passarel·la concreta és invisible per a la CPU, per al nombre de destins sans i per al p95 de latència (un pagament rebutjat respon ràpid, fins i tot més ràpid que un de correcte). I la caiguda de comandes de 900 a 792 —un 12 %— queda dins de la variació normal d'un dimarts. Cap llindar fix raonable no detecta un 12 %.
Això és el que s'anomena una fallada parcial, i és el tipus d'incident que més triga a detectar-se en sistemes reals.
Detecció proposada: tres capes.
Capa 1 — el senyal directe. Filtre de mètriques sobre l'error concret, desglossat per passarel·la.
aws logs put-metric-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name filtro-pago-rechazado-banco-x \
--filter-pattern '{ $.tipo_error = "PAGO_RECHAZADO" && $.pasarela = "banco-x" }' \
--metric-transformations \
metricName=PagosRechazadosBancoX,\
metricNamespace=MercadoFresco/Tienda,\
metricValue=1,defaultValue=0 \
--profile mercadofresco-dev --region eu-west-1Capa 2 — l'alarma de proporció, que és la bona. Un llindar absolut de «més de N rebuigs» falla en tots dos sentits: de nit 3 rebuigs són moltíssim, al pic del divendres no són res. El correcte és alarmar sobre el percentatge, amb una expressió de mètrica:
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-tasa-rechazo-pagos \
--alarm-description "Mes del 5% dels intents de pagament son rebutjats" \
--evaluation-periods 3 --datapoints-to-alarm 2 \
--threshold 5 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--metrics '[
{"Id":"rechazos","MetricStat":{"Metric":{"Namespace":"MercadoFresco/Tienda",
"MetricName":"PagosRechazadosBancoX"},"Period":300,"Stat":"Sum"},
"ReturnData":false},
{"Id":"intentos","MetricStat":{"Metric":{"Namespace":"MercadoFresco/Tienda",
"MetricName":"IntentosPago"},"Period":300,"Stat":"Sum"},
"ReturnData":false},
{"Id":"tasa","Expression":"100 * rechazos / intentos",
"Label":"% de pagaments rebutjats","ReturnData":true}
]' \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1Amb un 12 % de rebuigs enfront d'un llindar del 5 %, i «2 de 3» períodes de 5 minuts, aquesta alarma salta
en 10-15 minuts. El requisit és publicar també IntentosPago (un segon filtre de mètriques o
una mètrica personalitzada); sense denominador no hi ha proporció.
Capa 3 — la xarxa de seguretat genèrica: detecció d'anomalies sobre PedidosConfirmados. Una
caiguda del 12 % sostinguda durant 6 hores sí que surt de la banda d'anomalia, perquè el model coneix
el patró d'un dimarts al matí amb precisió. És l'alarma mercadofresco-pedidos-anomalos que
ja vam crear: hauria saltat, probablement a la primera hora. És més lenta que la capa 2, però és
l'única que detecta problemes que ningú no ha anticipat.
Per què no genera falsos positius. La capa 2 mira una proporció, i una proporció és independent del volum: funciona igual a les 4 de la matinada que el divendres a les 19:00. Amb «2 de 3» períodes de 5 minuts, un pic aïllat de rebuigs —un banc que reinicia la seva passarel·la un minut— no dispara res.
La lliçó de fons: les alarmes d'infraestructura detecten caigudes totals. Les fallades parcials, que són les més freqüents i les que més diners costen, només les detecten les mètriques de negoci i les proporcions.
Solució 2
a) Panorama per hores.
fields @timestamp, duracion_ms, resultados, termino
| filter evento = "busqueda"
| stats count() as cerques,
avg(duracion_ms) as mitjana_ms,
pct(duracion_ms, 95) as p95_ms,
sum(resultados = 0) as sense_resultats,
100.0 * sum(resultados = 0) / count() as pct_sense_resultats
by bin(1h)
| sort @timestamp descLa clau és a sum(resultados = 0): a Insights, una expressió booleana val 1 o 0, així que
sumar-la compta els casos certs. És l'idioma per calcular percentatges en aquest llenguatge.
b) Termes sense resultats: valor de negoci directe.
fields termino | filter evento = "busqueda" and resultados = 0 | stats count() as vegades by termino | sort vegades desc | limit 20
Això no és monitoratge tècnic: és una llista de productes que els clients cerquen i MercadoFresco no ven. La Marta l'envia a la Sara cada dilluns. És el millor exemple que els registres d'una aplicació serveixen per a molt més que depurar.
c) Filtre de mètriques i alarma de proporció.
Dos filtres, perquè calen numerador i denominador:
# Denominador: totes les cerques
aws logs put-metric-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name filtro-busquedas-total \
--filter-pattern '{ $.evento = "busqueda" }' \
--metric-transformations \
metricName=Busquedas,metricNamespace=MercadoFresco/Tienda,\
metricValue=1,defaultValue=0 \
--profile mercadofresco-dev --region eu-west-1
# Numerador: les que no retornen res
aws logs put-metric-filter \
--log-group-name /mercadofresco/tienda/aplicacion \
--filter-name filtro-busquedas-vacias \
--filter-pattern '{ $.evento = "busqueda" && $.resultados = 0 }' \
--metric-transformations \
metricName=BusquedasSinResultados,metricNamespace=MercadoFresco/Tienda,\
metricValue=1,defaultValue=0 \
--profile mercadofresco-dev --region eu-west-1I l'alarma amb expressió:
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-buscador-degradado \
--alarm-description "Mes del 20% de les cerques no retornen resultats" \
--evaluation-periods 3 --datapoints-to-alarm 3 \
--threshold 20 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--metrics '[
{"Id":"vacias","MetricStat":{"Metric":{"Namespace":"MercadoFresco/Tienda",
"MetricName":"BusquedasSinResultados"},"Period":300,"Stat":"Sum"},"ReturnData":false},
{"Id":"total","MetricStat":{"Metric":{"Namespace":"MercadoFresco/Tienda",
"MetricName":"Busquedas"},"Period":300,"Stat":"Sum"},"ReturnData":false},
{"Id":"pct","Expression":"100 * vacias / MAX([total, 1])",
"Label":"% cerques sense resultats","ReturnData":true}
]' \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1Tres decisions justificades:
MAX([total, 1])al denominador evita la divisió per zero quan no hi ha cerques de matinada. Sense això, l'expressió produeix valors no numèrics i l'alarma es comporta de manera erràtica.- 3 de 3 períodes de 5 minuts = 15 minuts, tal com demanava l'enunciat. Aquí sí que convé «3 de 3» i no «2 de 3»: un índex corromput no s'arregla sol, així que el senyal serà sostingut, i esperar 15 minuts evita el soroll d'un desplegament de l'índex de cerca.
notBreachingperquè de matinada pot no haver-hi cerques, i això no és un incident.
Solució 3
Diagnòstic partida per partida.
1. Ingesta de registres: 412 USD (8 GB → 620 GB). La funció de «comandes recomanades» del Luis
registra l'activitat de cada client en cada visita. Probablement escriu una línia per producte
vist, i molt probablement amb nivel: DEBUG perquè així ho va deixar després de depurar. 612 GB extra a 0,63
USD/GB són 386 USD. És la causa principal.
Error del Luis: deixar DEBUG en producció i escriure al registre d'aplicació un flux d'esdeveniments
d'analítica que no és un registre d'aplicació.
Correcció, en dos temps:
# Immediat: baixar el nivell de registre per variable d'entorn / parametre
aws ssm put-parameter --name /mercadofresco/produccion/nivel-log \
--value INFO --type String --overwrite \
--profile mercadofresco-dev --region eu-west-1
# I assegurar la retencio, que molt probablement segueix en "no expira mai"
aws logs put-retention-policy \
--log-group-name /mercadofresco/tienda/aplicacion \
--retention-in-days 30 \
--profile mercadofresco-dev --region eu-west-1De fons: els esdeveniments de comportament de client no van a CloudWatch Logs. Van a
mercadofresco-informes-analitica a S3, via Firehose, on l'emmagatzematge costa 0,023 USD/GB en
lloc dels 0,63 USD/GB d'ingesta. És 27 vegades més barat i és on la Sara els vol de totes maneres.
2. Mètriques personalitzades: 96 USD (8 → 320 mètriques). 320 × 0,30 = 96 USD. Gairebé amb seguretat,
la funció de recomanacions publica una mètrica amb una dimensió de cardinalitat mitjana —CategoriaProducto
amb 300 valors, o ModeloRecomendador—.
Error del Luis: fer servir com a dimensió una cosa que no és una categoria petita i tancada.
Correcció: treure aquesta dimensió i, si cal el desglossament, posar-lo com a propietat EMF, que és consultable amb Insights i no costa 0,30 USD per valor. Nota important per a l'examen mental: la factura de mètriques no baixa fins al mes següent, perquè les mètriques ja creades es facturen mentre rebin dades. No es poden esborrar; cal deixar de publicar-les.
I la prevenció de debò, a la política IAM del rol —el patró de 04-01—:
{
"Effect": "Allow",
"Action": "cloudwatch:PutMetricData",
"Resource": "*",
"Condition": {
"StringEquals": { "cloudwatch:namespace": "MercadoFresco/Tienda" }
}
}Això no limita la cardinalitat —IAM no pot—, però sí que impedeix que una aplicació creï espais de noms nous sense control, que és l'altra meitat del problema.
3. Logs Insights: 71 USD (11.200 execucions a 30 dies). 11.200 consultes escanejant volums enormes. Dues causes superposades: algú depurant amb la finestra en «30 dies» sense canviar-la, i molt probablement un giny de registre en un tauler obert en una pantalla que reexecuta la consulta cada vegada que es refresca.
Correcció: reduir la finestra per defecte dels ginys de registre, posar un limit baix, i establir
la norma de començar sempre per 15 minuts. Com que el grup baixa de 620 GB a ~8 GB en arreglar el punt
1, aquesta partida cau sola en més d'un 95 %.
4. Synthetics: 27 USD. canario-mercadofresco-admin cada minut són 43.200 execucions al mes.
Per a un tauler d'administració que fan servir tres persones en horari d'oficina, és absurd.
Correcció:
aws synthetics update-canary \
--name canario-mercadofresco-admin \
--schedule Expression="rate(15 minutes)" \
--profile mercadofresco-dev --region eu-west-1De 43.200 a 2.880 execucions: de 51,84 a 3,46 USD. I si només importa en horari d'oficina, es pot
programar amb una expressió cron en lloc de rate.
5. Alarmes i taulers: 4 USD. Correcte. No es toca.
Factura estimada després de les correccions:
| Partida | Abans | Després | Com |
|---|---|---|---|
| Ingesta de registres | 412 USD | ~14 USD | INFO + analítica a S3 + retenció |
| Mètriques personalitzades | 96 USD | ~10 USD | Treure la dimensió (efecte el mes següent) |
| Logs Insights | 71 USD | ~1 USD | Menys volum i finestres acotades |
| Alarmes i taulers | 4 USD | 4 USD | — |
| Synthetics | 27 USD | ~14 USD | Canari d'admin cada 15 min |
| Total | 610 USD | ~43 USD |
Tres controls preventius:
- Pressupost d'AWS Budgets filtrat per servei = CloudWatch, amb llindar de 60 USD i avís al
80 % a
[email protected]. Detecta la desviació en dies, no a la factura del mes següent. Es veu al mòdul 11. - Regla d'AWS Config que marqui com a no conforme qualsevol grup de registres sense
retentionInDays, amb remediació automàtica que li posi 30 dies. És exactament el cas d'ús de la lliçó 05-04. - Revisió al procés de desplegament: ningú no desplega codi que escriu registres nous sense que una altra persona revisi el volum estimat i el nivell de registre. És una casella a la revisió de codi, no una eina. El mòdul 8 la integra al pipeline.
I un quart, cultural, que és el que de debò funciona: ensenyar la factura. Quan el Luis va veure que
els seus registres de depuració costaven 386 USD al mes —més que les instàncies EC2 que els generaven— no
va tornar a deixar DEBUG activat.
Conclusió
MercadoFresco ja té una mirada. Saps que CloudWatch són tres coses —mètriques, registres i alarmes— i saps què no és: ni el registre de qui ha cridat l'API (CloudTrail, 05-03), ni el rastrejador de peticions (X-Ray, 05-02), ni l'avaluador de compliment (Config, 05-04), ni el bus d'esdeveniments (EventBridge, 07-03).
Domines el model de mètriques: espai de noms, nom i dimensions, amb la regla d'or que
cada combinació de dimensions és una mètrica facturable i que un identificador en una dimensió
és l'error més car d'aquest servei. Saps triar l'estadística —Sum per a comptadors,
Average per a utilització, p95/p99 sempre per a latència— i per què la mitjana de 0,17 s amagava
que deu clients per minut esperaven nou segons per pagar. Coneixes la retenció automàtica que
agrega les dades a 5 minuts després de 15 dies, i la taula de les mètriques que de debò decideixen alguna cosa a
cada servei que has muntat, amb la diferència crítica entre HTTPCode_ELB_5XX_Count i
HTTPCode_Target_5XX_Count.
Has publicat les mètriques de negoci PedidosConfirmados i TiempoConfirmacionPedido a
MercadoFresco/Tienda, primer amb PutMetricData directe i després per lots des d'un fil a part
per treure la crida de la ruta crítica d'una compra. Saps per què EC2 no publica memòria
—l'hipervisor no veu dins del sistema operatiu convidat— i has desplegat l'agent unificat a
lt-mercadofresco-tienda amb la seva configuració a Parameter Store, les seves append_dimensions resoltes
soles i el seu retention_in_days posat des del primer dia. I coneixes l'Embedded Metric Format,
que a Lambda et dona mètriques gratis a la ruta crítica i —això és el més important— et permet desar la
clau d'S3 i l'ID de petició al costat de la mètrica sense pagar cardinalitat.
En registres, has centralitzat el que es podia centralitzar i saps el que no: els accessos de l'ALB
i de CloudFront viuen a S3 i es consulten amb Athena. Tens la política de retenció per grup, la
classe Infrequent Access per als flow logs, i les consultes de Logs Insights que responen
preguntes reals —els bloquejos de WAF per regla, el p99 d'una Lambda amb @duration, els rebuigs
al port 5432, i parse amb expressió regular per al programari que no registra en JSON—. I, sobretot,
has respost la pregunta de la comanda de vuit segons: quatre grups de registres consultats
alhora amb un identificador de correlació, una cronologia muntada esdeveniment a esdeveniment, i el
culpable identificat —7.402 ms dins de PostgreSQL, un patró N+1 amb 39 consultes per a una comanda
de 38 productes—.
Saps convertir un patró de registre en mètrica amb un filtre de mètriques (i per què defaultValue=0
no és opcional), i treure registres amb subscripcions en temps real o exportació a S3.
En alarmes, domines el que separa una alarma útil d'una font de soroll: la combinació «M de N»
amb --datapoints-to-alarm 2 --evaluation-periods 3, el tractament de dades absents —amb
breaching per a les mètriques l'absència de les quals és l'incident, com PedidosConfirmados—, les
alarmes compostes que converteixen tres SMS en un i que exigeixen treure les accions de les filles, i
la detecció d'anomalies que sap que 0 comandes a les 4 de la matinada és normal i a les 19:00 és
una catàstrofe. Has muntat vuit alarmes noves —que se sumen a les dels mòduls 2 i 4 fins a
tretze en total—, la composta mercadofresco-tienda-degradada i el detector d'anomalies sobre
PedidosConfirmados.
I has respost la primera pregunta que va deixar oberta el mòdul 4, que era la més incòmoda de totes:
l'avís arriba de debò. Has comprovat les subscripcions buscant el temut
PendingConfirmation, has subscrit el correu i el telèfon de guàrdia, i has fet el simulacre amb
set-alarm-state, que dispara les accions reals sense tocar la mètrica. I saps que la cadena inclou
el telèfon: el «no molestar» d'un mòbil ha silenciat més alarmes que qualsevol error de
configuració d'AWS.
Tot això es veu junt al tauler mercadofresco-produccion, ordenat de dalt a baix —negoci,
components, detall—, amb les mètriques de CloudFront al seu giny d'us-east-1, les línies
d'anotació que ensenyen com de prop estàs del llindar abans que salti, i el giny de registre amb els
últims errors. I amb el canari canario-mercadofresco-compra recorrent la botiga cada cinc
minuts perquè un diumenge de matinada, sense ni un sol client real, algú continuï comprovant que es
pot comprar. Tot plegat per uns 44 USD al mes, amb les cinc maneres de disparar aquesta xifra
identificades i la retenció posada el mateix dia que es crea cada grup.
Queda, però, una insatisfacció concreta. La investigació de la comanda de vuit segons
va funcionar, però va costar quatre consultes, una cronologia muntada a mà en un full de càlcul, i va dependre
per complet que algú s'hagués recordat de propagar la capçalera X-Peticion-Id per tots els
components. Si demà el coll d'ampolla és dins de la Lambda, o a la crida a la passarel·la de
pagament, o al temps de connexió a la base de dades, caldrà repetir la feina sencera i endevinar
un altre cop on mirar.
Existeix una eina que fa aquesta feina sola, que dibuixa la cronologia en un gràfic i que diu,
per a una petició concreta, quant va trigar cada tram del recorregut: AWS X-Ray. A la lliçó
05-02, «AWS X-Ray i traçabilitat distribuïda», veurem què és una traça, un segment i un
subsegment, com es propaga l'identificador amb X-Amzn-Trace-Id, per què no es tracen totes les
peticions i com es configura el mostreig, com instrumentar la botiga amb aws_xray_sdk i activar
el rastreig a mercadofresco-estado-pedido amb una casella i un permís, com llegir el mapa de
serveis i cercar amb filtres com annotation.pedido_id, i tornarem a aquesta mateixa comanda de 8,14
segons per veure-la, aquesta vegada, en un sol gràfic.
Curs d'AWS
Mòdul 1: Introducció a AWS
- Què és AWS?
- Configuració del teu compte d'AWS
- Infraestructura global d'AWS
- Consola d'administració d'AWS
- AWS CLI i SDK
Mòdul 2: Serveis principals d'AWS
Mòdul 3: Xarxes i lliurament de contingut
- Amazon VPC
- Grups de seguretat i llistes de control d'accés
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Mòdul 4: Seguretat i identitat
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager i Parameter Store
- AWS Shield
- AWS WAF
Mòdul 5: Monitoratge i gestió
- Amazon CloudWatch
- AWS X-Ray i traçabilitat distribuïda
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Mòdul 6: Bases de dades
- Com triar la base de dades adequada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Mòdul 7: Integració d'aplicacions
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrons d'integració: idempotència, reintents i cues de missatges fallits
