hacomono TECH BLOG

フィットネスクラブやスクールなどの顧客管理・予約・決済を行う、業界特化型SaaS「hacomono」を提供する会社のテックブログです!

hacomonoにSRE文化を取り入れる為にSLOダッシュボードを創設した話

はじめに

初めまして。SRE部のiwachan(@Diwamoto_)です。
hacomonoには2024年の1月に入社しました。もう半年過ぎてて時間が早く感じます。
最近は5ヶ月になった娘を絶賛溺愛しています。
徒歩0分で娘の顔を見れる点はリモートワークの利点の一つですね。
また、Xで溢れている地面師ネタを理解したく、やっとNetflixに加入して「地面師たち」を全部見ました。ひとまず今年1番面白かったです。

TL;DR

  • SLO文化を醸成するためにダッシュボードを展開したよ
  • そのおかげで潜在的なバグを発見したよ
  • かなり安くできたよ
  • ここまで来るのにSLO本が本当に助けになったからみんなも読んでみてね


想定読者

  • SRE本を読んで、組織に導入したいと検討している方
  • SRE文化の醸成に興味がある方
  • SLOの実装に興味がある方


なぜやりたかったのか

hacomonoではマルチテナントの監視にDatadog、シングルテナントの監視にCloudWatchを利用しています。

hacomonoのマルチテナントに関する歴史は基盤本部のありさんが書いた記事を参考にしてください。

以前からの課題として、

  • ELBごとの監視はできていたが、APIごとの可用性 / レスポンスタイムの監視や、テナントごとの監視ができていなかった
  • hacomonoのサービスの信頼性を可視化できていなかった
  • 安定性の観点から、リリース速度がなかなか向上できなかった


があげられます。
これらを解消する為に、SLOを導入し、hacomonoの最初のSLOダッシュボードを作成することにしました。

どうやったのか

SLOを導入するプロダクトの決定


hacomonoは会員管理、予約、決済、スクール管理、物販の在庫管理までをオールインワンで担う、リアル店舗や施設の「次世代OS」となるDXプラットフォームで、プロダクトを実現するために複数のシステムが動いています。
これら全体に一気にSLOを導入することは難しく、まずは重要な機能から小さく始めたいと考え、hacomonoのうち重要な機能を洗い出し、決済関連で重要となるPOSシステムにSLOを導入するところから始めました。

POSプロダクトにおいて

  1. SLOが計測され開発者がダッシュボードで閲覧できること
  2. SLOの低下を検知できること
  3. それらをまとめたSLO Docsを作成し、それを元にSLOの運用が始められること

を目指しました。

CUJ / SLI / SLOの決定


SREの原則に則り、まずはCUJを決定しました。
POSの機能は大きく分けて「物販機能」と「ドロワーを操作する機能」があります。
それらを基にストーリーを考え、CUJを下記のように決定しました。



これらの機能はAPIで実現されています。APIの信頼性を知る為には主にAvailability(可用性/エラーレート)とLatency(レスポンスタイム)が使われるので、下記の通りSLIを決定しました。


このSLIの目標値として、CUJの機能となる各APIのSLOを決定しました。

SLOの具体的な値に関してはAthenaを用いて実績を先月の実績を取得し、サマリとして出力することでSLOを決定しています。
以下にAthenaクエリと、それで得られた結果を載せておきます。

