2019」が開催されました。Cookpadのエンジニアやデザイナーがどのようにサービス開発に取り組んでいるのか、またその過程で得た技術的知見について公開します。小杉山拓弥氏(以下、小杉山):それでは、発表を始めさせていただきます。IDはitkqでインターネットでやっていて、クックパッドには2018年に新卒入社しました。技術部SREグループというところにいて、カオス大臣という設定があるんですが、まあ、それはよくて、耐障害性の向上などの取り組みをやっています。今日はタイトルにもある通り、カオスエンジニアリングの話をしていきたいと思います。この言葉自体は去年もバズったところがあって、みなさんも聞いたことがあるのではないかなと思っています。簡単に説明だけしていくんですが、もっとも有名な定義として「カオスエンジニアリングの原則」というものがあります。ここには日本語訳を出してるんですが、「分散システムにおいてシステムが不安定な状態に耐えることのできる環境を構築するための検証の規律です」。抽象的なところがありますが、例えとして挙げられるのが予防接種です。将来的な病気を予防するために、人体に対して意図的に弱らせたウイルスを侵入させることによって免疫をつける、ということがあると思うんですが、その人体をシステムに置き換えてみるとわかりやすいかなと思います。Monkeyというツールがあるんですが、それを使ってランダムにインスタンスを落としていきます。Monkeyは使ってないし、すぐに使う予定もありません。今日話したいこととしては、「結局、カオスエンジニアリングって何だっけ?」ということを考えたい、というのがあります。Monkeyが確かにカオスエンジニアリングという言葉を広めたのは確かだと思うんですが、少なくとも「ランダムに落とす」というところだけ切り取ると、ここは重要じゃないと思っています。最近カオスエンジニアリングが流行っているから、うちでもランダムにインスタンスを落としていこうというのは説明が足りてないと思うし、こういう導入の仕方は間違ってるのではないかなと、個人的には思っています。今日は、クックパッドのTechConfということもあるので、クックパッドがカオスエンジニアリングをやっていく理由と、「実際どうやってやっているのか?」という、その2点について話していきたいと思います。マイクロサービスアーキテクチャへの道のり最初の理由についてなんですが、これはクックパッドが長い時間をかけてやっているマイクロサービスアーキテクチャへの移行というのが大きく関係しているので、まずここから話していきたいと思います。2008年当時、cookpad.comというレシピサービスは、1つのRailsアプリケーションでした。下にあるディベロッパーの数は適当なんですが、上の円と比較した規模感だと思ってください。これが2015年になると、開発も進むし、ディベロッパーも増えて、「世界最大のRailsモノリス」と呼ばれるまでに成長します。ここまで大きなモノリスになるとさまざまな問題が起きていて、コードベースもそれなりに大きくて、テストもたくさんあるので、CIが遅いとか、共有コードがたくさんあるので、どっか1行変更しただけで、ぜんぜん違う部分のテストがめちゃくちゃ落ちるとか、ということがありました。違う機能を作ってるはずなんだけど、共有コードの問題からコンフリクトしてしまって、「どっちが先に出すのか?」という調整が発生したりしました。この時、並行して、事業の変化から新しいサービスというのが立ち上がってきます。これはRailsとは独立したアプリケーションとして開発されていて、必要があればレシピの情報を問い合わせるようになっていきます。これとは別に、レシピサービスから切り出せる機能というのを切り出す、という動きもありました。切り出すコストというのはそれなりにあったはずなんですが、さまざまなトレードオフを考えた結果、このような独立がいくつか起きていきます。モノリスからマイクロサービスに移行このようにクックパッドでは、組織の構造の変化とか事業の変化を起点として、全体のシステムを1つのアプリケーションではなくて、アプリケーションの集合体からなるシステムとして構築していく、マイクロサービスアーキテクチャへ移行していくことにしました。これによって、先ほどのモノリスで非常に問題だった速度面の問題は、大きく改善されたと言っていいと思います。一方で、トレードオフはあります。全体としては分散システムになっていって、複雑になっていました。ここに出してるのは、実際にいまクックパッドで起こっているサービス間の関係を一部出したものなんですが、認証やA/Bテストという内部システムがあったりとか、cookpadTVなど、新規のサービスも出てきています。認証サービスのレスポンスが遅くなったことがありました。すると、その認証に依存している、とくにユーザーに面しているサービスまでその影響が及んで、レスポンスタイムが遅くなったり、エラーを返してしまうということが起こりました。このようにマイクロサービスアーキテクチャを進めていくと、1つのシステムの障害が別のシステム、複数のシステムにまで及んでしまうということが起こりうります。レスポンスタイムが遅くなったら、クライアント側でリトライするとかということもあると思うんですが、逆にリトライが集中すると、かえって障害を悪化させてしまうということもあります。当時、中央的にインフラを運用していたSREチームからすると、サービス間通信の状況がすぐわからないというのが、課題としてありました。個々のシステムのメトリクスは取れているんだけど、サービス間通信のメトリクスは取れていなくて困っていました。サービスメッシュとはなにか問題を解決するために導入したのがサービスメッシュです。これは簡単なサービスメッシュの図のつもりなんですが、簡単に言うとサービスメッシュは、アプリケーションレイヤーから通信を引き離したものになります。Cに通信したいとします。Proxyがやります。Bがどこにいるかわかるようになっています。これを導入したことによって、アプリケーションにほとんど手を加えることなく、サービス間通信を実現しました。これによってうれしいことがありました。Envoyは、サービス間通信のメトリクスを吐くことができます。AからBの通信でリトライが何回起きてるとか、そういう情報を吐くことができます。あらゆるサービスにこのEnvoyというのを導入していくことによって、中央からメトリクスを集めると、サービス間通信の状況がよくわかるようになりました。これまでアプリケーションレイヤーでやっていた、タイムアウトやリトライ処理を個々のアプリケーションに実装するのではなく、そのEnvoyという共通のコンポーネントで行うことによって、コンフィグを共通化できるし、中央的にそれを管理できることによって、一時的な障害に耐えやすくなりました。障害が起きたときにユーザーに対してどう振る舞うかなったんですが、これで問題が解決したかというとそんなことはなくて、突然ストレージがぶっ壊れて、サービスが応答しなくなるということもあるし、ネットワークが突然ダメになってしまうということもある、ということです。このように、障害をゼロにするというのは絶対にできなくて。クラウドのSLAは100パーセントじゃないし、逆にどんな高価な機器でも、壊れる可能性はあるということです。そんな時に考えたのが、依存先のシステムが障害になった時に、とくにユーザーに面してるサービスはどうふるまうべきなのか、ということでした。マイクロサービスアーキテクチャにおいて障害に対する問題意識を改めて考えたんですが、ユーザーにとっては、システムの裏側がモノリスなのかマイクロサービスなのかはどうでもよくて、ユーザーがやりたいことが提供できるのかできないのかという、ただそれだけが重要だと思っています。あるシステムで障害が起きた時に、引きづられてサービスごと全部ダウンしてしまうのか、それとも部分的に機能を提供できるのかというのは、大きな違いがあると思っています。部分的に提供したほうがうれしいユーザーは絶対いるはず。いうことで、まずは我々のシステムが、障害が起きるとどのようにふるまうのかを知る必要がありました。Injectionするという考え方がありました。カオスエンジニアリングを始めるのは必然というのが我々とカオスエンジニアリングの出会いです。ここに出してるのは、冒頭で出した原則なんですが、我々なりの解釈でいくと、マイクロサービスアーキテクチャで発生する障害に対する、プロアクティブなアプローチのエンジニアリングというふうに捉えています。去年、カオスエンジニアリングやっていく宣言というのが、突然出たかのように見えたんですが、まったく突然ということはなくて。今まで説明してきたように、我々のシステムのアーキテクチャが発展していくにしたがって、自然と、また、必然的に必要になったものということを強調したいです。この図に戻るんですが、分散システム的に構築されたマイクロサービスアーキテクチャで重要なレイアウトととして、ネットワークがあります。このネットワークは、全体を支えるというのもあるんですが、場合によってはアプリケーションレイヤーの障害やパフォーマンス劣化をこいつが隠してしまうことがあります。Platformです。Injectionの機能を持っています。AからBへの通信のリクエストの30パーセントを503で失敗させるとか、ということができます。Injectionするプラットフォームというのを作っていくことにしました。大きな理由としては、アプリケーションにほとんど手を入れることなく、ネットワーク層の障害をエミュレートできることです。クックパッドにはhako-consoleといういい感じのコンソールがあって、これはディベロッパーもSREも見ることができる、サービスのためのコンソールになっています。Planeを担当しているItachoというソフトウェアがあるんですが、こいつを操作するロジックを入れたことで、実質的にこれがプラットフォームになりました。unknownsという概念があります。これを使って、まずはシステムにおける障害を分類して考えています。knownsは知ってること。Aがダメになってしまった時に、Bがどれだけダメになるのかがわかる。unknownsは、Aがダメになることがわかってるんだけど、どういう影響があるのかは実際わからない。unknownsは、想像もできていないことで、Bがダメになることすら考えたことがない、という状況になっています。これ自体は非常に汎用的な概念で、一般的に知識というのは知れば知るほど知らないことが出てくるので、広がる方向としては下向きです。カオスエンジニアリングは、この下2つに対抗する手段だと考えています。その知らないことに対して、それを知るためには、仮説を立てて実験するという方法があります。Testingという手段があると考えています。unknownsに対抗する手段だと考えています。unknownsがあるということで、ここから始めていくことを決めました。これを放ったらかしにすると、たぶん本番環境で実験するとユーザーに悪い影響が出ると分かっているけれど、分かっていながら実験するのは、僕は「違うんじゃないかな」と最初に思いました。ステージング環境で障害をエミュレート先ほどの図に戻りますが、我々のサービスメッシュというのは、重要なこととして、本番環境も、ステージングも、だいたい全部が同じメッシュ構造を持っていることです。ネットワークレイヤーの障害のエミュレートをステージングでやっても、ある程度、なにかしら発見があるのではないかと考えました。この例では、クックパッドブログからレシピサービスへの通信を失敗させるという、すごく単純な実験をしてみました。クックパッドブログはレシピサービスのレスポンスをキャッシュしている部分があるので、そのロジックに耐障害性に関する弱い部分が見つかって、それを修正することができました。これは本当に単純な実験で、今日は時間の関係で詳細は言えませんが、また機会があれば発表したいと思います。今日は「クックパッドがなんでカオスエンジニアリングをやっていくのか?」と、「どうやっていくのか?」の2点について話しました。unknownsの検証や改善からやっていって、ユーザーやディベロッパーをないがしろにしないやり方で、今後も耐障害性の向上に取り組んでいきたいと思います。以上で発表を終わります。ありがとうございました。TechConfに関する記事をまとめています。TechConfに関する新着記事が公開された際に、通知を受け取ることができます。