Application Signalsでバーンレートアラームを学ぶ

# はじめに
　先月開催されたAWS 春のObservability祭り 2025に参加しました。Observabilityに関するAWSのサービスについて学ぶことができる、とても良いイベントでした。おおよそ半年おきに開催されており、私は3回目の参加でした。
 
 https://aws.amazon.com/jp/blogs/news/awsobservabilityfes2025spring/
 
　多数のLTの中でSenior Developer Advocateの山口さんからバーンレートアラームについての解説がありました。解説を聞いて、私自身は旧来の監視（固定しきい値、メッセージ監視）の経験しかないため、今後の運用を考えるとSLI、SLO、エラーバジェット、バーンレートアラームといったキーワードについてしっかりと理解したいと思いました。Application Signalsの設定で簡単に始められるというお話だったので、実際に手を動かしながら学ぶことにしました。
　Observability初学者のため、AWSの以下ドキュメントを読みながら進めてきましたが、解釈に誤りや認識齟齬があればご指摘いただけると幸いです。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-ServiceLevelObjectives.html
 
# 実施した環境
　シンプルなAPIGateway、Lambda、DynamoDBの構成をCDKでデプロイして利用しました。イメージは以下の形となります。
 
![as000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/50f89bc2-3b5b-4bb9-bc66-e9a80eb8de94.png)



Lambda関数に対してApplication SignalsとX-Rayを有効化しました。
`設定`→`モニタリングおよび運用ツール`→`その他の監視ツール`の`編集`から遷移できます。

 ![as_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/8ca24de9-73dc-4be4-8e26-8e04f3e95a9a.png)

# 用語について
今回取り扱うキーワードについて、私なりの解釈です。
|キーワード|私の理解|
|--------|-------|
|SLI|サービス品質をOKと判断する指標<br>（例：総リクエストに対するレイテンシーが1秒以下のリクエストをOKとする）|
|SLO|どの程度SLIを守れたら、OKとするかの指標<br>（例：過去30日間のリクエストのうち、99%以上がSLIを満たしていること）|
|エラーバジェット|どれくらい失敗が許容できるかの指標。<br>（例：過去30日間のリクエストのうち、最大1%まではSLIを超えてもOK(予算を使ってもOK)）|
|バーンレート|エラーバジェットがどれくらいの速さで消費されているかの指標<br>（例：このままのエラー発生ペースが続いた場合、SLOが守れるか判断するための数値）|
|ルックバックウィンドウ|SLOの達成率やバーンレートの消費量を評価するために、過去どれだけの期間を元に計算するかという指標<BR>（例：60分の期間内の実績を元にSLI、SLOを評価）|


# Application Signalsの設定
順番に設定していきます。
左ペインで`CloudWatch`→`Application Signals`→`サービス`を選択します。

当該AWSアカウントで初めてApplication Signalsを有効にするときは、`サービスの検出を開始`を選択します。

![as010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/d847c8fd-246e-45f4-96dd-5a4bdcb05a87.png)

ポップアップでチェックボックスを入れ、`サービスの検出を開始`を選択します。

![as011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/8864987a-6219-4001-b416-29c8e3ada4ac.png)

この操作を行うことで、ロール`AWSServiceRoleForCloudWatchApplicationSignals`が作成されて、ポリシー`CloudWatchApplicationSignalsServiceRolePolicy`が紐づけられていました。

続いて`Application Signalsを有効にする`を選択します。
今回はLambda関数に設定して、モニタリングする機能を選択で対象のLambda関数を選択して`完了`を選択します。


![as012.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/f1eaca0a-feb2-46d5-8f4e-9debc80b6e66.jpeg)

## SLOを作成する
　いよいよSLO作成に入っていきます。

![as013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/612c2ebf-0806-4ff3-bf4e-e938d5601b93.png)

左ペインで`サービルレベル目標（SLO）`を選択し、`SLOを作成`をクリックします。
SLOの名前を入力したら、各項目の設定をしていきます。

　私が検証した際には、SLO作成時に対象のサービスが検出されていないと設定をすることができませんでした。監視対象のアプリケーションが一定程度、起動した状態でないと、マネージメントコンソールからはサービスとしては検出されず、SLOの作成も行えない形になっていました。