-- slo_summarys.sql
-- 特定の月のSLO実績を抽出します
-- 出力するカラム:
-- http_method (ex. GET)
-- request_uri (ex. /api/auth)
-- total_count (ex. 1000)
-- bad_count (ex. 10) 可用性においてNGとするステータスコードの数 (400,401,403,404,413,429,500,502,503,504)
-- request_count_slo (ex. 99) 可用性SLO実測値
-- ↓ 以下は可用性SLOを決める時の参考になる数です。SLOをXX%とした場合にどれだけエラーが許容されるかを示しています
-- p50_availability_bad_count (ex. 500) 仮に可用性SLOを50%とした場合にSLO違反となるリクエスト数 → SLO50%にするということはこれだけエラーを許容しないといけない
-- p90_availability_bad_count (ex. 100) 仮に可用性SLOを90%とした場合にSLO違反となるリクエスト数
-- p95_availability_bad_count (ex. 50) 仮に可用性SLOを95%とした場合にSLO違反となるリクエスト数
-- p99_availability_bad_count (ex. 10) 仮に可用性SLOを99%とした場合にSLO違反となるリクエスト数
-- p999_availability_bad_count (ex. 1) 仮に可用性SLOを99.9%とした場合にSLO違反となるリクエスト数
-- p50_response_time (ex. 0.123) 50パーセンタイルのレスポンスタイム
-- p90_response_time (ex. 0.456) 90パーセンタイルのレスポンスタイム
-- p95_response_time (ex. 0.789) 95パーセンタイルのレスポンスタイム
-- p99_response_time (ex. 1.234) 99パーセンタイルのレスポンスタイム
-- p999_response_time (ex. 2.345) 99.9パーセンタイルのレスポンスタイム
-- ↓ 以下はレイテンシSLOを決める時の参考になる数です。SLOをXX%とした場合にどれだけエラーが許容されるかを示しています
-- p50_response_bad_count (ex. 1) p50_response_timeを違反してるリクエスト数
-- p90_response_bad_count (ex. 1) p90_response_timeを違反してるリクエスト数
-- p95_response_bad_count (ex. 1) p95_response_timeを違反してるリクエスト数
-- p99_response_bad_count (ex. 1) p99_response_timeを違反してるリクエスト数
-- p999_response_bad_count (ex. 1) p999_response_timeを違反してるリクエスト数
-- -------------------------------------------------------------------------------
-- parameter1 必須: date - SLO計測月 : ex. '2024/05/%' : partitionキーなので必ず設定して下さい、この月のSLO実績を算出します
WITH percentile_values AS (
  SELECT
    request_verb AS http_method,
    url_extract_path(request_url) AS request_uri,
    elb_status_code AS status_code,
    round(request_processing_time + target_processing_time + response_processing_time, 3) AS response_time
  FROM alb_logs
  WHERE date LIKE ?
  AND request_processing_time >= 0 AND target_processing_time >= 0 AND response_processing_time >= 0
  AND request_verb IN ('GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS')
  AND starts_with(url_extract_path(request_url), '/api')
)
SELECT
  http_method,
  CASE
      { ここに  WHEN request_uri LIKE '/api/users/%' THEN '/api/users/$user_id' のようにパスパラメータがあるAPIのグルーピングルールを記載します。 }
    ELSE request_uri
  END AS request_uri,
  count(*) AS total_count,
  count(CASE WHEN status_code IN (403,404,413,429,460,500,502,503,504) THEN 1 END) AS bad_count,
  round(100.0 * (count(*) - count(CASE WHEN status_code IN (403,404,413,429,460,500,502,503,504) THEN 1 END)) / count(*),3) as request_count_slo,
  count(*) - cast(round(count(*) * (50.0 / 100.0)) as int) as p50_availability_bad_count,
  count(*) - cast(round(count(*) * (90.0 / 100.0)) as int) as p90_availability_bad_count,
  count(*) - cast(round(count(*) * (95.0 / 100.0)) as int) as p95_availability_bad_count,
  count(*) - cast(round(count(*) * (99.0 / 100.0)) as int) as p99_availability_bad_count,
  count(*) - cast(round(count(*) * (999.0 / 1000.0)) as int) as p999_availability_bad_count,
  round(approx_percentile(response_time, 0.5), 2) AS p50_response_time,
  round(approx_percentile(response_time, 0.9), 2) AS p90_response_time,
  round(approx_percentile(response_time, 0.95), 2) AS p95_response_time,
  round(approx_percentile(response_time, 0.99), 2) AS p99_response_time,
  round(approx_percentile(response_time, 0.995), 2) AS p995_response_time,
  round(approx_percentile(response_time, 0.999), 2) AS p999_response_time
FROM percentile_values
GROUP BY
  http_method,
  CASE
    { 上で述べたパスグルーピンググループと同じものを追加します。}
    ELSE request_uri
  END
ORDER BY total_count;




SLO Docs


SLOに関する情報をSREだけが持っているのではなく、誰もがいつでも確認ができるよう、SLO Docsという形でNotionにまとめました。




