SLOの運用のために OSS shimesabaの導入

カヤックSREの池田です。今回は、カヤックのプロダクトの一つ『Tonamel』で導入したエラーバジェット算出ツール shimesabaの話をします。

shimesabaとは?

github.com

shimesabaは監視サービスであるMackerelを用いて、エラーバジェットを計算しサービスメトリックとして投稿することでSLI/SLOの運用を助けるツールです。 このツールを用いることで、以下のようなグラフが得られます。

  • この図の上部は、エラーバジェットの使用率=信頼性の損失率の推移を表すグラフになっています。
  • この図の下部は、エラーバジェットをいつ?どのくらい?損失したのかを表すグラフになっています。

一言で、エラーバジェットと言ってもいくつかの計算方法が存在します。
今のところshimesabaでは、Rolling windowのコンプライアンス期間で、Windows-based SLOによる評価方法 を採用した場合のエラーバジェットを計算します。
Calendar-based のコンプライアンス期間や Request-based SLO による評価方法を採用したエラーバジェットが必要な場合は、v1時点でのshimesabaは未対応ですので他の方法を検討することになると思います。

shimesabaを導入した効果

shimesabaを導入する以前のTonamelでは、SLOの運用について以下の問題がありました。

  • Redshiftを使ってアクセスログを集計しSLIを算出していたが、SLI集計部のメンテナンスコストが高かった。
  • SLOの判定を行っていたが、エラーバジェットの計算までは行っていなかった。 (SLI集計用のSQLのメンテナンスコストが高かったため、Redshiftでエラーバジェットを計算するまで手を出せてなかった)
  • SLI/SLO関係がredash上に存在したため、Mackerelメトリックとの比較が困難であった。
  • サーバーに起こった一時的なエラーやアラートに過敏に反応していた。

これらの問題を解決するためにshimesabaを開発・導入しました。 SLIの集計はMackerelに、エラーバジェットの計算はshimesabaに任せることで、複雑なSQLのメンテナンスが必要なくなりました。 さらに、計算されたエラーバジェットをMackerelのサービスメトリックとして投稿することで、他のアラートやメトリックとの比較も容易になりました。

また、エラーバジェットの導入によって、SLO違反が発生しサーバーに起こった一時的なエラーやアラート起きても、過敏に反応することなく心の余裕を持つことができるようになりました。 週に一度の定例会では、時折以下のような議論が生まれるようになりました。

  • 『エラーバジェットが最近損失していないが、ユーザー影響のある問題は起きてないか?起きてるならSLOを厳しくしたほうが良いのでは?』
  • 『エラーバジェットをすごい勢いで消費しているが、実際にユーザー影響あるの?ないならSLOが過敏すぎるんじゃないの?』

このようにSLOの品質について、エラーバジェットを用いてユーザーファーストな観点での振り返りができるようになりました。

shimesabaの変遷

このような効果があったshimesabaですが、shimesaba自体は、Mackerel Advent Calendar 2021の7日目の記事でも紹介していました。(当時はv0.5.0)

https://techblog.kayac.com/introducing-shimesaba-for-slo-error-budgets-with-mackerel

この記事の2021年12月頃からTonamelにて試験運用を重ねていました。その結果、2022年4月19日にv1.0.0を迎えて正式運用状態に入りました。 v0時代は Mackerel APIからメトリックを取得して、独自実装の式を用いてSLOの評価を行っていましたが、カヤックのSREチームメンバーやMackerelの皆様にアドバイスを頂き、v1からは大きく変わりました。 詳しくはあとに記述しますが、v1からはMackerelのアラートに基づいてSLOを評価し、1度設置したshimesabaの設定ファイルを極力変更しなくても良くなるようにしました。 そのため、config.yamlの書き方がv0時代から大きく変わっていますので、今回の記事で改めて詳しく導入も含めて説明しようと思います。 現在では、他のプロダクトへより広げていきたいと考えているフェーズになっています。

以下、shimesabaの導入について話します。

shimesaba 導入

今回は、AWS Lambda 上で動かす想定で話します。もちろんCLIツールとしても動きますので、動かし方はいくつか自由度があります。 shimesabaを動かすためには3つの物が必要になります。

  • Mackerel API Key (Writeが可能なもの)
  • SLO評価用の監視ルール
  • config.yaml (設定ファイル)

1. Mackerel API Keyの準備

shimesabaはMackerel APIにアクセスして、エラーバジェットの計算に必要な情報や、サービスメトリックの投稿を行います。 Mackerel API KeyはダッシュボードのAPIキーのタブ から発行できます。

shimesabaは、サービスメトリックの投稿を行いますので、Writeの権限がついているMackerel API Keyが必要になります。

2. SLO評価用の監視ルールの準備

冒頭で、shimesabaは Rolling windowのコンプライアンス期間で、Windows-based SLOによる評価方法 を採用していると述べました。 Windows-based SLOでは、ある時間枠に対して、SLOでもって良好であるのかどうかを判断し、コンプライアンス期間中の良好である時間枠の比率をもとにエラーバジェットを計算します。 shimesabaでは、Mackerelのメトリックにおける時間の最小単位である1分を計算に用いる時間枠として固定しています。 良好であるか?という判断は、設定にて指定された監視ルールのアラートが発生していなければ、良好であると判定します。

