クックパッドでのサービスメッシュについて

Taiki氏:みなさん、こんばんは。Taikiといいます。クックパッドで働いています。「service mesh」という単語を聞いたことがある人ってどのぐらいいます?

(会場挙手)

お〜、さすがプラットフォームに興味があるみなさん。その中でも「Envoy Proxy」というネットワークプロキシを聞いたことがある人ってどのぐらいいます?

(会場挙手)

お〜、情報のキャッチアップが早い。あ、すいません(笑)。別にそこに精通してなくてもよくて、そのへんの雰囲気がわからない人でも大丈夫なスライドになっていると思います。

クックパッドでservice meshというものを1年ぐらい前から作って運用しているのですが、今日の話はそれをなんでやったのか、どういういいことがあったのかです。中の要素技術とか、「クックパッドのユースケースだとこういう構成でうまくいった」みたいな事例紹介ができればいいなと思っています。

背景の説明から、どういう問題があって、どうやって導入して、運用上どういうことをやっているか、どういういいことがあったかみたいな話をしようと思っています。今日は20分なのでコンパクトにいきます。

クックパッドは料理に関することをだいたいやっている会社です。一番大きいプロダクトだと、レシピ共有サービスというのがあって、「cookpad.com」のURLでアクセスできます。

日本とグローバルプラットフォームという2つのプラットフォームがあるんですけど、両方合わせてだいたい200前後のサービス開発者がいると思います。プロダクションサービスがだいたい100ぐらいある規模感です。

組織も、1つのチームで1つのプロダクトを作っているわけではなくて、複数いろんなチームがそれぞれサービスとかプロダクト開発をしているチームがあり、中央にSREとか僕たちみたいなプラットフォームチームがいます。

開発はそれぞれのサービスチームに権限移譲がされているんですけど、まだ運用に関しては中央のSREチームで持っているのが現状です。もっと開発、組織的なスケーラビリティを上げていきたいねということで、この運用もサービスチームで自分たちでできるように仕組みを整えることを目指しているところです。

技術的な要素としては、Ruby on RailsでアプリケーションやAPIサーバをよく作っています。

ほとんどはこれです。たまに別の言語で作られているアプリケーションがあったりします。

典型的な構成だと、だいたいインターネットフェイシングな、AWSのロードバランサがあって、そこから先にアプリケーションがある。その内部の別のプロダクトの情報を取ってきたり、ユーザーの情報とかレシピの情報とか、あるいは動画の情報とかを取りにいきたいときは、インターナル専用のロードバランサがあります。

これもAWSもマネージドサービスがあって、それを通って各アプリケーションにルーティング、ロードバランシングされるという構成がよくありました。

100近いアプリケーションが通信しあっている

問題はなにかというと、5年・10年前は1つの大きなRailsのアプリケーションだけ運用していればよかったんですけど、いまは100近いアプリケーションがあることです。それぞれが相互に通信し合っているので、1つ落ちると連鎖的にいくつか落ちちゃったりして、全体的なシステムのReliabilityを上げるのが難しくなってきているというのが1つあります。

(また、)1つのアプリケーションだと、デバッグってかなり簡単だと思うんですけど、ネットワークを介して通信した先のさらに通信した先のサービスのインシデントとかがちょっと起きたり。あとネットワークトラブルが起きたりすると、どこが原因となってこのインシデントが起きているのかという、そのdetectionにめっちゃ時間がかかったりするようになって大消耗してたので、それをなんとかしたいというのがあります。

あとは、Capacity Planningとかも、ネットワークトポロジーが成長していくといろんなアプリケーションが相互に依存しているので「うーん、難しい……」となったので、そのあたりもなにが起きているのか見えるようにしたいというのが課題でした。

まずservice meshの前の話で、とりあえずライブラリですね。ほとんどがRubyアプリケーションだったので、「ライブラリでなんとかできないかな?」となって試したのがこの2つです。

RetryとかCircuit Breakingとかで落ちたサービスの影響をなるべく受けないようにするライブラリが「Expeditor」というやつで、これを作ったりしました。

あと、「分散トレーシング」といって複数のサービスにまたがったAPIコールを1つに集めて分析できるものがあるんですけど、それを試していました。

分散トレーシングのやつはAWSの「X-Ray」というマネージドサービスなんですけど、これでそういうサービスマップが見れたり、あるHTTPリクエストについて関わったそのサービスでどういうステータスが返って、どのぐらい時間かかってたかというのが分析できたりみたいなやつを導入していました。

