Aller au contenu

La baseline d'alarmes CloudWatch dont tout SaaS de production a besoin — CloudFormation prêt à coller

Les onze alarmes CloudWatch qui attrapent les modes de défaillance qu'un SaaS de production rencontre réellement. Taux 5xx ALB, santé des cibles, connexions et lag de réplica RDS, capacité ASG, bande passante NAT GW, expiration des certificats. Voici l'ensemble.

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 :

  1. Couche applicative — taux 5xx ALB, compte d’hôtes target group malsains, temps de réponse cible p95.
  2. Couche compute — capacité ASG insuffisante, échec de déploiement de service ECS.
  3. Couche données — compte de connexions RDS, CPU RDS, lag de réplica RDS.
  4. Sortie réseau — bande passante NAT Gateway approchant la limite.
  5. TLS — expiration de certificat ACM s’approchant à 30 jours.
  6. 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.