### 1. サービスレベル指標（SLI）の設定
　まず、SLIの設定から始めていきます。
 Application Signalsでは大きく3つのタイプが選択できるようになっていました。
 今回のようなApplication Signalsに対応しているリソース（Lmbda）のレイテンシーをSLIとして設定する場合は、`サービスオペレーション`を選択する形になります。`Dependency`は「依存」ということなので、処理が依存する先のリソースのトレース（外部APIなど）を行いたい場合に使用するものと理解しました。
 　また、`CloudWatchメトリクス`はApplication Signalsに対応していないリソースやオンプレ機器のログをCloudWatchに集約し、そのメトリクスを活用して監視する際に活用するものと理解しました。
 
　今回使用する`サービスオペレーション`の計算方法としては2種類提供されていました。
　・`リクエスト`を選択した場合、SLIとしては基準を設定するのみでした。しきい値を満たさなかったら、SLO未達という形で扱われます。全リクエストに対して、SLIのしきい値を満たした割合でSLOが評価されます。
　・`期間`を選択した場合、期間内における統計値での判断ができる形になっています。(SLIとしてパーセンタイル値での統計ベースの評価になります)
 
　今回の設定では、シンプルに検証できる`リクエスト`を選択し、`レイテンシーが1000ms以下`であることという基準で設定しました。

![as003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/b0a53e55-35a9-43c2-bab9-688509cf2a3e.png)


### 2. サービスレベル目標（SLO）の設定
　続いてSLOの設定になります。間隔、達成目標、警告のしきい値を設定していきます。
 「間隔」は、SLOを測定するウィンドウ幅になります（直近30日間で評価するなど）。今回は短期間で評価したかったので、`1時間`としました。
　
　「達成目標」は、特定の期間において何%のリクエストがSLIを満たす必要があるか、という指定になります。
　今回は`99%`を基準値としました（デフォルトは99.9%でした）。
 
　「警告のしきい値」は、SLOの達成のために警告と判断、検知するための基準となります。今回はデフォルトの`30%`としました。

![as004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/31e0bee3-83cc-4f11-aae1-9036df02a634.png)
 
　今回の設定をベースに考えると、例えば60分で10,000リクエストを受け付けるとした場合、SLOを満たすためには、全リクエストの1％（100リクエスト）未満は、1000msを超えてOK。100リクエストの30%（30リクエスト）までは、利用可能なエラーバジェット（1000msを超えたリクエストが70リクエストに達すると警告発生）となる。
 と私は理解しました。

　いずれの設定値も取り扱うサービスのビジネスインパクトを考慮して、一旦設定して、ビジネスの拡大やクライアントへの影響に応じて見直していく数値と考えています。

### 3. バーンレートの設定
　いよいよバーンレートの設定です。
　エラーレートの計算を行うため、基準とする時間をルックバックウィンドウとして、分単位で設定できるようになっています。（最小1分から最大10,080分(7日間)まで設定可能）
　また、複数のバーンレートを設定できるようになっていました。（最大10個まで設定可能）
 
　次に設定するバーンレートアラームは、このルックバックウィンドウを使って、今どれくらいの速さでエラーバジェットを消費しているかを判断します。

　今回は、ルックバックウィンドウは`5分`と`30分`を設定しました。

　![as005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/3aad7e17-23e5-4524-8128-eda4f466c4c5.png)


 #### バーンレートを計算してみる
今回の設定をベースにエラー発生状況を仮定して、バーンレートアラームを算出すると以下のようになりました。
（前提）
　SLI：1000ms以下のリクエストをOKと判断する
　SLO：60分のローリング間隔で99%がSLIを達成
　エラーバジェット：60分のローリング間隔で1%

バーンレートの計算式はドキュメントに記載がありました。
```
burn rate threshold = (X%) × SLO interval length / look-back window size
X：消費されたエラーバジェット割合
```

　**ルックバックウィンドウ：5分　におけるバーンレートアラームの算出**
　5分間でエラーバジェットの50%を消費した場合に緊急のアラームをあげる
　X = 50% = 0.5
　SLO interval length = 60分
　look-back window size = 5分
　burn rate threshold = 0.5 × 60 / 5 = 6.0
　**バーンレート：6.0**　･･･SLOの6倍のペースでエラーバジェットを消費
 
