この記事はMackerel Advent Calendar 2021の7日目です。
こんにちは、SREチーム所属の@mashiikeです。
皆様はSLO
とエラーバジェット
という言葉を聞いたことはありますか?
サービスの信頼性を保証することを目標するSRE(Site Reliability Engineer/Site Reliability Engineering)の領域に携わってる方なら聞いたことがあると思います。
今回は、SLO
とエラーバジェット
に関して、Mackerelを用いてサービス/サーバー監視をしている際に便利なツールとして shimesaba
というものを作った話をします。
はじめに
本題に入る前に、SLI
やSLO
, エラーバジェット
という言葉について触れておきます。
これらの言葉は、ざっくりと説明すると以下のようになります。
- SLI(Service Level Indicator): サービスレベル指標、サービスの品質を測るための指標です。Mackerelをお使いの皆様にとっては、ホストメトリックやサービスメトリック、それらを組み合わせた式グラフ等を思い浮かべるとわかりやすいと思います。
- SLO(Service Level Objective): SLIに対する目標値。
- エラーバジェット: 許容される信頼性の損失を表したもの。SLOを満たせなかった度合い。
より細かい説明や言葉の正確な定義等は、『SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム』(通称SRE本)を参照していただければ幸いです。
ところで、Mackerelをお使いのSREの方は、以下のようなことを思ったことはありませんか?
- SLOの具体的な適用方法がわからない。
- エラーバジェットの算出方法や、運用方法がよくわからない。
- 今どれくらいエラーバジェットが残ってるのか、素早く把握したい!
そこで作ったのが shimesaba
というツールです。
SLO/エラーバジェット を算出するツール shimesaba
このツールは、MackerelをSLIを計測するサービスとして捉え、Mackerelに保存されている各種メトリックからSLOやエラーバジェットを計算してMackerelのサービスメトリックとして投稿するツールです。
※ shimesaba
の名前は 鯖(Mackerel)の運用をもっと締める
の発想から 〆鯖
という料理の名前をつけました。
先に、試験運用中のサービスにおけるSLI/SLOダッシュボードをお見せします。
使い方
shimesaba
はCLIのツールまたはAWSのLambda関数のbootstrapとして動くように作られており、cronやEventBridge等で定期的に実行する想定となっています。
CLIとして動かす場合は以下のように実行します。
$ shimesaba --config config.yaml --mackerel-apikey $MACKEREL_APIKEY run
試験運用の現場ではLambda関数として動かしております。そちらの動かし方は READMEをご参照いただければ幸いです。
肝心の config.yaml の中身ですが、色々なケースに対応できるように細かくなってしまいました。
詳細はREADME を参照していただくとして、今回は例として以下のケースでの設定を紹介します。
SLOやエラーバジェットを適用する先のサービスの想定としては以下のようなものとなっています。
- リクエスト駆動型のサービス
users
とtasks
という2つのマイクロサービスを持っており、ALB (Application Load Balancer)
によって負荷分散されているtasks
マイクロサービスでエラーが発生したとしても、グレイスフルデグレーションによってリクエストは成功するような性質を持つ
各SLOに共通する内容として、 - 直近28日のローリングウィンドウで1時間毎に算出 - SLOの評価は1分ごとに行う - エラーバジェットは0.1%。 28日(=40320分)× 0.1% で40分
3つのSLOを設定します。
- 可用性SLO : ALBのリクエスト数に関して 5xx以外のリクエスト数 / 全体のリクエスト数
= リクエスト成功率
が97%以上を目標とする。
- レイテンシーSLO : ALBのレスポンスタイムに関して p99 レスポンスタイム
が5秒以下 かつ p90 レスポンスタイム
が1秒以下を目標とする。
- 品質SLO : tasks
マイクロサービスのエラー数 が5未満となることを目標とする。
上記のような場合、shimesaba
の設定は以下のようになります。
required_version: ">=0.5.0" x-alb-metric-base: &alb-metric-base # ALBのホストメトリックに関する共通の設定(yamlアンカー) type: host # ホストメトリックの場合はtypeをhost設定 service_name: prod # 取得元のMackerelのサービス名 host_name: dummy-alb # ALBの名称 interpolated_value: 0.0 metrics: #Mackerelのメトリックの取得設定 - id: task_error_count # 取得したメトリックの識別子。SLOの設定で参照します [required] name: component.errors.tasks # サービスメトリックス名。 各マイクロサービスのエラー数が component.errors.<マイクロサービス名> で投稿されてる想定。[required] type: service # サービスメトリックの場合はtypeをservice設定 [required] service_name: prod # 取得元のサービス名 [required] interpolated_value: 0.0 # データポイントに欠損値があった場合の補間値 - id: alb_2xx_count name: custom.alb.httpcode_count.alb_2xx <<: *alb-metric-base # 共通設定は yamlのアンカーを使ってまとめることも - id: alb_3xx_count name: custom.alb.httpcode_count.alb_3xx <<: *alb-metric-base - id: alb_4xx_count name: custom.alb.httpcode_count.alb_4xx <<: *alb-metric-base - id: alb_5xx_count name: custom.alb.httpcode_count.alb_5xx <<: *alb-metric-base - id: alb_p99_response_time name: custom.alb.response.time_p99 <<: *alb-metric-base - id: alb_p90_response_time name: custom.alb.response.time_p90 <<: *alb-metric-base x-definition-base: &definition-base # SLOに関する共通設定(yamlアンカー) service_name: prod # 計算したエラーバジェット等をサービスメトリックスとして投稿する先 [required] time_frame: 28d # エラーバジェットを計算する際のローリングウィンドウのサイズ [required] calculate_interval: 1h # ローリングウィンドウの移動幅 [required] error_budget_size: 0.001 # エラーバジェットの大きさ。ローリングウィンドウに対しての比率を設定 [required] # この場合 28d × 0.001 = 40m を指定したことに definitions: # SLOの設定 - id: availability # 可用性SLOの定義 objectives: # SLOに関する目標に設定。簡単な足し引きと比較が可能です。 - expr: (alb_2xx_count + alb_3xx_count + alb_4xx_count) / (alb_2xx_count + alb_3xx_count + alb_4xx_count + alb_5xx_count) >= 0.97 <<: *definition-base - id: latency # レイテンシーSLOの定義 objectives: # 複数の目標を設定することも。設定した場合ANDで評価します - expr: alb_p99_response_time <= 5.0 - expr: alb_p90_response_time <= 1.0 <<: *definition-base - id: quality # 品質SLOの定義 objectives: - expr: task_error_count < 5.0
definitions
の中のobjectives
で式を使ってSLOを設定できるようになっています。
リクエスト成功率
はホストメトリックとしては存在しませんが、簡単な式を使って計算できるようになってます。
SLOの判定とエラーバジェットの算出について
shimesaba
は一回の実行で、以下のサービスメトリックスをMackerelに投稿します。
shimesaba.failure_time.*
: SLO違反となった合計時間 (単位:分)shimesaba.uptime.*
: SLO違反ではない合計時間(単位:分)shimesaba.error_budget.*
: 現在のエラーバジェットの残り(単位:分)shimesaba.error_budget_percentage.*
: エラーバジェットの損耗率shimesaba.error_budget_consumption.*
: 新規のエラーバジェット消費量(単位:分)shimesaba.error_budget_consumption_percentage.*
: 新規のエラーバジェット消費量(%)
先程の例で3種類のSLOを設定しましたが、この場合shimesaba.failure_time.*
として shimesaba.failure_time.availability
、shimesaba.failure_time.latency
、shimesaba.failure_time.quality
と3個のサービスメトリックが投稿されることになります。
つまり、shimesaba
はSLOの数×6個のサービスメトリックを一回の実行で投稿することになります。
SLOの評価については、Mackerelのメトリックスは基本的には1分単位ですので、shimesaba
のデフォルトの設定では1分単位で評価します。
より具体的には、先程の例のavailability
について、仮に
- 2021-11-01の1時から2時までの間に4分間SLOが満たせなかった。
- 2021-11-01の2時から3時までの間に1分間SLOが満たせなかった。
- 2021-11-29の1時から2時までの間に3分間SLOが満たせなかった。
- それ以外の時間はSLOを満たせた。
という状況になり、error_budget_size
が0.1%の設定の場合は次の図のようになります。
shimesaba.failure_time.availability
はSLOを満たしていなかった時間の合計 と shimesaba.uptime.availability
はSLOを満たしていた時間の合計となっています。
ですので、この2つの合計値は算出時点でのtime_frame
の値と同じ値になります。
ローリングウィンドウのサイズが28日(=40320分)でエラーバジェットの大きさが0.1%なのでエラーバジェットは40分となっております。
shimesaba.error_budget.availability
は エラーバジェットからshimesaba.failure_time.availability
を引いた値が投稿されます。
shimesaba.error_budget_percentage.availability
はshimesaba.failure_time.availability
をエラーバジェットで割った値が投稿されます。
ローリングウィンドウはcalculate_interval
ごとにずらして計算されます。
1つ前のローリングウィンドウとかぶっていない新しい区間でのエラーバジェット消費量をshimesaba.error_budget_consumption.availability
とshimesaba.error_budget_consumption_percentage.availability
として投稿します。
おわりに
shimesaba
は継続的なSLO/エラーバジェットの運用をするために生まれました。
試験運用を始めてまだ1ヶ月ほどなので、これから先にさらなる機能追加や修正が必要になってくるとは思います。
いつか皆様のSLI/SLO運用の助けに慣れれば幸いです。