SLOを収集する機能の作成


ここまででSLOが決定できたので、
具体的には、下記のようなシステムを作ってDatadog上でSLOダッシュボードを見れるようにしました。

  • posプロダクトはALB + fargateの部分です。
  • SLOの計測にはALBのログを利用しました。SLO監視対象になっているAPIのログを一括で引き出します。
  • 検索とDatadogへの送信にはLambdaを利用しました。S3に保存されているALBのログを5分に一回Athenaで検索し、集計した上でDatadogのAPIに送信します。



ダッシュボード


SLOを簡単に確認するために、二つのダッシュボードを作成しました。

■ 各APIのSLO概要ダッシュボード
  • 各APIのSLOが現在どの程度なのか一目でわかるようにしました。
  • これにより、直感的にどのAPIのパフォーマンスが悪くなっているか、信頼性が保たれているかわかりやすくできました。



■ テナントごとの詳細SLOダッシュボード
  • 上のダッシュボードで違反しているものがわかった時に、次のドリルダウンとして、どのテナントのどのAPIのSLOが悪くなっているかを確認するものを作りました。
  • これにより、デプロイによってAPIのパフォーマンスがマルチテナント全体で悪くなったのか、特定のテナントにおいてイベントが開催されたりして一時的にレイテンシが悪くなったのか等がわかるようになりました。




バーンレートの低下アラート


特定のSLOのバーンレートが上昇した時にアラートを鳴らすようにしました。
これにより、リリース後に「レスポンスが悪化した」「エラーが返ってくるようになった」ことをすぐに検知できるようになりました。

↑ バーンレートの上昇を検知し、対処を実施する図


それでなにが得られたのか

初めてhacomonoの信頼性を可視化することができた


これまで、なんとなく「まあ動いているだろう」「アラートがなっていないから動いているだろう」という希望的観測で信頼性を測っていたものをSLOによって可視化することで、「このAPIは正常に動作している」ことが数字で担保できるようになりました。

潜在的なバグが発見できた


これまでPOSシステムはSentryによるエラー監視はしていましたが、アプリケーション層でエラーにならないものに関しては検知できていませんでした。(ex. API層では正常に処理していたが、タイムアウトの設定値がBFFの方が早く、BFFではエラーになっていた)
今回SLOを用いたAPIごとの信頼性モニタリングを導入したところ、想定とは違う動作をしていたAPIがいくつか見つかり、SLOの数値が悪くなっていました。
これらをプロダクトチームにフィードバックすることで、サービス全体の信頼性を向上させることに繋がりました。

まとめ

大きな一歩を踏み出せた


前述の通りhacomono全体にいきなりSLO運用を入れるにはかなりハードルが高く、これまでSLOによる運用ができていなかったのもこのような背景があります。
そこに一石を投じるべく、なるべく小さく安く進めるためにAthenaやLambdaを利用して、月に$5以下ととても安く始められました。
最終的なゴールはプロダクトメンバー全員がSLOを意識しながら開発でき、hacomono全体がSLOにより高い信頼性を担保できていることを目指していきます。
ありがとうございました。SLOやっていき!


SRE本、SLO本にかなり助けられた


言わずと知れたSREを始める上での本です。
SREの原則を学ぶ為にはこの本以上に書かれている本はないでしょう。
分厚くてなかなか見れないという方には、TopotalさんがやっているSpotifyのPodCast「もう一度読むSRE」があるのでそちらをお勧めします。僕もそれで学びました。

SREの原則のうち、SLOについての詳細な内容が書かれています。
SRE本の第二部[SREの原則]を読んだ後にこの本を読むのがSLOハンズオンとしては最速だと思います。
この本の中には

  • SLOの実践の仕方
  • どうやってSLOを社内に広めていくか
  • SLOを広めるにあたってどういう障害があって、それをどう克服していくか

等実際にSLOをやっていく上で必要な内容が詳細に記載されていました。お勧めです。

参考にさせていただいた記事



株式会社hacomonoでは一緒に働く仲間を募集しています。
エンジニア採用サイトや採用ウィッシュリストもぜひご覧ください!