Chaos Engineering に向けてレシピサービスの Steady State を追求する

こんにちは、今年ソフトウェアエンジニアとして新卒入社した @itkq です。社会人になってから 1 クールで見るアニメの本数がガクッと減っていることに気づいて最近は無力を感じています。さて、この開発者ブログで「Chaos Engineering やっていく宣言*1」が公開されたことは記憶に新しいと思います。私はインフラストラクチャー部 SRE グループに配属され、最近は Chaos Engineering に関わる取り組みも行っています。その中から今回は「レシピサービスの Steady State を追求する」取り組みについて、背景や現状も含めて紹介します。

Steady State とはなにか、なぜ必要か

一昔前の Web サービスといえば、様々な機能が 1 つのアプリケーション上に実装されたモノリシックアーキテクチャが一般的でした。その後サービスという単位で機能を切り出して別アプリケーションとして実装するサービス指向アーキテクチャを経て、オーナーシップを持って速度をもった開発を行うといった組織論の意味合いも含めたマイクロサービスアーキテクチャに変遷し今に至ることはもはや前提として良いでしょう。クックパッドにおけるアーキテクチャの変遷は、クックパッドとマイクロサービス*2 に詳しく書かれています。

アーキテクチャの変化に伴い、Web サービスにおける障害の質も変化しました。モノリシックアーキテクチャでは、データベースの接続に失敗するなどアプリケーションで致命的なエラーが発生した場合、当然ながらアプリケーションのすべての機能は利用できなくなります。一方で、マイクロサービスアーキテクチャでは、あるサービスが何らかの原因で利用できなくなった場合でも、別のサービスは正常に利用できることが起こりうります。複数のサービスが全体としてのサービスを構成しているため、あるサービスが障害に陥った場合に「ユーザから見える (全体としての) サービスは正常に動作しているのか?」という疑問に答えられる必要があります。ここで重要なことは、「あるサービスが障害になっても、サービス全体の機能が停止しない」というフォールトトレランスの考え方です。障害中のサービスを graceful に degradate するなどの手法により、ユーザは依然として目的のサービスを利用できる可能性があります。

Web サービスにおいてそのビジネスのために最も重要なことは、ユーザが快適にサービスを利用できているかどうかです。システムの裏側の内部がどうなっているかということはユーザにとって関心がありません。ゆえにシステムの内的な状態ではなく、ユーザの体験という外的な状態をもってシステムが正常であるかを判断できる必要があります。その判断基準となるものが Steady State です。Chaos Engineering においては、本番環境で意図的に障害を注入するような実験を行い、Steady State に変化が見られないという仮説を検証します。すなわちその障害がユーザの体験を損ねていないことを Steady State によって判断し、システムの回復性に自信を持つ、または未知だったシステムの弱点を修正することが目的です。

クックパッドでは、Envoy proxy (以下 Envoy) を用いたサービスメッシュを本格的に導入しており、マイクロサービスアーキテクチャにおいてクリティカルであるサービス間通信の Observability が確保されつつあります。詳細は Service Mesh and Cookpad*3 で述べられています。一方でクックパッドにおける各サービスの依存関係はますます複雑になっており、Chaos Engineering を起点としてシステム全体の Resiliency を高めていく必要がある段階であると意識し始めました。そこでクックパッドのシステムアーキテクチャ上での Chaos Engineering の仕組みや実装を検討すると同時に、Steady State の仮説検証も進めていくことにしました。Steady State を追求する取り組みは、Chaos Engineering の文脈でなくても、マイクロサービスアーキテクチャによるモダンな Web アプリケーションでは重要だと私は考えています。

レシピサービスの Steady State を考える

クックパッドでは現在、komerco*4, クックパッドマート*5 など数々の新規事業を進めていますが、事業の柱となっているのはやはりレシピサービス (cookpad.com) です。このレシピサービスは巨大な Rails アプリケーションでありますが、「お台場プロジェクト*6」の一環で、機能の切り出しが進められており、Envoy を通していくつかの別サービスと通信しているのが現状です。先述した Steady State が必要な背景と合わせて考慮し、まずレシピサービスの Steady State を定義することが先決という結論を出しました。

Steady State は Web サービスの歴史からするとまだまだ新しい概念で、Steady State を考えるにあたり、Netflix のソフトウェアエンジニアによって書かれた Chaos Engineering 本*7 を大いに参考にしました。しかし、Steady State は事業の性質によって全く異なり、また正解が 1 つに決まっているものではないことは少し考えただけで想像できました。具体的に Netflix のサービスと違う点として、課金していないユーザもレシピサービスを利用できる点が挙げられます。

どのユーザを対象とした Steady State を考えるべきなのか