よって、shimesabaを動かすためには、SLO評価用の監視ルールを作成する必要があります。

例えば、HTTPリクエストの5xxレート*1をもとにして、可用性に関するSLO評価用の監視ルールを作りたい場合は以下のような式監視を作ることになります。

もちろん、可用性のSLOの定義を外形監視のルールにしても問題ありません。 つまり、具体的なSLOはMackerelの監視ルールの形で表現され、いつSLO違反が起きたかは対象のアラートを追うことで振り返ることができます。 すでにSLOとして活用している監視ルールがある場合は、そちらを用いることも可能だと思います。

また、別途運用しているアラートが存在し、そのアラートの通知と混ざることが煩わしい場合は以下のようにSLO関連のものだけ集めた通知グループを用意すると良いかもしれません。

こうすることで、ほかの通知チャンネルにSLO関連のアラートが送られなくなります。

3. 設定ファイルの準備

ここまで用意したら、設定ファイルを用意します。 yaml形式の設定ファイルの設定例として、以下の例を示します。

required_version: ">=1.1.0" 

destination:
  service_name: prod          # エラーバジェット等のサービスメトリックを投稿する先
  metric_prefix: api          # 投稿されるErrorBudget等のサービスメトリックのプレフィックス (省略した場合は shimesabaになる)
  # 追加のサービスメトリックの有効化設定: いらないサービスメトリックスは、ここでON/OFF可能
  metrics:                    
    error_budget_remaining_percentage:
      enabled: true
    uptime:
      enabled: true  # v1.2.0からデフォルトがOFF
    failure_time:
      enabled: true  # v1.2.0からデフォルトがOFF

rolling_period: 28d         # コンプライアンス期間: ローリングウィンドウのサイズ (28d とかくと 28日, 52h とかくと52時間) 
calculate_interval: 30m     # 計算の期間 = メトリックの投稿間隔  (1hと指定すると最終的に投稿されるメトリックは1時間毎,30m と書くと30分毎, 最低1分)

error_budget_size: 0.1%     # エラーバジェットのサイズ いくつかの指定方法がある。
                            #   %で指定した場合は、rolling_periodの長さに対して、何%の時間をエラーバジェットとして取るのか? (分で切り捨て)
                            #   1h とか 40m とか指定した場合は、その指定した時間の分だけエラーバジェットのサイズをとる。

slo:
  # 可用性に関するSLOを定義する例: 『SLO availability』というprefixを持つアラート もしくは、『api.example.com"』というsuffixを持つ外形監視のアラートのどれかが発生した場合は、SLO違反とみなします。
  - id: availability
    alert_based_sli:
      - monitor_name_prefix: "SLO availability"
      - monitor_name_suffix: "api.example.com"
        monitor_type: "external" 
  # レイテンシーに関するSLOを定義する例: 『SLO latency』というprefixを持つホストメトリック監視のアラートが発生した場合はSLO違反とみなします。 
  - id: latency
    alert_based_sli:
      - monitor_name_prefix: "SLO latency"
        monitor_type: "host"

rolling_period は特に規定がなければ SRE本*2のサンプルに載っている4weeks=28daysを指定しておくのが、最初は短くも長くもなくちょうどよい感じになります。
error_budget_size はエラーバジェットの損失がない場合の最大値ですが、rolling_periodの値に対して 0.1% くらいがちょうどと感じています。 もちろん、厳しく取り扱いたい場合は 100m (100分) などの値を指定して、エラーバジェットを低くしておくのもありだと思います。
calculate_interval は 投稿されるサービスメトリックのtickになりますので、shimesabaの起動間隔と同じにしておくことをおすすめします。
ある程度、高解像度なサービスメトリックが欲しい場合はcalculate_interval5m程度に設定して、5分ごとにshimesabaを起動することになるでしょう。
それほど、細かくなくて良い場合は、30m程度が良いと思います。

slo は複数定義できます。 idは被らずユニークになるように指定してください。投稿されるサービスメトリックが、このidの値によって決まります。 仮に上記の設定で、idlatencyの場合は以下のサービスメトリックが投稿されます。

  • api.error_budget.latency : エラーバジェットの残り時間 (単位:分)
  • api.error_budget_percentage.latency: エラーバジェット使用率。100%なら全て使い切った。0%ならエラーバジェットは損失していない。
  • api.error_budget_remaining_percentage.latency: エラーバジェット残存率。100%なら損失していない。0%ならエラーバジェットはすべて使い切った。
  • api.error_budget_consumption.latency : その時刻に消費したエラーバジェットの時間 (単位:分)
  • api.error_budget_consumption_percentage.latency: その時刻に消費したエラーバジェットのパーセンテージ。最大量の何%を消費したのかを表す
  • api.failure_time.latency: その時刻を基準とするローリングウィンドウ内での合計のSLO違反となった時間 (単位:分)
  • api.uptime.latency : その時刻を基準とするローリングウィンドウ内での合計の良好な時間 (単位:分)

