Skill d'Investigation AWS CloudWatch
Patterns réutilisables pour investiguer les incidents de production avec CloudWatch Logs, Metrics et Alarms. Ces patterns sont conçus pour être composés ensemble lors du triage d'incident.
Pattern 1 : Modèles de requête Logs Insights
Détection de pic d'erreurs
Trouver les erreurs les plus fréquentes dans une fenêtre de temps, groupées par type d'erreur :
fields @timestamp, @message, @logStream
| filter @message like /(?i)(error|exception|fatal|critical)/
| stats count(*) as errorCount by bin(5m), @logStream
| sort errorCount desc
| limit 20
Répartition de la latence P99 par opération
Identifier quelles opérations génèrent les pics de latence :
fields @timestamp, @duration, operation
| filter ispresent(@duration)
| stats avg(@duration) as avgMs,
pct(@duration, 50) as p50Ms,
pct(@duration, 95) as p95Ms,
pct(@duration, 99) as p99Ms,
count(*) as invocations
by operation
| sort p99Ms desc
| limit 15
Détection de démarrage à froid Lambda
Quantifier l'impact des démarrages à froid pendant un incident :
fields @timestamp, @duration, @initDuration, @memorySize, @maxMemoryUsed
| filter ispresent(@initDuration)
| stats count(*) as coldStarts,
avg(@initDuration) as avgInitMs,
max(@initDuration) as maxInitMs,
avg(@duration) as avgDurationMs
by bin(5m)
| sort @timestamp desc
Détection de dépassement mémoire (OOM)
Trouver les fonctions Lambda ou conteneurs arrêtés par manque de mémoire :
fields @timestamp, @message, @logStream, @memorySize, @maxMemoryUsed
| filter @message like /Runtime exited|out of memory|OOMKilled|Cannot allocate memory|MemoryError/
| stats count(*) as oomEvents by @logStream, bin(10m)
| sort oomEvents desc
| limit 10
Pour la tendance d'utilisation mémoire avant OOM :
fields @timestamp, @maxMemoryUsed, @memorySize
| filter ispresent(@maxMemoryUsed)
| stats max(@maxMemoryUsed / @memorySize * 100) as peakMemPct,
avg(@maxMemoryUsed / @memorySize * 100) as avgMemPct
by bin(5m)
| sort @timestamp desc
Détection de délai d'expiration
Trouver les invocations qui ont atteint le timeout configuré :
fields @timestamp, @duration, @logStream, @requestId
| filter @message like /Task timed out/ or @duration > 28000
| stats count(*) as timeouts by @logStream, bin(5m)
| sort timeouts desc
Pattern 2 : Corrélation entre l'historique des alarmes et les événements de déploiement
Processus
- Récupérer l'heure de transition de l'alarme — noter le timestamp exact quand l'alarme est entrée en état ALARM.
- Interroger CloudTrail pour les événements liés aux déploiements dans la fenêtre [alarm_time - 30min, alarm_time] :
# Requête CloudTrail Lake pour les événements de déploiement
SELECT eventTime, eventName, userIdentity.arn, requestParameters
FROM <event-data-store-id>
WHERE eventTime > '<alarm_time_minus_30m>'
AND eventTime < '<alarm_time>'
AND eventName IN (
'UpdateFunctionCode', 'UpdateFunctionConfiguration',
'UpdateService', 'CreateDeployment', 'RegisterTaskDefinition',
'CreateChangeSet', 'ExecuteChangeSet',
'StartPipelineExecution', 'PutImage'
)
ORDER BY eventTime DESC
-
Critères de corrélation — un déploiement est « corrélé » si :
- Il cible le même service/resource que l'alarme
- Il s'est complété dans les 15 minutes avant la transition de l'alarme
- L'identité du déployeur correspond à un rôle CI/CD (pas un humain appliquant un hotfix)
-
Renforcer la corrélation :
- Vérifier si la même alarme était saine dans le cycle de déploiement précédent
- Confirmer qu'il n'y a pas d'autres changements environnementaux (événements de scaling, changements de config) dans la même fenêtre
- Chercher les défaillances de canary/moniteurs synthétiques commencées au même moment
Format de sortie
Deploy Correlation:
Event: UpdateFunctionCode
Time: 2024-03-15T14:23:07Z (12 min avant l'alarme)
Actor: arn:aws:sts::123456789012:assumed-role/github-actions-deploy/session
Resource: arn:aws:lambda:us-east-1:123456789012:function:payment-processor
Correlation: STRONG — même resource, acteur CI/CD, alarme OK cycle précédent
Pattern 3 : Arbre de décision pour réduire le rayon d'impact
Utiliser cet arbre pour délimiter systématiquement un incident du plus large au plus spécifique :
START
|
v
[1] ACCOUNT — Quel(s) compte(s) montrent l'alarme ?
| - Vérifier : les alarmes se déclenchent-elles sur plusieurs comptes ?
| - Si oui → suspecter un service partagé (SSO, réseau, pipeline de déploiement partagé)
| - Si non → procéder à la Région
v
[2] REGION — Quelle(s) région(s) sont affectées ?
| - Vérifier : même alarme dans d'autres régions ?
| - Si multi-région → suspecter un service global (IAM, Route53, S3 global)
| - Si single-région → procéder au Service
v
[3] SERVICE — Quel namespace de service montre une dégradation ?
| - Vérifier le namespace CloudWatch : AWS/Lambda, AWS/ECS, AWS/ApiGateway, etc.
| - Si plusieurs services → suspecter une dépendance partagée (VPC, NAT, DNS, IAM)
| - Si service unique → procéder à l'Opération
v
[4] OPERATION — Quelle action API ou fonction échoue ?
| - Pour Lambda : quel nom de fonction ?
| - Pour ECS : quel service/définition de task ?
| - Pour API GW : quel stage/resource/method ?
| - Si toutes les opérations → suspecter un problème au niveau du service (throttling, quota)
| - Si opération spécifique → procéder à la Resource
v
[5] RESOURCE — Quelle instance de resource spécifique ?
- Function ARN, Task ID, identifiant DB instance
- C'est votre cible d'investigation
- Procéder à l'analyse des logs et traces limitée à cette resource
Investigation de dépendance partagée
Quand le rayon d'impact s'étend sur plusieurs services, investiguer dans cet ordre :
- VPC/Réseau — NAT Gateway ErrorPortAllocation, pertes de paquets, défaillances de résolution DNS
- IAM/STS — ThrottlingException sur AssumeRole, latence de vente de tokens
- Dépendance aval — base de données partagée, cache ou API externe
- Pipeline de déploiement — déploiements simultanés entre services depuis le même pipeline run
- Événement AWS — vérifier AWS Health Dashboard et Service Health pour la région
Pattern 4 : Patterns de requête métrique de style PromQL
Ces patterns utilisent les maths de métrique CloudWatch et GetMetricData pour construire des signaux composites. Les exprimer comme des requêtes de métrique pour les dashboards ou la récupération programmatique.
Taux d'erreur en pourcentage
MetricDataQueries:
- Id: errors
MetricStat:
Metric:
Namespace: AWS/Lambda
MetricName: Errors
Dimensions: [{Name: FunctionName, Value: TARGET}]
Period: 60
Stat: Sum
- Id: invocations
MetricStat:
Metric:
Namespace: AWS/Lambda
MetricName: Invocations
Dimensions: [{Name: FunctionName, Value: TARGET}]
Period: 60
Stat: Sum
- Id: error_rate
Expression: "errors / invocations * 100"
Label: "Error Rate %"
Détection d'anomalie de latence (Comparaison à la baseline)
MetricDataQueries:
- Id: current_p99
MetricStat:
Metric:
Namespace: AWS/Lambda
MetricName: Duration
Dimensions: [{Name: FunctionName, Value: TARGET}]
Period: 300
Stat: p99
- Id: baseline_p99
MetricStat:
Metric:
Namespace: AWS/Lambda
MetricName: Duration
Dimensions: [{Name: FunctionName, Value: TARGET}]
Period: 300
Stat: p99
# Utiliser StartTime/EndTime sur la même fenêtre la semaine précédente
- Id: anomaly_ratio
Expression: "current_p99 / baseline_p99"
Label: "Latency vs Baseline (ratio > 2 = anomaly)"
Score de pression de throttling
Combiner plusieurs signaux de throttling en une seule métrique de pression :
MetricDataQueries:
- Id: lambda_throttles
MetricStat:
Metric: {Namespace: AWS/Lambda, MetricName: Throttles}
Period: 60
Stat: Sum
- Id: api_gw_429s
MetricStat:
Metric: {Namespace: AWS/ApiGateway, MetricName: 4XXError, Dimensions: [{Name: ApiName, Value: TARGET}]}
Period: 60
Stat: Sum
- Id: dynamo_throttles
MetricStat:
Metric: {Namespace: AWS/DynamoDB, MetricName: ThrottledRequests, Dimensions: [{Name: TableName, Value: TARGET}]}
Period: 60
Stat: Sum
- Id: throttle_pressure
Expression: "lambda_throttles + api_gw_429s + dynamo_throttles"
Label: "Combined Throttle Pressure"
Marge disponible d'exécution concurrente
MetricDataQueries:
- Id: concurrent
MetricStat:
Metric: {Namespace: AWS/Lambda, MetricName: ConcurrentExecutions}
Period: 60
Stat: Maximum
- Id: headroom
Expression: "1000 - concurrent"
Label: "Remaining Concurrency (account limit 1000)"
Pattern 5 : Reconstruction de chronologie d'incident
Processus
Reconstituer une chronologie précise en fusionnant les données de plusieurs sources :
- Collecter les timestamps :
| Source | Requête | Rendement |
|---|---|---|
| CloudWatch Alarms | API d'historique des alarmes | Heures de transition d'état |
| CloudWatch Metrics | GetMetricData avec période 1-min | Premier point d'anomalie |
| CloudWatch Logs | Logs Insights avec earliest(@timestamp) |
Première occurrence d'erreur |
| CloudTrail | LookupEvents filtrée par heure | Événements de déploiement/changement |
| AWS Health | DescribeEvents | Incidents côté AWS |
- Construire la chronologie :
fields @timestamp, @message
| filter @message like /ERROR|WARN|timeout|refused|denied/
| stats earliest(@timestamp) as firstSeen, latest(@timestamp) as lastSeen, count(*) as occurrences
by @message
| sort firstSeen asc
| limit 20
- Identifier la séquence :
Timeline:
T-15m: CloudTrail — UpdateFunctionCode par rôle CI/CD
T-12m: Logs — première erreur "Connection refused to payments-api.internal"
T-10m: Metrics — nombre d'erreurs franchit le seuil 5/min
T-8m: Alarm — PaymentProcessorErrors entre en état ALARM
T-5m: Metrics — latence p99 monte à 28s (timeout)
T-0: Actuel — taux d'erreur 45%, alarme toujours déclenchée
- Déterminer l'événement racine — le changement le plus ancien qui a précédé tous les symptômes. Remonter depuis le premier symptôme jusqu'à la mutation la plus récente (déploiement, changement de config, événement de scaling ou décalage de dépendance externe).
Pièges
- Les timestamps des métriques CloudWatch sont fin-de-période. Un datapoint 1-minute à 14:05 couvre 14:04-14:05.
- Les événements CloudTrail peuvent avoir jusqu'à 15 minutes de délai de livraison. Utiliser
eventTime, pas l'heure d'ingestion. - Les timestamps du log group dépendent de l'intervalle de flush de l'agent/SDK. Prévoir 30-60s de décalage horaire.
- Les changements d'état d'alarme ont un délai d'évaluation intégré (périodes x périodes d'évaluation). L'anomalie réelle a commencé plus tôt.