Une nouvelle charge de production part en ligne sur AWS. L’architecture est saine — ALB vers ECS vers Aurora, autoscaling lié au CPU, NAT Gateway pour la sortie, certificats ACM sur le listener. Trois semaines plus tard, un incident : le reader Aurora a pris 90 secondes de retard, l’application a continué de router les lectures vers lui, les clients ont vu de la donnée périmée pendant une heure, l’astreinte l’a appris par un ticket client. Les alarmes CloudWatch présentes sur le compte se déclenchent sur le CPU d’instance à 90 %, les cibles ALB malsaines, et le stockage RDS à 85 %. Aucune n’a attrapé le lag de réplication. La baseline ci-dessous est l’ensemble de onze alarmes qui attrape les modes de défaillance qu’un SaaS de production rencontre réellement — assemblée à partir des post-mortems des alarmes qui n’ont pas fonctionné.
Ce qui se passe
Les alarmes CloudWatch viennent en trois saveurs qui correspondent à des signaux différents :
- Alarmes métriques — seuil sur une seule métrique (taux 5xx au-dessus de 1 %, CPU au-dessus de 80 %).
- Alarmes composites — booléen sur plusieurs alarmes métriques (ne se déclenchent que si A et B sont toutes les deux en ALARM).
- Détection d’anomalie — bande autour d’une baseline apprise (compte de requêtes en baisse de 30 % par rapport à la normale pour l’heure).
La baseline ci-dessous s’appuie sur des alarmes métriques — prévisibles, faciles à raisonner, et les valeurs de seuil sont bien comprises. Les alarmes composites apparaissent pour un cas précis (supprimer les alarmes NAT GW pendant la fenêtre de maintenance). La détection d’anomalie est utile pour le compte de requêtes et la profondeur de file, mais demande des semaines pour s’entraîner et n’est pas dans la baseline.
Les onze alarmes couvrent six surfaces de défaillance :
- Couche applicative — taux 5xx ALB, compte d’hôtes target group malsains, temps de réponse cible p95.
- Couche compute — capacité ASG insuffisante, échec de déploiement de service ECS.
- Couche données — compte de connexions RDS, CPU RDS, lag de réplica RDS.
- Sortie réseau — bande passante NAT Gateway approchant la limite.
- TLS — expiration de certificat ACM s’approchant à 30 jours.
- Sauvegardes — échec de job AWS Backup (couvert dans l’article AWS Backup de cette série).
Chacune a un seuil informé par des patrons réels de production, pas par les défauts de la console AWS.
Comment vérifier
La vérification est binaire : ces alarmes existent-elles, dans le compte de production, sur les ressources de production, avec des notifications qui partent vers un endroit qu’un humain verra dans les minutes ? Lancer l’inventaire :
aws cloudwatch describe-alarms \
--query 'MetricAlarms[].[AlarmName,MetricName,Threshold,StateValue,AlarmActions[0]]' \
--output table
Les colonnes à scanner : chaque load balancer de production doit avoir une alarme HTTPCode_ELB_5XX_Count et une UnHealthyHostCount. Chaque cluster Aurora doit avoir DatabaseConnections, CPUUtilization et AuroraReplicaLag. Chaque NAT Gateway doit avoir BytesOutToDestination contre le quota de bande passante. Chaque certificat ACM doit avoir DaysToExpiry inférieur à 30.
Un trou fréquent : les alarmes existent mais partent vers un topic SNS sans abonnés, ou un canal Slack en sourdine. Tester le chemin de bout en bout :
aws cloudwatch set-alarm-state \
--alarm-name prod-alb-5xx-rate \
--state-value ALARM \
--state-reason "drill"
La notification doit atterrir dans le canal d’astreinte en quelques secondes. Sinon, l’alarme est décorative.
La correction
Le snippet CloudFormation ci-dessous provisionne les onze alarmes contre des ressources taguées. Les seuils reflètent des patrons de production que nous avons validés sur des clients gérés ; à ajuster pour la charge spécifique. Remplacez ${LbFullName}, ${TgArn}, ${ClusterId}, ${NatGatewayId}, ${CertArn} et ${SnsTopicArn} par les références de ressources réelles de la stack.
Resources:
Alb5xxRate:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-alb-5xx-rate
AlarmDescription: Taux d'erreur 5xx ALB au-dessus de 1%
Metrics:
- Id: e1
Expression: "100 * (m_5xx / m_total)"
Label: "pourcentage 5xx"
- Id: m_5xx
MetricStat:
Metric:
Namespace: AWS/ApplicationELB
MetricName: HTTPCode_ELB_5XX_Count
Dimensions:
- Name: LoadBalancer
Value: !Ref LbFullName
Period: 60
Stat: Sum
ReturnData: false
- Id: m_total
MetricStat:
Metric:
Namespace: AWS/ApplicationELB
MetricName: RequestCount
Dimensions:
- Name: LoadBalancer
Value: !Ref LbFullName
Period: 60
Stat: Sum
ReturnData: false
Threshold: 1
ComparisonOperator: GreaterThanThreshold
EvaluationPeriods: 5
DatapointsToAlarm: 3
TreatMissingData: notBreaching
AlarmActions: [!Ref SnsTopicArn]
TargetUnhealthy:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-tg-unhealthy
MetricName: UnHealthyHostCount
Namespace: AWS/ApplicationELB
Dimensions:
- Name: TargetGroup
Value: !Ref TgArn
- Name: LoadBalancer
Value: !Ref LbFullName
Statistic: Maximum
Period: 60
EvaluationPeriods: 3
Threshold: 0
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
TargetLatencyP95:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-tg-latency-p95
MetricName: TargetResponseTime
Namespace: AWS/ApplicationELB
Dimensions:
- Name: TargetGroup
Value: !Ref TgArn
- Name: LoadBalancer
Value: !Ref LbFullName
ExtendedStatistic: p95
Period: 60
EvaluationPeriods: 5
DatapointsToAlarm: 3
Threshold: 1.0
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
RdsConnections:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-rds-connections
MetricName: DatabaseConnections
Namespace: AWS/RDS
Dimensions:
- Name: DBClusterIdentifier
Value: !Ref ClusterId
Statistic: Maximum
Period: 60
EvaluationPeriods: 5
Threshold: 80
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
RdsCpu:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-rds-cpu
MetricName: CPUUtilization
Namespace: AWS/RDS
Dimensions:
- Name: DBClusterIdentifier
Value: !Ref ClusterId
ExtendedStatistic: p95
Period: 300
EvaluationPeriods: 3
Threshold: 80
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
RdsReplicaLag:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-rds-replica-lag
MetricName: AuroraReplicaLag
Namespace: AWS/RDS
Dimensions:
- Name: DBClusterIdentifier
Value: !Ref ClusterId
Statistic: Maximum
Period: 60
EvaluationPeriods: 3
Threshold: 5000
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
AsgInsufficientCapacity:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-asg-insufficient-capacity
MetricName: GroupInServiceInstances
Namespace: AWS/AutoScaling
Dimensions:
- Name: AutoScalingGroupName
Value: !Ref AsgName
Statistic: Minimum
Period: 300
EvaluationPeriods: 2
Threshold: 1
ComparisonOperator: LessThanThreshold
AlarmActions: [!Ref SnsTopicArn]
EcsServiceFailure:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-ecs-deployment-failure
MetricName: ServiceCount
Namespace: AWS/ECS
Dimensions:
- Name: ServiceName
Value: !Ref EcsService
- Name: ClusterName
Value: !Ref EcsCluster
Statistic: Minimum
Period: 60
EvaluationPeriods: 3
Threshold: 1
ComparisonOperator: LessThanThreshold
AlarmActions: [!Ref SnsTopicArn]
NatBandwidth:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-nat-bandwidth
MetricName: BytesOutToDestination
Namespace: AWS/NATGateway
Dimensions:
- Name: NatGatewayId
Value: !Ref NatGatewayId
Statistic: Sum
Period: 60
EvaluationPeriods: 5
Threshold: 5500000000
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
CertExpiry:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-cert-expiry-30d
MetricName: DaysToExpiry
Namespace: AWS/CertificateManager
Dimensions:
- Name: CertificateArn
Value: !Ref CertArn
Statistic: Minimum
Period: 86400
EvaluationPeriods: 1
Threshold: 30
ComparisonOperator: LessThanThreshold
AlarmActions: [!Ref SnsTopicArn]
BackupJobFailure:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-backup-job-failure
MetricName: NumberOfBackupJobsFailed
Namespace: AWS/Backup
Dimensions:
- Name: BackupVaultName
Value: !Ref BackupVaultName
Statistic: Sum
Period: 3600
EvaluationPeriods: 1
Threshold: 0
ComparisonOperator: GreaterThanThreshold
AlarmActions: [!Ref SnsTopicArn]
Quelques-uns des choix de seuil méritent une défense :
- ALB 5xx à 1 % — pas 5 %, pas 0,1 %. Une app web saine est à 0,0 % de 5xx en baseline ; 1 % sur cinq minutes signifie un impact client réel. L’expression mathématique dans l’alarme évite l’erreur classique d’alarmer sur le compte absolu de 5xx, qui se déclenche pendant les pics de trafic en réalité sains.
- Lag de réplica RDS à 5 secondes — le lag du reader Aurora doit être en sous-seconde en baseline. Cinq secondes est déjà un problème visible client pour des charges lecture-après-écriture ; l’alarme à trois minutes de dépassement est la fenêtre d’instinct.
- NAT GW à 5,5 Go/min — le quota de bande passante NAT Gateway est de 100 Gbps au total mais des contraintes par flux entrent bien en dessous. Le seuil attrape la sortie soutenue qui signale soit un événement d’exfiltration, soit une charge mal configurée qui devrait être sur un VPC Endpoint.
- Latence TG p95 à 1 seconde — spécifique à la charge. Pour une API exposée aux clients ce devrait être plus serré (200 ms est plus typique) ; pour un service de traitement de fond le seuil est plus haut.
Trois règles sur chaque baseline d’alarmes que nous déployons :
Les notifications partent vers un endroit qu’un humain voit dans les minutes. Un topic SNS sans abonnés est décoratif. Le courriel est trop lent ; PagerDuty ou un chemin de paging équivalent sur les alarmes critiques.
Tester le chemin d’alarme trimestriellement. set-alarm-state ALARM sur une alarme choisie pendant une fenêtre de maintenance. Si le page n’atterrit pas, l’alarme est cassée.
Ajuster les seuils après un cycle métier complet. Les défauts ci-dessus sont des points de départ ; les charges réelles ont des formes et le seuil doit coller à la forme, pas au catalogue.
La baseline tient en une journée concentrée pour déployer, une heure par trimestre pour ajuster, et attrape les modes de défaillance qui apparaissent dans les post-mortems réels. Les onze alarmes ne sont pas le plafond — elles sont le plancher.
Stack Harbor opère le nettoyage IAM, la baseline CloudWatch, AWS Backup et le rythme de contrôle des coûts dans le cadre de Gestion AWS et des Environnements infogérés en continu. Si les alarmes de votre compte se déclenchent sur les mauvaises choses ou ne se déclenchent pas du tout, commencez par une consultation d’infrastructure.