　**ルックバックウィンドウ：30分　におけるバーンレートアラームの算出**
　30分間でエラーバジェットの80%を消費した場合に緊急のアラームをあげる
　X = 80% = 0.8
　SLO interval length = 60分
　look-back window size = 30分
　burn rate threshold = 0.8 × 60 / 30 = 1.6
　**バーンレート：1.6**　･･･SLOの1.6倍のペースでエラーバジェットを消費

### 4. バーンレートアラームの設定  
　いよいよバーンレートアラームの設定です。

　バーンレートアラームは`バーンレートしきい値を直接設定`を選択して、先ほど計算した数値を指定、既存のSNSトピックを選択しました。
　今回のルックバックウィンドウ5分では、エラーバジェットをSLOの6倍以上で消費している場合にしきい値超過と判断されます。
　ルックバックウィンドウ30分では、エラーバジェットをSLOの1.6倍以上で消費している場合にしきい値超過と判断されます。

![as019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/24c82e14-0d04-4498-9480-da857ef1dca3.png)


#### (動作確認中)消費予算のパーセンテージで指定するとSLO期間が1日を基準に算出される（2025/5/5時点）
 バーンレートアラームで`消費予算のパーセンテージでバーンレートしきい値を設定`を選択して、総エラーバジェットの何%を消費して良いかを指定すると、作成されたアラームのしきい値が想定と異なる値になりました。
　ルックバックウィンドウ5分で、エラーバジェットの50%を消費した場合を想定したので、バーンレートのしきい値は6になると想定していました。また、ルックバックウィンドウ30分では、エラーバジェットの80%を消費した場合を想定したので、バーンレートのしきい値は1.6になると想定していました。
 
![as020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/c28efb13-61a7-4132-bdbc-9e8b6a03703e.png)

　結果として作成されたアラームは以下の値になっていました。
　5分のルックバックウィンドウのバーンレートのしきい値は144、30分のルックバックウィンドウは38.4となっていました。この数値ですが、想定していたしきい値の24倍となっています。つまり、SLOを24時間間隔で算出して、そのエラーバジェットを5分で50%消費、30分で80%消費した場合にしきい値超過となる計算でした。

![as021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/bb5c4833-809b-4fd2-8279-f07226f5fb1a.png)

![as022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/63fd8990-50eb-424b-8108-5b35aedfa351.png)

　SLO作成時には1ローリング時間で作成しているのに、バーンレート作成時にはなぜこの値になるのか、この点は引き続き確認をしていこうと思います。


#### （動作確認中）バーンレートアラームの設定項目が消える（2025/5/6時点）
  バーンレートを2つ追加後、バーンレートアラームを設定する項目で、1つ目のルックバックウィンドウにチェックを入れると、2つ目のルックバックウィンドウの表示が消えます。マネコンを英語表記に変えても同様の事象が発生しました。2つ以上のルックバックウィンドウを設定した場合でも、最後のルックバックウィンドウのチェックボックスが消えました。
  最後のルックバックウィンドウにアラームを設定したい場合は、当該のルックバックウィンドウを一旦Removeし、改めてルックバックウィンドウを設定すると、アラームのチェックボックスが出てきます。
  （一応、AWSサポートに事象について共有を行いました）

![as006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/960b771d-7eb0-4760-a117-2d04565165ee.png)

![as007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/01338acd-45b3-4b4d-ad06-8dd6bb1734b0.png)

### 5. SLOアラームの設定
　簡易通知のアラームとして、3つのアラームが設定可能となっていました。
　・SLI状態アラーム　：SLIを下回るたびにアラームが発行
　・SLO達成目標アラーム　：SLOの達成目標を下回るたびにアラームが発行
　・SLO警告アラーム　：SLOが警告のしきい値（SLI作成時に指定したしきい値）を超えるたびにアラームが発行
 
 ![as014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/ebd37656-f6f4-4185-9204-7af8d3948de2.png)

