Embedded SRE としてサービスに SRE をやってみた

技術本部 サービスリライアビリティグループ(SRG)の長谷川(@rarirureluis)です。
#SRG(Service Reliability Group)は、主に弊社メディアサービスのインフラ周りを横断的にサポートしており、既存サービスの改善や新規立ち上げ、OSS貢献などを行っているグループです。
本記事は、横軸組織である SRG がどのように特定のチームへ SRE の文化を形成し、それを継続しているかを簡単にご紹介します。これらを実現するためのビジネスサイドへの説明をした際の資料、文化を形成するための OSS なども紹介しますので SRE の導入の手助けになれば幸です。
 

この記事の目標


横軸組織の SRG がEmbedded SRE (Site Reliability Enginner)としてドットマネーというサービスに参加し、SRE 文化の構築やサービス品質の向上を行うまでにやってきたことをご紹介します。
この記事ではあくまで個人的なやり方であり、多くのパターンで当てはまるやり方ではないかもしれません。そのため、こういうやり方もアリだなと思っていただけると幸いです。
 

⚠ SRE を導入する前に


Embedded SRE として参加したら、いきなり SRE の話をするのではなくサービス側の部署から自身への信頼性を獲得するために色々なことを行いました。
これは私自身 SRE が初めてだったこと、そしてサービス側が SRE のメリットを享受する前に疲れてしまう可能性があるため、先に自身の信頼性を獲得しておくことで、これをいくらか軽減するのが目的です。
私がまずやってきたのは Toil の削減と、コミュニケーションです。

Toil などを探す

サービスのインフラ構成を把握したり、デプロイフローなどを調査しているうちに Toil が見つかりました。
例えば、
  • 全くメンテナンスされていない EC2 と IaC
  • 過剰なリソース割当によるコストの無駄
  • 長時間かかるデプロイフロー
  • インシデント発生時の対応フローがない
などなど
この記事の目的は SRE なので詳細は省きますが、EC2 と Ansible、過剰なリソース割当によるコストの無駄、デプロイフローの改善は全て、EC2 から Fargate へ移行を行うことでサービス側へ数値として明確な成果を示すことができました。

コミュニケーション

ドットマネーでは毎日サーバーサイドの夕会や、週に1回の全体会議などがあるのでそこへ顔を出したり、IaC に対する PR への積極的なレビュー、(当たり前ですが)インシデントの対応など

インシデント対応フローの確立

サービス品質を担保するためにもインシデント対応フローを確立し MTTR(Mean Time To Repair: 平均修復時間)を短縮することも SRE の1つです。
そしてその文化を継続させなければなりません。
インシデント対応フローのドキュメントは古くそれに沿ったフローでは動いていなかったためビジネスフローも含めゼロからを確立しました。
ここでは、オンコールの導入、インシデント対応フローの確立、Datadog Incident を活用した MTTR の計測とポストモーテムの運用に関する物事を進めてきました。
 

1. ビジネスサイドへサービス品質の話し合いを行う


サービス品質を維持するためにビジネスサイドの協力なしに実現することは不可能です。
特にエラーバジェットが枯渇したあとの動き方はプロダクトマネージャーの許可なしに行うことは難しいでしょう。

サービス側へ説明した際の資料

 
サービス側のエンジニア・ビジネスを含めた全体的なミーティングで、SRE について説明を行った際の資料です。
ここで全てを理解してもらうのは難しいと思います。多くの人が SRE について資料を読んだときに数十分で理解できた人はなかなかいないと思います。
このミーティングでは SRE の概念(SRE を導入することでサービス品質を可視化し、事業判断の材料ができる)を伝えられることをゴールとしました。
 

2. クリティカルユーザージャーニー(CUJ) を決める