Netflix のストリーミングサービスとクックパッドのレシピサービスの異なる点として、課金モデルが挙げられます。Netflix では課金しているユーザだけがストリーミングサービスを利用できますが、クックパッドでは課金していないユーザも一部機能を制限されながらレシピサービスを利用できます。

Chaos Engineering 本では、Steady State は “今顧客を失っていないか? という質問に答えられるようなビジネスに関わるメトリクスであること” とされています。クックパッドのレシピサービスは、課金ユーザだけでなく多くの課金していないユーザからも利用されている事実があります。「現時点」のビジネスメトリクスとするならば、Steady State の対象とするユーザは課金ユーザだけに絞るべきかもしれません。しかし、レシピサービスは月間約 5,500 万人が利用する*8、日本の家庭に根付いたものであるということができ、「顧客」は必ずしも課金ユーザに限定すべきではないと私は考えました。その裏側には、課金体系は月額のサブスクリプションのため、試しにプレミアムサービスを利用する障壁は低く、非課金ユーザのレシピサービスの体験が新たな課金ユーザの獲得、ひいてはレシピサービスの存続に関わる重要なものであるのではないかという思いがあります。この考えが適切かどうかに対する回答は未だ持っていません。しかし Steady State を考え始めるにあたって、対象とするユーザは「レシピサービスを利用するすべての人」という前提を置くことにしました。

この前提を元に、以下では 2 つの観点から Steady State となりうるメトリクスを選択し検証した結果を述べます。1 つ目は、レシピサービスのコア機能という観点です。しかし、レシピサービスの性質上そう単純にうまくいかないことが分かりました。この結果を踏まえ、2 つ目の観点としたのがユーザの定常な行動パターンです。こちらは結果的に 1 つ目の観点より妥当であることが分かりました。

(1) レシピサービスにおけるコア機能から考える

クックパッドのレシピサービスは、基本的にクックパッド側でレシピを追加するのではなく、レシピを投稿するユーザがいることによって成り立っており、レシピ投稿者は無くてはならない重要な存在といえます。一方で、レシピが投稿される数とレシピが閲覧される数を比較すると、圧倒的にレシピが閲覧される数のほうが多いです。ゆえに、ユーザにとってサービスが定常かどうかを考える上でユーザの体験に影響が現れやすいと考えられるレシピ閲覧にまず着目しました。レシピ閲覧以外の機能 (例えばレシピ検索やランキング) に障害が発生した場合でも、レシピ閲覧に紐付いていると考えられるものはレシピ閲覧数に影響を与えるはずと考え、記録するメトリクスはひとまずレシピ閲覧数だけで十分としました。

ある一週間における、レシピ閲覧数/sec のグラフが以下の図になります。集計はリバースプロキシ層のアクセスログをもとに行っています。先週の値と今週の値を比較できるように表示しています。最もよくレシピが閲覧されるのは夕方で、晩ごはんの献立を考えるためであることが推測できます。先週と今週の値にあまり変化がなく、定常といえる日が多い一方で、先週と比較して大きく値が変動している期間があります。この期間で障害とみなせる事象は発生していませんでした。実は、先週のこの期間は本州に台風が来ており、その影響で外食を控え家で自炊するケースが増えてこのような結果になったのだと推測しています。レシピサービスという性質上、台風だけでなく天候に大きく影響されることは事実です。アクセス数が通常と比較して増減するという意味で、システム目線では定常ではなくなっているのかもしれませんが、Steady State はユーザ側の視点で定常かどうかを表現するべきものであることを考慮すると、単純なレシピ閲覧数というメトリクスは適切ではないことが分かりました。

f:id:itkq:20181030211329p:plain
今週と先週における レシピ閲覧数/sec の比較

そこで、関連性のある複数の値の関係を相対値として表すことで、ユーザ視点とシステム視点の定常の差を縮めることを考えました。レシピサービスにおいては、天候などの外的要因に影響されて利用者数が増減したとしても、「レシピサービスを使う大半の人の行動パターン」に影響は無いのではないかという仮説を立てました。

余談

社内ブログでこの取り組みの共有をしたところ、予想より多くの人からリアクションがありました。サービス開発チームからはよりユーザに近い立場の目線のフィードバックをもらえたり、機械学習グループからは異常検知についてのコメントをもらうことができ、良い方向に進められている手応えを感じました。Steady State を含めた Chaos Engineering の取り組みは、全社的に導入するためにその必要性を広く周知していく必要があると考えており、以降も定期的な発信を積極的に行う予定です。

(2) ユーザの定常な行動パターンから考える

レシピサービスにとって最も重要な機能の 1 つといえるものが「レシピ検索」です。クックパッドを使って献立を決めたいユーザがとる行動の最も基本的な遷移は、

  1. 手元にある食材を把握する
  2. クックパッドで食材名をもとにレシピ検索
  3. いくつかレシピを見た上で料理するレシピを決める

