サービスの一般公開前からSLI/SLOと向き合う

Mackerel チームで SRE を担当している id:taxintt と申します。

はてなの SRE が毎月交代でブログ記事を書く Hatena Developer Blog の SRE 連載、3月分は私が担当します。2月の記事は id:masayosu さんの はてなにおけるEKSの運用と自動化 (2024年版) でした。

私が所属する Mackerel 開発チームでは、SaaS 型サーバー監視サービスである Mackerel を開発しています。

Mackerel は、テレメトリデータの計装・収集の標準化を目的としたプロジェクトである OpenTelemetry 対応のための開発を進めています。この記事では、OpenTelemetry のメトリックを扱うサブシステムの開発における SLI/SLO の決定・運用についてお話しします。

mackerel.io

OpenTelemetry のメトリック対応機能は記事執筆時点で public beta での機能提供となっています。Mackerel のユーザーであれば手続き不要で利用可能ですので、ぜひ皆様のアカウントでお試しいただければと思います。

mackerel.io

OpenTelemetry のメトリックを扱うサブシステムについて

最初に、本記事で扱うサブシステムの概要についてお話しします。

先述の通り、Mackerel では OpenTelemetry のメトリック(本記事内ではラベル付きメトリックと呼称します) の投稿・参照ができるようになりました。

メトリックの投稿・参照を行うためには、メトリックを保存するためのデータベースが必要となります。Mackerel で従来扱っているメトリックは時系列 DB に保存されています。一方で、ラベル付きメトリックは OpenTelemetry の仕様に準拠しており、そのままのデータ形式では時系列 DB に保存することはできません。そのため、ラベル付きメトリックに関して、時系列 DB に保存できるデータ形式への変換・保存と取得を行うサブシステムを開発しました。

ラベル付きメトリックのデータ変換・保存・取得を行うサブシステムは writer, reader と呼ばれるコンポーネントで構成されています。writer はラベル付きメトリック投稿用のエンドポイントを提供し、時系列 DB に保存できるデータ形式に変換して保存します。保存したラベル付きメトリックの取得にはオープンソースの監視ツールである Prometheus にて使われている PromQL というクエリ言語を用います。reader は PromQL を parse して時系列 DB からメトリックを取得します。

本記事では、ラベル付きメトリックのデータ変換・保存・取得を行うサブシステムにおける SLI/SLO の策定・運用についてお話しします。

ブログ内容の前提知識となる SLI/SLO に関しては、弊チームの SRE テックリードの登壇資料や、より詳しく知りたい場合は、下記の資料をご参照いただければと思います。

speakerdeck.com

SLI/SLOの策定

SLI/SLO の策定にあたっては、標準的な SLI/SLO の策定から始めました。既存のメトリックの投稿・参照機能に対してはすでに SLI/SLO が策定されており、メトリックの種別の違いはありますが今回取り上げているサブシステムにおいても同様に適用できるためです。

ラベル付きメトリックのデータ変換・保存・取得を行うサブシステムの SLI として下記を採用しました。

  • Availability : 正常に処理されたリクエストの割合
  • Latency: 十分に高速な時間で処理されたリクエストの割合
  • Freshness: 投稿されたメトリックが取得できるようになるまでの時間
  • Correctness: 正しく保存されたメトリックの割合

Availability, Latency はご存知の方が多いかと思いますが、Correctness や Freshness はあまり馴染みがないかもしれません。

Freshness は SLI の一種で「ある特定の時間をしきい値にして、それより最近に更新されたデータの比率」です。メトリックの投稿 ~ 取得までを一連のデータ処理として捉え、特定の出力に対する処理 (送信したメトリックの取得) の実行が正常に完了するまでの経過時間を計測しています。

Correctness は「正しい出力を生成した有効なデータの割合」です。メトリック取得のリクエストについて Availability を計測していても、メトリックの欠けなどが発生してしまうと監視サービスとしての信頼性に関わります。そのため、投稿したメトリック自体が期待する状態で変換・保存されているどうかを入力 (投稿したメトリック) と出力 (時系列DBから取得したメトリック) を比較して Correctness で計測するようにしています。

Availability, Latency に関しては writer, reader の両方で SLO を定義し、Freshness, Correctness に関してはサブシステムの粒度の SLO とみなして定義しました。

SLI/SLOの運用

このようにして、SLI/SLO を決定し、public beta 以前に一部ユーザに機能提供する private beta の期間より前から SLI/SLO の運用を開始しました。

サービスの一般公開前からの SLO の運用ということで、そんな段階から SLO の運用ができるの? リクエスト数が少ないと Availability などの SLO はほとんど変動しないのでは? と疑問に思われるかもしれません。

Mackerel ではドッグフーディングの文化がベースにあり、Mackerel の監視は Mackerel で行っています。監視サービスの性質上、メトリック投稿は継続的に行われるかつメトリック取得に関してもドッグフーディングの過程で一定のリクエストは発生するため、SLO の運用がサービスの一般公開前から可能になっています。

製品 - 信頼性 - ドッグフーディングから生まれた信頼性 - Mackerel(マカレル): 新世代のサーバー管理・監視サービス

こういった形で、ドッグフーディングや private beta のフェーズを経ていく中で、SLI/SLO の改善を行ってきました。今回は SLO の改善事例を2つご紹介します。

ALBが返す HTTP status code 460 の扱い

ある時、writer において時系列 DB へのメトリックの保存に失敗する事象を確認しました。開発チームでこの事象について調査したところ、メトリックの保存に失敗した時刻に ALB が status code として460を返しており、保存に失敗したリクエストの傾向として response time が5秒近くかかっていることがわかりました。