大まかに SRE を知ってもらったあと、ビジネスインパクトがもっとも大きい部分から CUJ を決定します。
なお、いきなりたくさんの CUJ を定義せずにまずは1つの CUJ を決めました。
ドットマネーではポイント交換サイトであるためもっともビジネス影響が大きいのは
ユーザーがサイトに訪れる → 商品ページ一覧を開ける → 交換ができる
という一連の流れを CUJ と定義しました。
 

3. CUJ から SLI を決める


CUJ を決めたあとはそこから SLO の指標となる SLI を探します。
ここも中々難しいところです。

SLI はどこのメトリクスを参照するか

ドットマネーでは Realtime User Monitoring(RUM)を導入していないためフロントからのメトリクスは利用せず、ユーザーからもっとも近いロードバランサーのログを利用しています。
逆にフロントからのメトリクスを利用するとユーザーのネットワーク環境などに左右され、外れ値などを除外していく作業が大変、かつ RUM はどこも💰がかかるので最初に SRE を導入するのであればユーザーからもっとも近いロードバランサーのメトリクス、ログを利用するのが簡単だと思います。
理想を語ればフロントで正しいデータを使えればロードバランサーなどのメトリクスなどよりも信憑性が高いものになります。これはフロント側の実装によるサービス品質の変化にも追従できるためです。
Google SRE の Cindy Quach さんが公開している記事、Learn how to set SLOs でも、フロントでの計測はせず LB(Istio) での例をあげています。
 
そして Google Cloud アーキテクチャセンターの記事ではどこから SLI を計測するかについて詳細に記載されています。
SLO の導入 | Cloud アーキテクチャ センター | Google Cloud
このドキュメントでは、さまざまな種類の一般的なサービス ワークロードに役立つサービスレベル目標(SLO)をいくつか定義します。このドキュメントは 2 部構成の後半です。パート 1 SLO の定義 では、SLO について紹介し、サービスレベル指標(SLI)から SLO がどのように導出されるか、そしてどのような SLO が適切かについて説明しています。 State of DevOps で、ソフトウェア デリバリーのパフォーマンスを向上させると認められた機能が報告されています。これら 2 つのドキュメントでは、次の機能について説明します。 ドメイン にかかわらず、多くのサービスが共通の機能を共有し、一般的な SLO を使用できます。以下では、サービスタイプ別に一般的な SLO に関して考察しており、各 SLO に適用される SLI について詳しく説明します。 リクエスト主導型サービスは、クライアント(別のサービスまたはユーザー)からリクエストを受け取り、なんらかの計算を行い、ネットワーク リクエストをバックエンドに送信し、続いてクライアントにレスポンスを返します。リクエスト主導型サービスは、多くの場合、可用性 SLI とレイテンシ SLI によって測定されます。 SLI としての可用性 可用性の SLI は、サービスが稼働しているかどうかを示します。可用性の SLI は次のように定義されます。 正常に処理された有効なリクエストの割合。 最初に、「有効」の定義を決める必要があります。基本的な定義としては、「長さがゼロでないこと」や「クライアント サーバー プロトコルに準拠していること」などが考えられますが、「有効」の定義を決定するのはサービス オーナーです。有効性を評価する一般的な方法は、HTTP(または RPC)レスポンス コードを使用することです。たとえば、通常、HTTP 500 エラーは SLO にカウントされるサーバーエラーと判断され、 400 エラーはクライアント エラーで SLO にカウントされません。 測定対象を決めたら、システムから返されたすべてのレスポンス コードを調べ、アプリケーションがそれらのコードを適切かつ一貫して使用していることを確認します。SLO のエラーコードを使用する場合は、コードがサービスに対するユーザー エクスペリエンスの正確な指標であるかどうかを確認することが重要です。たとえば、ユーザーが在庫切れの商品を注文しようとした場合、サイトは中断してエラー メッセージを返しますか、それとも類似商品を提案しますか。SLO で使用するには、エラーコードをユーザーの期待に関連付ける必要があります。 開発者がエラーの取り扱いを誤るということも考えられます。ユーザーが一時的に在庫切れの商品をリクエストした場合に、開発者が誤ってエラーを返すようプログラムしている場合があります。しかし、実際にはシステムは正しく機能しており、エラーは発生していません。コードはユーザーが必要なアイテムを購入できなくても、成功として返す必要があります。もちろん、このサービスのオーナーは商品が在庫切れであることを把握する必要があります。ただし、販売できないからといって、お客様にとってエラーが発生したわけではなく、SLO にカウントされるべきではありません。ただし、サービスがデータベースに接続できないためアイテムの在庫があるか判断できない場合は、エラーとなり、エラー バジェットにカウントされます。 サービスがより複雑である場合も考えられます。たとえば、サービスが非同期リクエストを処理している場合や、実行時間が長いプロセスをユーザーに提供している場合が該当します。このような場合は、別の方法で可用性を公開できます。ただし、やはり可用性は成功した有効なリクエストの割合として表すことをおすすめします。可用性は、お客様のワークロードがリクエストどおりに実行される時間(分)として定義できます(このアプローチは、可用性を測定するための「良好な時間」の方法と呼ばれることもあります)。仮想マシンの可用性は、VM に SSH でアクセスできる、VM の最初のリクエスト後の分数の割合で測定できます。 SLI としてのレイテンシ レイテンシ(速度ともいいます)の SLI は、サービスの速度が十分かどうかを示します。レイテンシの SLI は可用性と同様に定義されます。 しきい値よりも速く処理された有効なリクエストの割合。 レイテンシを測定するには、リクエスト タイプごとのタイマーの開始と停止までの時間差を計算します。重要なことは、ユーザーによってレイテンシが認識されるかどうかです。よくある間違いは、レイテンシを正確に測定しすぎることです。実際には、更新に
この記事でもクライアント側での計測では変動が大きい要素が多くレスポンスに関するトリガーには適していないと記載されています。
いろんな意味で労力さえあればフロント(クライアント)での計測はもちろん可能ですが今回は、はじめて SRE をやるので手軽なユーザーからもっとも近いロードバランサーで計測するようにしました。
 