といえるのではないでしょうか。この例では、レシピ検索とレシピ閲覧は密接に紐付いています*9。これを踏まえて、前回の仮説から次のようにアップデートしました。「ユーザは探しているレシピを閲覧できている」こと、もう一歩踏み込んで、ユーザが上で示した行動パターンを取れていることをもって Steady State とする、という仮説です。前回の失敗からの学びから、今回は相関すると考えられる 2 つのメトリクスの比を Steady State として使えないかを検討します。これは、台風が来てアクセスが増えた場合でもユーザの行動パターンは変化しないのではないかという考えに基づきます。また、このアイディアは同僚の @KOBA789 と雑談していたときに生まれたものです。雑談は大事ですね。

レシピ閲覧数と検索数の比を検討するにあたり、まず本番のデータからこの値を抽出・表示するのではなく、今回は過去のデータを使って妥当性を判断することにしました。クックパッドでは、Redshift を中心としたデータ基盤*10 が整っており、過去のレシピ閲覧数や検索数を SQL で抽出することができます。また SRE グループでは、障害が発生した後にその障害が与える影響・障害の原因・根本対処などを postmortem として記録する文化が根付いており、過去の障害とその影響も容易に検索することができるため、データ基盤の活用と合わせることでユーザの体験に影響を与えた実際の障害が発生していた区間のメトリクスを検証できます。Chaos Engineering 本において、Steady State を表せるようなメトリクスを検討する際は、そのメトリクスを取るために必要な労力のバランスを取る必要があると書かれていますが、過去のデータを元に検証する労力は、本番環境で新しくメトリクスを採取して時間経過をおいてから検証することに比べてはるかに低いことは明らかです。

以下の図は、ある 2 週間の iOS アプリケーションにおける レシピ検索 / レシピ閲覧 の数の比を示したものです。

f:id:itkq:20181030211355p:plain
レシピ閲覧数と検索数の比 (1)

オレンジ色で値が明らかに跳ねている期間で、実際にレシピサービスを使うユーザに影響があった障害が発生していました。青色で値が突出している部分は、テレビ放映によりアクセス数が増加した時間帯でした。この比の興味深いポイントは、次に示す別の 2 週間の図に表れています。1 つ前の図と同じクエリ・スケールです。このうち、実は大型の台風が来ていた期間があります。レシピ閲覧数は増加していますが、それに伴ってレシピ検索数も同じように増加しているため、値には大きな変化が表れていないと考えられます。

f:id:itkq:20181030211347p:plain
レシピ閲覧数と検索数の比 (2)

他にもレシピサービスに関係する過去の障害に対してこの比を計算してみたところ、1 枚目の図に近いスケールで値が跳ねていました。ところで、年間を通してクックパッドで最もアクセスの多い期間はバレンタイン周辺です。ある年のバレンタイン周辺の 2 週間について同様に描画したグラフが以下の図です。

f:id:itkq:20181030211404p:plain
レシピ閲覧数と検索数の比 (3)

バレンタインの周辺は、相対的に全体の値が下にシフトしていました。しかし、障害の時ほど明らかな外れ値は見られません。

以上のように、ユーザの定常な行動パターンを考え、基本的だと考えられる行動パターンに関係する 2 つのメトリクスの比を検討したところ、1 回目で試した「単純なレシピ閲覧数」よりもユーザから見たシステムの Steady State を表せていることが分かりました。そこで、現在ではレシピ閲覧数とレシピ検索数の比を遅延高々数十秒で記録・表示しています。それ以降ユーザに大きな影響を与えるような事象は (この記事を書いている時点では) 観測できていませんが、過去の事例を使った検証結果より、この値を記録していくことには意味があると考えています。

まとめ

Steady State とは何か、またそれが必要になった背景の説明と、クックパッドのレシピサービスの Steady State を追求する取り組みについて紹介しました。実サービスにおける Steady State に関する情報はあまり見当たらないため、参考になれば幸いです。Chaos Engineering 本を参考にしつつ、レシピサービスの Steady State を考える過程で、以下に挙げるいくつかの学びがありました。

  • Steady State はサービスの性質によって異なり、あらゆるサービスで唯一の正解となるメトリクスは存在しない
  • 仮設を立てながら Steady State となり得るメトリクスを地道に検証していく必要がある
    • そのためにメトリクスの収集・表示・アラーティングなどの環境が整っている必要がある
  • 過去の障害記録と当時のログデータやメトリクスは、妥当性のある Steady State の検証を効率的に行う助けとなる

クックパッドのレシピサービスは、ユーザの生活と密接に関係しており、ユーザの体験を損ねないことが重要です。Steady State の定義だけでなく、その先にある Chaos Engineering も用いるなどしてシステム全体の Resiliency のさらなる向上を目指していきたいと考えています。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/