さらなる調査の結果、OpenTelemetry Collector の仕様としてメトリックの送信処理はタイムアウトがデフォルトで5秒に設定されており、5秒を超過すると Collector (writer から見た時のクライアント) から connection を切断されることが原因だとわかりました。

OpenTelemetry Collectorとは、テレメトリデータの受信、加工、送信を行う proxy です。

実際に確認すると、Backend へのメトリック送信処理のデフォルトのタイムアウト値が5秒に設定されています。タイムアウトの処理は個別の exporter ではなく、exporter 用の helper 関数を提供している exporterhelper package で実装されています。

opentelemetry-collector/exporter/exporterhelper/README.md at main · open-telemetry/opentelemetry-collector

  • timeout (default = 5s): Time to wait per individual attempt to send data to a backend

ALB が生成する HTTP status code に関する説明を確認すると、下記のように記載されています。

Application Load Balancer のトラブルシューティング - Elastic Load Balancing

HTTP 460 ロードバランサーはクライアントからリクエストを受信したが、クライアントはアイドルタイムアウトが経過する前に、ロードバランサーとの接続を閉じました。

この事象自体はメトリックの保存処理の latency 改善によって解消されました。 一方で、writer の Availability の SLI に関しては、非正常なリクエストとして HTTP status code が500系のリクエストに加えて460のリクエストも計上するように修正しました。これは「ユーザーが正常にメトリックが投稿されることを期待する」ことの中に「collector に設定されたタイムアウト時間内にメトリック投稿が完了すると期待する」ことが内包されていると考えると、ALB が status code として460を返すリクエストはメトリック投稿に失敗した非正常なリクエストと見做せるからです。

機械的に HTTP status code が500系のリクエストが非正常なリクエストであると定義するのではなく、ユーザーにとっての期待を踏まえた正常/非正常は何なのかを明確にする必要があると実感した事例でした。

latency アラートに対するアクションの紐付け

reader の latency SLO に関しても、改善を行いました。

当初は、ALB の response time を利用して、latency の SLO を閾値にしたアラートを設定していました。その一方で、開発期間中は、メトリック取得処理のボトルネックを確認するための検証により latency が一時的に悪化することが多く、latency のアラートがたびたび発報していました。しかし、そのアラートに対してアクションが紐づいていなかったので、発報したアラートは放置され気味でした。 これを機に、latency SLO に基づくアラートについて考え直すことにしました。

今回のケースでは、アラート発報時の latency は SLO を満たしていなかったものの、SLO のタイムウィンドウ内でエラーバジェットを消費しきるかが不明でした。そのため、対応が必要か曖昧な場合がほとんどで、具体的なアクションに紐づかないアラートと化していました。

この状況を踏まえて、「ALB の response time が xx ミリ秒以下となるレスポンスの割合が xx 日間のローリングウィンドウで xx %」という latency の SLO の表現に合わせる形で、アラート発報の条件をエラーバジェットの消費をベースにしたものに変更することにしました。これにより、アラートの発報 = SLO 違反 (もしくは SLO 違反になる) という形になり、調査や latency の改善といったアクションに紐づくことになります。

サービスの一般公開前においてSLI/SLOと向き合うということ

このような改善を経て、改めて実感したことがあります。それは、サービスの一般公開前こそSLI/SLOの原則に向き合うべきということです。 前提として、サービスの一般公開前から「完璧な SLI/SLO を策定し切る」ことは無理だと考えています。ユーザー数や事業の方向性など機能開発に影響を与える変数は多く、それにより SLI/SLO の前提が変わる可能性があるからです。

そのため、現在の状態をベースにした SLI/SLO を策定し、サービスの信頼性に向き合う機会を早い段階から設けるべきと筆者は考えています。SLO と共に Revisit date が設定されていれば、策定した SLO と現状のサービスの信頼性に定期的に向き合い、SLI/SLO の修正に繋げることができます。Revisit Date とは、設定した SLI/SLO を見直す予定日のことを指しており、Revisit Date を設けることで SLI/SLO が現状に即したものになるように、定期的な見直しを促すための仕組みとなっています。今回取り上げたラベル付きメトリックのデータ変換・保存・取得を行うサブシステムでは、Revisit Date を他のシステムと比較しても短いスパンである1ヶ月ごとに設定して、SLO の見直しを行なっています。

SLI/SLO の原則に向き合って、サービスの信頼性を考え学びを得ることの重要性は事例からもお分かりいただけるかと思います。ALB が status code として460を返すリクエストに関しても、「ユーザー視点でのサービスの信頼性」を考察し、計測した結果 SLI の定義を修正することになりました。

SLO を違反しないようにすることが SLO 策定・運用のゴールではなく、SLI/SLO がユーザー視点でのサービスの信頼性を表現できるように継続的に改善していくことが重要です。Mackerel 開発チームとしての SLO 改善も道半ばで、現在は CUJ (Critical User Journey) に基づいて全ての SLI/SLO の再整備を行っています。

speakerdeck.com

また、今回のような学びを得るにあたって、ドッグフーディングは重要な要素の1つとなっています。サービスの信頼性の考察ひいては SLO の改善につながる要素としてユーザー視点がありますが、これはドッグフーディングを経て獲得していったものだと実感しています。ただし、ドッグフーディングの中で考察した「ユーザーにとっての期待/満足度」が他のユーザーにも適用できるかどうかは考える必要があります。

最後に

サービスの一般公開前のフェーズにおいて、SLI/SLO とどのように向き合ったかという事例についてお話ししました。 これまでの SLO 運用における学びを残すためにも今回ブログを書かせていただきましたが、参考になれば幸いです。