4. SLO クエリを書いていく


今回のCUJ(ユーザーがサイトに訪れる → 商品ページ一覧を開ける → 交換ができる)はすべて「3秒以内に正常にレスポンスが返せる」としています。
💡
なぜ3秒? SLO を決める前のレスポンスタイムを調査し、現状達成できそうないい感じの値が3秒でした。 弊社の別サービスでは実際に API レスポンスにわざと遅延を設け、どれぐらいでユーザー体験が損なわれるかを体感で設定していたりします。
 
ドットマネーで現在利用している「ユーザーがトップページに訪れる」際の Datadog クエリを例にあげます。
 
ドットマネーでは頻繁に DoS 攻撃を受けるため、DoS に関するリクエストや、怪しいリクエストを除外しています。
怪しいユーザーエージェントなどは日頃変わっていくので実際はサービス品質に影響がないのに、SLO の値だけどんどん悪くなっていきます。そのため、定期的に見直しなどが必要になります。
💡
正常とはステータスコードとして の部分です。 4xx は Total Event に含まないようにします。 クエリの部分でミスると SLO に大きな影響を与えるので期待している SLI と相違がないことを複数人で確認できるといいかもしれません。 私はクエリを書いていて数日後にミスしていたことがありました…。
 

5. Target SLO/Window とエラーバジェット


前項のクエリで実際に現在の SLO を算出してみると 99.5% 程度あることが分かりました。
そのため一旦 Target SLO は全て 99.5% に設定してみました。
💡
Target SLO は柔軟に下げることも、上げることもできます。 最初適当な値を設定し、定例などで見直すことが大事です。
 