各値の間には以下の関係があります。 - 100 - api.error_budget_percentage.latency = api.error_budget_remaining_percentage.latency - api.uptime.latency + api.failure_time.latency = error_budgetの最大値 - api.error_budget_consumption.latency / error_budgetの最大値 * 100 = api.error_budget_consumption_percentage.latency - api.error_budget.latency / error_budgetの最大値 * 100 = api.error_budget_remaining_percentage.latency

3.5 手元での動作確認

ここまでが、動かし方によらない共通部分となります。 この時点で、お使いの環境のビルド済みバイナリを使って、CLIとして動作確認をすることをおすすめします。 以下のように、--dry-run--dump-reports のオプションを付けることで、実際にはサービスメトリックを投稿せずに、手元で計算のみをしてくれます。

$ shimesaba -config config.yaml -mackerel-apikey <Mackerel API Key> --dry-run --dump-reports  
2022/06/20 18:45:32 [info] start run in the `test-mashiike` organization.
2022/06/20 18:45:32 [notice] **with dry run**
2022/06/20 18:45:34 [info] service level objective[id=availability]: start create reports 
2022/06/20 18:45:34 [info] service level objective[id=availability]: finish create reports 
2022/06/20 18:45:34 [info] error budget report[id=`availability`,data_point=`2022-06-20T08:30:00Z`]: size=200.0000[min], remaining=195.0000[min](2.5%), consumption=0.0000[min](0.0%)
2022/06/20 18:45:34 [info] error budget report[id=`availability`,data_point=`2022-06-20T09:00:00Z`]: size=200.0000[min], remaining=195.0000[min](2.5%), consumption=0.0000[min](0.0%)
2022/06/20 18:45:34 [info] error budget report[id=`availability`,data_point=`2022-06-20T09:30:00Z`]: size=200.0000[min], remaining=195.0000[min](2.5%), consumption=0.0000[min](0.0%)
2022/06/20 18:45:34 [info] service level objective[id=availability]: start save reports 
2022/06/20 18:45:34 [info] service level objective[id=availability]: finish save reports 
2022/06/20 18:45:34 [info] service level objective[id=latency]: start create reports 
2022/06/20 18:45:34 [info] service level objective[id=latency]: finish create reports 
2022/06/20 18:45:34 [info] error budget report[id=`latency`,data_point=`2022-06-20T08:30:00Z`]: size=200.0000[min], remaining=185.0000[min](7.5%), consumption=0.0000[min](0.0%)
2022/06/20 18:45:34 [info] error budget report[id=`latency`,data_point=`2022-06-20T09:00:00Z`]: size=200.0000[min], remaining=185.0000[min](7.5%), consumption=0.0000[min](0.0%)
2022/06/20 18:45:34 [info] error budget report[id=`latency`,data_point=`2022-06-20T09:30:00Z`]: size=200.0000[min], remaining=185.0000[min](7.5%), consumption=0.0000[min](0.0%)
2022/06/20 18:45:34 [info] service level objective[id=latency]: start save reports 
2022/06/20 18:45:34 [info] service level objective[id=latency]: finish save reports 
2022/06/20 18:45:34 [info] run successes. run time:1.633562403s

4.0 AWS Lambda 関数としてデプロイ

shimesabaはGo言語製のアプリケーションで、AWS Lambda関数のbootstrapとしても機能します。 linuxのamd64のビルド済みバイナリをダウンロードしてきて、bootstrapという名前に変更してconfig.yamlとともにzipファイルにまとめることで、そのままLambda関数として動きます。

lambda.zip
├── bootstrap
└── config.yaml

また、弊社の長田が制作したssmwrapを内包しており、Mackerel API KeyはSSM Parameter Store経由で与えることが可能になっています。

例えば、 /shimesaba/MACKEREL_APIKEY というパラメーター名でMackerel API Keyを保存した場合は、以下のような環境変数を与えることで動作すると思います。

  • SSMWRAP_PATHS=/shimesaba/
  • SHIMESABA_CONFIG=config.yaml

あとは、Amazon EventBridgeで定期的にこのLambda関数を起動するルールを作成すれば終了です。 EventBridgeルールのscheduled_expressionは、config.yamlのcalculate_intervalと同じにしておくことをおすすめします。

まとめ

今回は、 Rolling windowのコンプライアンス期間で、Windows-based SLOによる評価方法を採用したエラーバジェットを計算するツールshimesabaの導入について話しました。 このツールを使うことで、Mackerelをお使いの場合は、SLOの導入をある程度お手軽にできると思います。 もっと細かいツールの使い方等は、Mackerel User Groupのslackに#shimesabaのチャンネルがありますので、そちらでお聞きいただければ幸いです。

カヤックでは、SLOの実践について一緒に悩めるエンジニアについても募集しています。

*1:ここではMackerelのAWSインテグレーションで取得したAWS Application Load Balancerのメトリクスを使用しています

*2: