MackerelでSLOとエラーバジェットを運用するためのツール shimesaba

この記事はMackerel Advent Calendar 2021の7日目です。
こんにちは、SREチーム所属の@mashiikeです。

皆様はSLOエラーバジェットという言葉を聞いたことはありますか?
サービスの信頼性を保証することを目標するSRE(Site Reliability Engineer/Site Reliability Engineering)の領域に携わってる方なら聞いたことがあると思います。
今回は、SLOエラーバジェットに関して、Mackerelを用いてサービス/サーバー監視をしている際に便利なツールとして shimesaba というものを作った話をします。

github.com

はじめに

本題に入る前に、SLISLO, エラーバジェットという言葉について触れておきます。 これらの言葉は、ざっくりと説明すると以下のようになります。

  • SLI(Service Level Indicator): サービスレベル指標、サービスの品質を測るための指標です。Mackerelをお使いの皆様にとっては、ホストメトリックやサービスメトリック、それらを組み合わせた式グラフ等を思い浮かべるとわかりやすいと思います。
  • SLO(Service Level Objective): SLIに対する目標値。
  • エラーバジェット: 許容される信頼性の損失を表したもの。SLOを満たせなかった度合い。

より細かい説明や言葉の正確な定義等は、『SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム』(通称SRE本)を参照していただければ幸いです。

ところで、Mackerelをお使いのSREの方は、以下のようなことを思ったことはありませんか?

  • SLOの具体的な適用方法がわからない。
  • エラーバジェットの算出方法や、運用方法がよくわからない。
  • 今どれくらいエラーバジェットが残ってるのか、素早く把握したい!

そこで作ったのが shimesaba というツールです。

SLO/エラーバジェット を算出するツール shimesaba

github.com

このツールは、MackerelをSLIを計測するサービスとして捉え、Mackerelに保存されている各種メトリックからSLOやエラーバジェットを計算してMackerelのサービスメトリックとして投稿するツールです。
shimesabaの名前は 鯖(Mackerel)の運用をもっと締める の発想から 〆鯖という料理の名前をつけました。

先に、試験運用中のサービスにおけるSLI/SLOダッシュボードをお見せします。

f:id:ikeda-masashi:20211206182758p:plain
試験運用中のサービスでの事例

使い方

shimesabaはCLIのツールまたはAWSのLambda関数のbootstrapとして動くように作られており、cronやEventBridge等で定期的に実行する想定となっています。

CLIとして動かす場合は以下のように実行します。

$ shimesaba --config config.yaml --mackerel-apikey $MACKEREL_APIKEY run 

試験運用の現場ではLambda関数として動かしております。そちらの動かし方は READMEをご参照いただければ幸いです。

肝心の config.yaml の中身ですが、色々なケースに対応できるように細かくなってしまいました。
詳細はREADME を参照していただくとして、今回は例として以下のケースでの設定を紹介します。

SLOやエラーバジェットを適用する先のサービスの想定としては以下のようなものとなっています。

  • リクエスト駆動型のサービス
  • userstasksという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.availabilityshimesaba.failure_time.latencyshimesaba.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%の設定の場合は次の図のようになります。

f:id:ikeda-masashi:20211206182725p:plain

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.availabilityshimesaba.failure_time.availabilityをエラーバジェットで割った値が投稿されます。

ローリングウィンドウはcalculate_intervalごとにずらして計算されます。
1つ前のローリングウィンドウとかぶっていない新しい区間でのエラーバジェット消費量をshimesaba.error_budget_consumption.availabilityshimesaba.error_budget_consumption_percentage.availabilityとして投稿します。

おわりに

shimesabaは継続的なSLO/エラーバジェットの運用をするために生まれました。
試験運用を始めてまだ1ヶ月ほどなので、これから先にさらなる機能追加や修正が必要になってくるとは思います。
いつか皆様のSLI/SLO運用の助けに慣れれば幸いです。

カヤックではサービスの信頼性を追求するエンジニアも募集しています
中途採用も募集しています