このとき、エラーバジェットが消費されていない状態はあまり良くないことに注意してください。
ドットマネーで利用している SLO モニター
ドットマネーで利用している SLO モニター
上記の画像を見ると「交換完了」の SLO モニターのエラーバジェットが全く消費されていないことに気づきます。
一見、エラーバジェットが消費されていないことが称賛されるかもしれませんが言い換えると 「デプロイ回数が少ないのか」「技術的挑戦を行ってないのか」と見方を変えることできます。
エラーバジェットは Target SLO に対して割り当てられた「技術的挑戦」への予算です。
エラーバジェットが余っている状態は Target を厳しくするなどして Target Window に対してちょうど 0% になるようにしましょう。またはデプロイ回数を増やして技術的挑戦を行っていきましょう。

Target Window について

Target Window は定例の頻度や、開発サイクルによって決定します。
ドットマネー自体のデプロイ周期は1週間に約1回ですが、ドットマネーに Embeded SRE として参加しているのは私一人のためリソース的に Target Window は30日が良いと判断しました。
例えばサービスのデプロイが毎週水曜日であれば、SLO に関する定例は木曜日に設定し Target Window を1週間にすることで機能リリースによる SLO の変化やエラーバジェットについて話し合いをすることができます。
次項で説明するエラーバジェットがなくなった際の動き方を考えると Target Window を1週間に設定するのは結構厳しいものがあるんじゃないかなと思ってます。
 

6. エラーバジェットがなくなったら


ビジネスサイドの同意のもと、ドットマネーではエラーバジェットがなくなった際の動き方を
「本番障害への対応、信頼性回復のための改修、外部会社が関係する機能のリリースを除く場合、エラーバジェットが枯渇した際には機能のリリースを禁止する」
としました。
ドットマネーは外部会社との兼ね合い上、どうしてもリリースしないといけない局面があるためそれを例外としています。
また、機能のリリースを禁止する場合は信頼性回復のための改修ができるリソースがある場合にも例外としています。
こうすることで機能リリースは遅くなりますが、できなくなることはなくなります。
実際、エラーバジェットが枯渇時したときに機能リリース間近でしたが信頼性回復のためにドットマネーのエンジニア1人のリソースを確保していただき、私を含め2人で信頼性回復のための改修に動きつつ、新機能のリリースを並行して行えたため、このエラーバジェット枯渇時の動き方は中々良かったんではないかと思っています。
 

7. SRE を浸透させる、定例など


SRE は終わりのない文化です。
SRE を導入したら定例などで振り返りや、サービス内に SRE を浸透していく必要があります。
定例ではエラーバジェットの確認だけでなく、SLI が正しいか、SLO は緩すぎないか、キツすぎないか、今までやってきたことの振り返りを行います。
ここでいう定例というのは私が所属している SRG で行うのではなく、Embeded SRE とドットマネー側のエンジニアとビジネスサイドを含めたで定例を行います。
ドットマネー側の人間を巻き込むことで SRE を浸透させることが目的です。

サービス全体に SRE を浸透させる


どうやったらサービス全体に SRE を浸透できるかを考えていると、定例などはもちろんそうですが CS 対応をしている方、フロント、ビジネスサイドには中々難しいんでないかと思いました。
そこでサービス側の方、全員が参加する random チャンネルへ週に1回、SLOサマリーを投稿するツールを作成してみました。
 
は、Datadog API を使って SLO を取得し整形して Slack に投稿するシンプルなツールです。
コンテナイメージで公開しているので手軽に利用することができます。
まだまだリアクション数が少ないですが当初に比べて反応してくれる方も増えました。
「ないよりはあったほうがいい」そんなツールです。
💡
障害の話になりますが、SLO の値が日々悪くなっていくことがありました。 そしてある日、大規模な障害が起きました。 後述する Tips で紹介しているエラーバジェットバーンレートを導入する前だったので前もって気づけなかったのですが SLO の値が悪くなっていくといつか大爆発する、そんなことを学べました。
 

Tips


SLO に対するアラートはどうしたらいか