ただ、Rubyはいいんですけど、そのほかのプログラミング言語とかランタイムについては、当然各々作っていく必要があって。作るだけなら1回筋肉をがんばればいいんですけど、それを継続的に改善していく必要があります。それで「なんかそれに時間を費やすのはなんか違うなぁ。もっとほかのやり方があるな」というのを考え始めました。

service meshとの出会い

ということで、「ネットワークプロキシを通信の間に挟んで、そのネットワークプロキシにRetryとか、あとService Discoveryとか、そういう仕事をさせればいいんじゃないかな」というアイデアがありました。

「service mesh」という概念に出会うわけです。これが2017年の初めのほうにSREチームのメンバーの人が「SRECON America」に行ってきて、「こんなのがあるよ」というのを教えてもらって。「おっ、これは僕たちがやりたかったのに近い」というので、これに取り組み始めた感じです。

いままでライブラリレイヤーでRetryとか、あとはCircuit Breakingとかをやっていたのを、外の別のプロセスのネットワークプロキシに任せるというのがコンセプトです。

それだけじゃなくて、そのネットワークプロキシを中央のなにかで制御することによって、動的に設定を変えたりとか。そのネットワークプロキシいっぱい管理するのは非常に大変で、いろんなサービスが生まれたり死んだりとかしている状況で、中央で管理するやつがあるといいねというのがわかったのが、2017年頃です。

service meshを検証、導入へ

ということで、クックパッドでもservice meshの検証をして導入を始めました。だいたい時系列的には、2017年の初めぐらいに「こういうのが欲しい。あるといいかもしれない」みたいな構想を練ってました。

いろいろあって、2017年の終わり頃にMVPを作り始めて検証とか始めたという感じです。2018年の初め頃にいろんなアプリケーションで使えるよという、そういうタイムラインでした。

クックパッドでは「Envoy Proxy」というネットワークプロキシを採用しています。

なんでこれにしたのかというのは、その当時比較できる中では一番軽量で、自分たちが欲しい機能、Graceful ReloadingとかgRPCのサポートがあったのがでかいです。

service meshの実装って世の中にいまではたくさんあるんですね。Istioが一番有名だと思うんですけど、当時計画してた頃にはIstioは発表されていなくて、まだなにも知らなかったんです。

MVPを作り始める頃にはIstioが発表されていたんですけど、クックパッドでは、あとで紹介があるかもしれませんが、Amazon ECSというAWSのマネージドのコンテナオーケストレーションツールみたいなやつで動いているんです。(一方で)当初のIstioはKubernetes前提になっていたので使いづらいというか使えないかもしれないぐらいのレベルで、自分たちでcontrol-planeを作ってEnvoyを操作するという方向でservice meshを作ろうと決めました。

目標にしていたのは、resiliencyのセッティングとかをいままでは各アプリケーションのコードに直接書いていたんですけど、これを中央で一覧できるようにしたいというのがありました。

というのも、複数のチームが絡むようなサービス通信になってくると、誰がそこに責任を持つのかが難しくなります。その各サービスのアベイラビリティだけだとその各チームで決められるんですけど、それを通して全体という話になると、どこかが把握してどこかが調整する必要がありました。それを中央で低コストできるためには、一覧性があって、一括して操作できるというのが大事でした。

あと、各チームが最初にいろいろ自分たちで設定を追加できるようにしたかったので、レビューフローというのが必要です。

あと、全部のメトリックスは当初からPrometheusにいろんなメトリクスを集めようとしていたので、メトリクスは全部Prometheusに入れたい、そういう要求がありました。当然ながら、そのプラットフォームチームは3人ぐらいで数が少ないので、マネージドサービスとかを使って運用コストをなるべく下げたい。そういう目標で作りました。

構成図と設定

その構成図がこれです。

Envoy Proxyが下のほうにいて、サービス開発者がサービス間通信に関する設定を書いて、GHEのあるリポジトリがあって、そこにマージをします。

そうすると、EnvoyのxDS APIというのがあって、Envoyの設定を更新するためのAPIのプロトコルがあるんですけど、それに則ったかたちに設定を変換するツールがJenkinsの中で走って、生成されたファイルがS3にアップロードされる。各サービスのEnvoyは、その生成されたファイルを取得することで自身の設定を更新し続ける。こういう仕組みになっています。

HTTP/1.1でつながる、つまりJSONベースのAPIサーバに対しては、ELBを使ってロードバランシングとかしているので、Envoyは直接ELBを見にいく。ELBから先はNginxとか各アプリケーションコンテナにルーティングされます。