　いずれもしきい値超過のたびに通知を受けることになります。そのままSNS通知を設定すると、即時対応が必須ではない場合にも通知を受け取ってしまうので、アラーム疲れの一因になるのではないかと思いました。（こちら単体でのアラーム発行をする際の使い道が私にはピンときませんでした。作成したメトリクスを組み合わせて複合アラームをSNSで通知するなら納得ができます）

### (参考)自動作成されたアラーム
　以下のアラームが作成されていました。

　SLI状態アラーム（エラーが1％以上になるとアラーム）
 
![as015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/a404fd20-ad98-45d6-8942-c3f4bd8e3dc0.png)


　SLO達成目標アラーム（SLOが99%以下になるとアラーム）
 
![as016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/248e261a-1c7f-4a2e-9d78-b0d7de62c7bc.png)

　SLO警告アラーム(SLOが99.3%以下になるとアラーム)

![as017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/dab56b05-c9b8-4502-ab01-1d3dba883f51.png)


### 6. Set SLO time window exclusion
 なぜかこの項目だけ、英語表記になっていました。SLOの算出に含めない時間帯を指定する形になります。
 定期的にメンテナンス時間を設けている場合、SLOの算出からは除外したいこともあろうかと思います。そんな時は、x月x日のx時x分からx分間は毎日メンテナンスとして扱うといったことも実現できます。

### 設定後の状態

　サービス画面で以下のようにSLIのステータス、サービスのリクエスト数やレイテンシーが見られるダッシュボードが作られました。
 
 ![as025.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/b976bd30-3f5d-483c-923e-959e59babcfb.png)

　また、以下のようにSLOの画面ではSLOの達成度だけでなく、エラーバジェット、バーンレートが可視化されていました。

 ![as026.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/a3b02f53-bba1-405d-b0e3-2efad6482dd5.png)

# 動作検証
　実際にAPIリクエストを投げて、遅延を発生させることでアラーム発報までを確認してみました。
Lambda起動時にwaitが入るようにコードを修正し、5分のルックバックウィンドウでSLOを下回る状況を作り出しました。

SLOの画面にて、SLOの達成度が低下、エラーバジェットも消費しています。そして、バーンレートが急激に上昇しました。

![as029.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/fa7ce2c3-05da-4e91-8b28-8cc7f9769af8.png)

CloudWatchアラームでもバーンレートのしきい値を超過したということでアラーム状態となりました。

![as027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/44994b7d-97bf-4490-a2b7-770052c21d22.png)

この内容はSlackへの通知まで受けられました。

![as028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/37ce4b25-1238-4eba-bfcf-9511a41a188b.png)


### (参考)Application SignalsとFault Injection Serviceとの共存はできない
　今回の動作検証でLambda関数の遅延を一定の割合で引き起こす方法として、Fault Injection Serviceの利用を考えました。結論から言うと、Application Signalsとの共存ができませんでした。Application Signals、Fault Injection ServiceともにLambda関数の環境変数`AWS_LAMBDA_EXEC_WRAPPER`を利用します。
　この環境変数`AWS_LAMBDA_EXEC_WRAPPER`に、Fault Injection Serviceを使用する際は`/opt/aws-fis/bootstrap`を、Application Signalsを利用する際は、`/opt/otel-instrument`を設定する必要があります。同一の環境変数に複数の値を設定することはできませんので、同時利用できませんでした。ということで、今回はFault Injection Serviceを諦めて、Lambda関数自体にwait処理を入れる形にしました。

# まとめ
　今回、実際に手を動かしてみることで、バーンレートアラームでの監視方を学ぶことができました。ルックバックウィンドウでの計算も自動で行ってくれるため、とても簡単に始められました。一方で、自動で作成されるアラームをそのまま使うのではなく、本当に対処が必要な状態に絞り込む検討が必要と感じました。AWSドキュメントには異なるウィンドウを持つ複数のアラームを組み合わせる方法についても解説がありました。単発のSLI、SLOのしきい値監視はせず、バーンレートの複合アラームを作成することで、適切なアラーム設定ができそうです。この辺りは引き続き勉強していきます。
　また、今回はAWSネイティブで実装しましたが、今後3rd partyのObserbavility製品での監視設定も学んでいきたいと思います。
　この内容が、これからバーンレートアラームでの監視を検討しようとするどなたかの一助になれば幸いです。