今回は SLO に対してアラートを設定はしておらず、今後も設定する予定はありません。
これは後述するエラーバジェットバーンレートに対してアラートを設定しているためです。

エラーバジェットバーンレート

バーンレートとは、Google の造語で、SLO の目標長に対してエラーバジェットがどの程度速く消費されるかを示す単位なしの値です。たとえば、30 日間を目標とする場合、バーンレートが 1 であれば、1 の割合が一定であれば、エラー予算がちょうど 30 日で完全に消費されることを意味します。消費率 2 とは、一定であれば 15 日、消費率 3とは 10 日でエラーバジェットが枯渇することを意味します。
バーンレートについては Datadog のドキュメントが分かりやすいです。
バーンレートアラート
SLO バーンレートアラートは、SLO エラーバジェットの消費率が指定した閾値を超え、それが特定の期間継続した場合に通知されます。たとえば、SLO の 30 日間目標に対して、過去 5 分間で過去 1 時間に 14.4 以上のバーンレートが測定された場合にアラートを設定できます。また、アラートが必要な閾値より少し低い閾値、例えば 7.2 以上のバーンレートが観測された場合にオプションで警告を出すように設定することができます。 注: バーンレートアラートは、メトリクスモニターの種類(メトリクス、インテグレーション、APM メトリクス、異常検知、予測値、外れ値モニター)のみで構成された メトリクスベースの SLO または モニターベースの SLO でのみ利用可能です。 バーンレートとは、Google の造語で、SLO の目標長に対してエラーバジェットがどの程度速く消費されるかを示す単位なしの値です。たとえば、30 日間を目標とする場合、バーンレートが 1 であれば、1 の割合が一定であれば、エラー予算がちょうど 30 日で完全に消費されることを意味します。消費率 2 とは、一定であれば 15 日、消費率 3とは 10 日でエラーバジェットが枯渇することを意味します。 この関係は以下の式で表されます。 $${\text"SLO 目標の長さ" \text" (7、30 または 90 日)"} / \text"バーンレート" =
 
このエラーバジェットバーンレートはエラーバジェットに対して直接アラートを仕込めるため SLO に対してアラートを仕込む必要がなくなります。
またノイズアラートになりやすい 5xx エラーレートに対してのアラートも消せるため一石二鳥です。
実際にドットマネーで設定しているアラートはこのようになっています。

クエリ
メッセージ

💡
short_window, long_window を同時に設定するクエリは Web 上からだとできないため Terraform(API) から設定を行いました。この記事を公開しているときには設定できているかもしれません。
💡
を同時に設定している理由 short_window のみだとアラートの頻度が多くなりノイズになりやすく、long_window も条件に追加することでノイズを減らし信憑性のあるものにするためです。
 

終わりに


今回の記事は SRE 初心者である私がどうサービスに対して SRE を導入したかの体験記みたいなものでした。
SRE を導入するのは敷居が高いですが、とりあえずやってみるのが大事かなと思います。
もちろん、SRE は文化であり組織でもあるので導入したから終わりという話ではなくまだまだ物語は続きそうです。
私自身 SRE を完全には理解していませんが、最初は深く考えすぎずに導入し日々改善していくのが手っ取り早く SRE に対して知見が得られると思います。(先が見えにくく地味な作業が多いので…)
直近考えていることは SRE とビジネスインパクトの紐付けを可視化したいと思っています。 例えばエラーバジェットがなくなりマイナスになった際の売上高と、エラーバジェットがきれいに使われる(もしくは余る)ときの売上高の差を可視化、とその方法を決め本当に SRE がビジネスに対して影響があることを漠然とした世界から現実にしたいと思っています。
もしすでにやっている方がいましたらぜひ教えて下さい。
SRG では一緒に働く仲間を募集しています。 ご興味ありましたらぜひこちらからご連絡ください。
 
このエントリーをはてなブックマークに追加