あとで紹介するんですけど、gRPCアプリケーションについてはELBというかALB、AWSのApplication Load BalancerがH2 Backendに対応していなくて、ELBを使わない方式でトラフィックを流したいのでどうにかEnvoyの機能を使って、EnvoyでロードバランシングとService Discoveryさせるようにしています。

そのサービス開発者が書く設定というのは、こんな感じのJsonnetファイルになっています。

なにを書くかというと、ルーティングに関する設定です。これはNginxのlocation directiveに近くて、パスごとにどういう設定をするかというのが書けるようになっています。

例えば、あるパスだとRetryはこのぐらい、タイムアウトは3秒。でも、あるパスだとリクエストが重いからRetryは10秒にするみたいなことを書くのがRoute Configです。そのルートをする先の設定も書けて、それがEnvoyではClusterというTerminologyになっています。Clusterがその来たリクエストを……。あっ、巻きでしゃべらないとダメですね。すいません、ちょっと時間忘れてました。

そのClusterがリクエストをどこに飛ばすかという情報で、だいたいはELBです。

そのService Discoveryについては、EnvoyのService Discovery Service API、さっきのEnvoyが設定を取得するためのAPIの1つに、Service Discovery Serviceというのがあって、これはある接続先につなぐときはどこにつなぐのかというのを、中央のサーバから各Envoy Proxyに配布するためのAPIです。

これは、lyftのdiscoveryという参考実装みたいなやつがあって、最初はそれを使っていましたが要件的に合いませんでした。「cookpad/sds」というところでGitHub上で公開しているんですけど、これを作って使うようにしています。

オペレーションとダッシュボード

使えるようになったので、実際オペレーションがどんなふうになっているのかです。

これが、その各サービスのEnvoyから送られているメトリクスをGrafanaで可視化したダッシュボードを作っていって、これがあるサービスが接続しているサービスに関するメトリクスです。

例えば……日本語でなんと言えばいいのかわからないですけど。なんだろう、困ったな。リクエストしている先から返ってきたレスポンスステータスのステータスコードを集めたものが表示されていたり、あと、RPS、どれぐらいリクエストがあるのかとか、Retryやタイムアウトがどれぐらいあるのかとかが表示されています。

これは上流のサービスに障害があったときのメトリクスです。Retryを諦めた数がすごい跳ねていて、サービス開発者がこのグラフを見たら「なんかうちのサービスが悪いけど、上流のあるサービスの調子が悪いんだな」とわかるようになります。

これはEnvoy自体を管理するためのダッシュボードで、いまだいたい1,100Envoyインスタンスが動いています。

これが映えるコンソールなんですけど、サービスマップ、各サービスがどれぐらいあって、どこと通信していて、どれぐらい流量があるのか、エラーがどのぐらいあるのかがわかるようになっています。これがNetflixのVizceralを使って動いています。

これがそのあるサービスで、おそらく一番依存が多いやつなんです。どこから参照されて、どこが一番リクエストが多いかが見れるようになっています。

このへんはあんまり関係ないので飛ばします。

サービスメッシュを使ってよかったこと

実際service meshを使ってみてなにがよかったかというと、一時的なエラーの上昇みたいなやつが、だいたいRetryとかタイムアウトでなんとかなってしまって、中央のSREチームの運用負荷が下がったというのがあります。

あと、いままで各サービスとかアプリケーションの中に埋まっていた、レジリエンシーに関するセッティングとかが中央に集まって、レビューできるようになったというのが大きいです。

Fault Isolation。Circuit Breakingみたいなところに関しては、まだそんなに効果を実感することがなくて、とりあえず入れているみたいな感じです。

Observability、各サービスの通信でいったいなにが起きているのかというのはすごく見れるようになって、簡単に見れるようになったのも大きいです。先ほどのGrafanaのダッシュボードみたいなやつで各サービスのヘルスステータスを一覧できるようになったのがとてもよかったです。

ということで、このEnvoyのxDS API、僕たちはv1を使っているんですけど、v2になって、そのマイグレーションをちょうどいまやっていてそろそろ終わりそうなので、それはまた別の機会でしゃべります。ほかにもいろんなことをこのプラットフォーム上でやろうと思っています。

ということで、なにか質問があったら、懇親会やTwitterで質問してください。

あと、EnvoyConというのが1ヶ月……2週間、3週間先にあって、そこで新しい進捗をしゃべるので。これシアトルであって、もうチケットとかは完売しちゃっているので難しいんですけど。スライドを公開するので、興味があれば見てください。ありがとうございます。

(会場拍手)