CloudWatchの複合アラームで、ELB(ALB)の5XXエラーの監視(検知・通知)を、「いい感じ(重複しないように)」にやろうとしたら、うまくいきませんでした。
ついでなので、複合アラームの作り方なども記載してみました。
これをCloudWatch複合アラーム(CompositeAlarm)によって、上記のように500,502,503,504のときに、重複しない検知・通知の仕組みを構築します。
また、CloudFormationで構築を行います。
ただし、それが「うまくいかなかったお話」になります。
※実はある方法によって「いい感じ」を実現することができた話を混ぜた内容で登壇したので、本記事の一番下に資料を載せました。(2023/05/29)
ELBにおける、ALB(Application Load Balancer)の5XXエラーを対象としています。
ALBの5XX系メトリクス(グラフ)には、以下の2種類があります。
それぞれの詳細の説明は省略しますが、今回対象にするのは、後者のHTTPCode_ELB_5XX_Countです。
というのもこのELB(ALB)の5XXエラーには、HTTPCode_ELB_5XX_Countというメトリクス以外に、500,502,503,504のときはそれぞれのメトリクスが別で存在します。
実際に上記エラーが起きてslackなどに通知する際には、「5XX」よりもそれぞれの具体的な数値で通知してくれた方が便利ですよね。
しかし上記の具体値のメトリクスで通知の設定はするが、それ以外のエラーのときのためにも、「5XX」のメトリクスの方でも通知しておきたくなります(※)。
※上記ステータスコード以外の5XX系のエラーが発生することはあまりないかもしれませんが・・・
具体的には以下公式でALBにおける各エラーコードが記載されています。
ところが「5XX」も通知するようにすると、500,502,503,504を検知したときに、それぞれのメトリクスだけでなく5XXの方のメトリクスでも「重複」して通知が来てしまいます。
それはちょっと嫌だなあ、なんて思った時に、CloudWatchの複合アラーム(CompositeAlarm)という機能で、重複を排除した通知の仕組みを作ってみました。
そして実装してみたら、「想定通りの挙動になりませんでした」。
上記手法では、うまくいった(想定通りの挙動になった)としても、500,2,3,4とそれ以外の5XXエラーが同時(同評価期間内)に発生した場合、5XXメトリクスの方の通知が来ません。(例えば500と501が同時に起きたとき、500の通知だけ来る)
それは実際にエラーが起きた時は結局AWSコンソールのグラフを見に行くと思うのでそれで判別すればいいかなという方向で、頑張らないことにしました。
具体的には、CloudWatch Alarm(CloudWatchアラーム)と、CloudWatch CompositeAlarm(CloudWatch複合アラーム)を使います。
CloudFormationで構築するため、yamlテンプレートを記載します。
※GitHubにもあります。
yaml折り畳み
AWSTemplateFormatVersion:'2010-09-09'# ------------------------------------------------------------## Metadata# ------------------------------------------------------------#Metadata:AWS::CloudFormation::Interface:ParameterGroups:-Label:default:"LoadBalancerFullName"Parameters:-LoadBalancerFullName-Label:default:"SNSTopicArn"Parameters:-SNSTopicArn# ------------------------------------------------------------## Parameters# ------------------------------------------------------------#Parameters:LoadBalancerFullName:Type: StringSNSTopicArn:Type: String# ------------------------------------------------------------## Resources# ------------------------------------------------------------#Resources: # ============================================== # # HTTPCodeELBOther5XXCount (Composite Alarm) # ============================================== # ### 5XXがアラーム状態で、500,502,503,504がOK状態のとき(それ以外の5系)に発火HTTPCodeELBOther5XXCountAlarm:Type: AWS::CloudWatch::CompositeAlarmProperties:AlarmName: ALB-HTTPCodeELBOther5XXCount-AlarmActionsEnabled:trueAlarmActions:-!Ref SNSTopicArnAlarmRule:!Sub"ALARM(${HTTPCodeELB5XXCountAlarm}) AND NOT (ALARM(${HTTPCodeELB500CountAlarm}) OR ALARM(${HTTPCodeELB502CountAlarm}) OR ALARM(${HTTPCodeELB503CountAlarm}) OR ALARM(${HTTPCodeELB504CountAlarm}))" # ============================================== # # HTTPCodeELB5XXCount from metrics # ============================================== #HTTPCodeELB5XXCountAlarm:Type: AWS::CloudWatch::AlarmProperties:AlarmName: ALB-HTTPCodeELB5XXCount-AlarmActionsEnabled:false ### 500,502,503,504との複合アラームでの通知をするのでこちらではAction=falseAlarmActions:-!Ref SNSTopicArnMetricName: HTTPCode_ELB_5XX_CountNamespace: AWS/ApplicationELBDimensions:-Name: LoadBalancerValue:!Ref LoadBalancerFullNameStatistic:"Sum"Period:300EvaluationPeriods:1DatapointsToAlarm:1Threshold:0ComparisonOperator: GreaterThanThresholdTreatMissingData: notBreaching # ============================================== # # HTTPCodeELB500Count from metrics # ============================================== #HTTPCodeELB500CountAlarm:Type: AWS::CloudWatch::AlarmProperties:AlarmName: ALB-HTTPCodeELB500Count-AlarmActionsEnabled:trueAlarmActions:-!Ref SNSTopicArnMetricName: HTTPCode_ELB_500_CountNamespace: AWS/ApplicationELBDimensions:-Name: LoadBalancerValue:!Ref LoadBalancerFullNameStatistic:"Sum"Period:300EvaluationPeriods:1DatapointsToAlarm:1Threshold:0ComparisonOperator: GreaterThanThresholdTreatMissingData: notBreaching # ============================================== # # HTTPCodeELB502Count from metrics # ============================================== #HTTPCodeELB502CountAlarm:Type: AWS::CloudWatch::AlarmProperties:AlarmName: ALB-HTTPCodeELB502Count-AlarmActionsEnabled:trueAlarmActions:-!Ref SNSTopicArnMetricName: HTTPCode_ELB_502_CountNamespace: AWS/ApplicationELBDimensions:-Name: LoadBalancerValue:!Ref LoadBalancerFullNameStatistic:"Sum"Period:300EvaluationPeriods:1DatapointsToAlarm:1Threshold:0ComparisonOperator: GreaterThanThresholdTreatMissingData: notBreaching # ============================================== # # HTTPCodeELB503Count from metrics # ============================================== #HTTPCodeELB503CountAlarm:Type: AWS::CloudWatch::AlarmProperties:AlarmName: ALB-HTTPCodeELB503Count-AlarmActionsEnabled:trueAlarmActions:-!Ref SNSTopicArnMetricName: HTTPCode_ELB_503_CountNamespace: AWS/ApplicationELBDimensions:-Name: LoadBalancerValue:!Ref LoadBalancerFullNameStatistic:"Sum"Period:300EvaluationPeriods:1DatapointsToAlarm:1Threshold:0ComparisonOperator: GreaterThanThresholdTreatMissingData: notBreaching # ============================================== # # HTTPCodeELB504Count from metrics # ============================================== #HTTPCodeELB504CountAlarm:Type: AWS::CloudWatch::AlarmProperties:AlarmName: ALB-HTTPCodeELB504Count-AlarmActionsEnabled:trueAlarmActions:-!Ref SNSTopicArnMetricName: HTTPCode_ELB_504_CountNamespace: AWS/ApplicationELBDimensions:-Name: LoadBalancerValue:!Ref LoadBalancerFullNameStatistic:"Sum"Period:300EvaluationPeriods:1DatapointsToAlarm:1Threshold:0ComparisonOperator: GreaterThanThresholdTreatMissingData: notBreaching
パラメータで渡すSNSTopicArn、LoadBalancerFullNameに関してですが、
SNSTopicArnは、アラーム時にアクション(AlarmActions)として通知する、通知先のSNSトピックのARNになります。
LoadBalancerFullNameはロードバランサーのフルネームで、実際にALBの5XXメトリクスをターゲットにするCloudWatch AlarmのDimensions(Name: LoadBalancer)のValueに指定するものです。
CloudFormationでALB(AWS::ElasticLoadBalancingV2::LoadBalancer
)を作成する場合、ALBリソースを定義して、Outputsで!GetAtt (ALBの論理ID).LoadBalancerFullName
で出力できます。
早速、一番の肝である、CloudWatch複合アラーム(AWS::CloudWatch::CompositeAlarm)になります。
複合アラームとは、他のアラームの状態(主に複数)を利用して計算し、その条件がTRUEになるときにアラーム状態になるものです。
### 5XXがアラーム状態で、500,502,503,504がOK状態のとき(それ以外の5系)に発火HTTPCodeELBOther5XXCountAlarm:Type: AWS::CloudWatch::CompositeAlarmProperties:AlarmName: ALB-HTTPCodeELBOther5XXCount-AlarmActionsEnabled:trueAlarmActions:-!Ref SNSTopicArnAlarmRule:!Sub"ALARM(${HTTPCodeELB5XXCountAlarm}) AND NOT (ALARM(${HTTPCodeELB500CountAlarm}) OR ALARM(${HTTPCodeELB502CountAlarm}) OR ALARM(${HTTPCodeELB503CountAlarm}) OR ALARM(${HTTPCodeELB504CountAlarm}))"
大事なのが複合アラーム特有のパラメータであるAlarmRule
で、これが唯一のCloudWatch Alarmのパラメータとの違いです。
ここに、複数のアラームをもとに状態遷移する、複合アラームのルールを書きます。
ここでは、[500|502|503|504]以外の5XXを通知するアラームにします。つまり、5XXが発火して、[500|502|503|504]が発火していない状態です。
具体的には以下のようになります。
ALARM(${HTTPCodeELB5XXCountAlarm})ANDNOT ( ALARM(${HTTPCodeELB500CountAlarm}) OR ALARM(${HTTPCodeELB502CountAlarm}) OR ALARM(${HTTPCodeELB503CountAlarm}) OR ALARM(${HTTPCodeELB504CountAlarm}))
文章にすると、以下のようになります。
これがうまく動くと、5XXが発火して、[500|502|503|504]が発火していない状態のとき、つまり501などのステータスのときに通知が来ます。
上記のように5XXと、500,2,3,4のアラームを作成します。
500,2,3,4の方は普通のアラームなので省きますが、5XXの方では、ActionsEnabled
をfalseにしています。
HTTPCodeELB5XXCountAlarm:Type: AWS::CloudWatch::AlarmProperties:AlarmName: ALB-HTTPCodeELB5XXCount-AlarmActionsEnabled:false ### 500,502,503,504との複合アラームでの通知をするのでこちらではAction=falseAlarmActions:-!Ref SNSTopicArn
これは、複合アラームであるHTTPCodeELBOther5XXCountAlarm
が発火するときに、もとの5XXのメトリクスのアラームは発火したくないので、falseにしておくことで発火の際のアクションが実行されません。
※AlarmActionsは指定する必要がないのですが、他に合わせて(また気軽にもとに戻せるように)SNSTopicArnを残しています。
上記のコードでデプロイし、いざしばらく運用してみました。
すると実際にサーバ側(ELB)でエラーが発生し、「HTTPCodeELBOther5XXCountAlarm」の方、つまり「500,2,3,4でない5XX」の方のアラーム通知がslackに来ました。
「うおお、うまくいった」
「でも、500でも502でも503でも504でもないステータスなんてそんな簡単に起こるか・・・?」
という疑問が浮かびながらも、数秒後にさらにslackに通知が来ました。
「504・・・」
504エラーの通知が来てしまいました。
実際にAWSコンソールのCloudWatchのメトリクスを見に行くと、
でした。
つまり、501など、その他のエラーは起きていませんでした。
いろいろ考えてみたのですが、まず事象としては以下になります。
まず、Other5XX通知が来たということは、確かにその時は504は発火せず5XXのみ発火していたはずです。
そして、後から504通知が来たということは、「5XXが先にメトリクスにputされて、後から504がputされた(またはputが同時で取得(?)のタイミングがずれた)」ということになります。
評価期間や評価タイミングの問題もあるかもしれませんが、もし発火タイミングとして「5XX -> 504」という内部の仕組みだとすると、毎回この挙動が起きてしまいます。
たまたまだとしても、1回目から起きてしまうと、また起きるんじゃないかとちょっと不安になります。
公式ドキュメントにも
ALARM(CPUUtilizationTooHigh) AND ALARM(DiskReadOpsTooHigh)
ALARM(CPUUtilizationTooHigh) AND NOT ALARM(DeploymentInProgress)
などの例があったりして、似たような条件だしいけるだろうと思っていました。
常時(毎分)メトリクスにputされ続けるものだと判定タイミングが多少ずれても起きづらい・・・?
今回のALBの5XXエラーメトリクスのように、起きたときだけメトリクスにputされるものだと判定タイミングずれが起きやすい・・・?
それともたまたま・・・?
そもそもALBのメトリクスの仕組み上そうなる・・・?
「1回、しかも最初に起きたのであれば、今後も起きる可能性があるかもな」
「回数が少なくても、たまに起きるようでは逆にノイズになるな」
ということで、その後挙動を再現させたり、深く調査してみたりはしませんでした。
評価期間や判定データポイントの数を変えたりするのも考えたのですが、結局タイミング次第で起きたりするんじゃないかなと思い、諦めました。
そして、そっと複合アラームや各具体値のアラームは削除して、5XXのみのアラームに戻してデプロイしました。。。
ほとんど考察や検証に時間をかけずに即断してしまった(あまり時間がなかった)ので、もしかしたら原因が違ったり、回避策があったり、何かが間違っていたり、実はほとんど起きなかったりするのかもしれません。
でもCloudWatchの複合アラームを使ってみるという経験が踏めたので、まあ良かったかなと思います。
時間があるときにでも再度調査・検証してみたり、もっとちゃんと考察しようかなと思います。
2023/05/29開催「JAWS-UG SRE支部 #6」にて、実はある方法によって実現することができた話を混ぜて登壇させて頂きました。
そのソリューションをCDKで構築できるようにし、そのCDKコンストラクトライブラリをConstruct Hubに公開しました。
よかったらぜひご覧下さい。(実現方法の詳細や流れも多少書いています。)
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。