クックパッドと分散トレーシング

こんにちは、技術部の Taiki (@taiki45) です。

近年の Web サービスの開発ではマイクロサービスに代表されるように分散アーキテクチャが採用されるようになってきました。大規模でも素早いプロダクト開発をするために、クックパッドでもマイクロサービスを採用し分散アーキテクチャへの移行を進めています*1。今回は、そのような分散アーキテクチャを利用したシステム構築において必須のコンポーネントになりつつある分散トレーシングについて、クックパッドでの事例を紹介したいと思います。

分散トレーシングとは

マイクロサービスのような分散アーキテクチャでは、個々のサービス同士の通信が複雑になるため、モノリシックアーキテクチャと比較して、システム全体としての振る舞いを把握することが難しくなります。これはプロダクト開発においては、障害発生時の原因究明が難しくなったり、あるいはシステム全体でのパフォーマンスの分析が難しくなるといった問題として顕在化します。 分散トレーシングはこのような問題に対処するためのツールです。開発者が、特定のクライアントリクエストを処理するのに関わったサービスを探したり、レイテンシに関するパフォーマンスをデバッグする時に利用されます。

分散トレーシングの実現のアプローチには大きくわけて2種類あり、一つは Black-box schemes *2、もう一つが Annotation-based schemes と呼ばれています。 前者の Black-box schemes はシステム内の各サービスに手を入れる必要がないことが利点ですが、それと引き換えに特定のリクエストに対する分析はできません。後者の Annotation-based schemes は各サービスに分散トレーシング用のメタデータを下流サービス*3へと伝播させる実装を加えることが必要になるという欠点がありますが、特定のリクエストを分析することができます。Annotation-based schemes は Google の Dapper*4 や Twitter の Zipkin*5 等に採用されており、Web サービス業界では主流なようです。

Annotation-based schemes に基づく実装

Annotation-based schemes に基づいた分散トレーシングシステムの実装の仕組みを大まかに説明すると、ユーザーからリクエストをうける最初のポイントで “トレースID” という分散トレーシングシステム内で一意となる文字列を発行し、トレースIDや “アノテーション” と呼ばれる処理結果等の追加情報を含んだログをストレージに保存し、さらに下流に存在するサービスへリクエストを発行する際にトレースIDとアノテーションを伝播していきます。このような「トレースIDに紐付く一連のログのまとまり」を “トレース” と呼びます。このトレースをトレースIDをキーにしてストレージから検索することにより、特定のリクエストに関わったサービスを特定したり、また複数のトレース情報を集計することで分散システム内のコミュニケーションパターンを分析することができます。また、各サービスがリクエストの処理を開始/終了した時刻もトレースログに一緒に保存すれば、レイテンシの算出もできます。Google の Dapper や Twitter の Zipkin といった実装では、トレースログのタイムスタンプから各ログの親子関係を算出するのではなく、 各トレース内で一意となる文字列である “スパンID” をログの識別子として利用し、ログの親子関係をスパンIDで表現するようになっています。

ほとんどの分散トレーシングシステムは次のようなコンポーネントに分解できます:

f:id:aladhi:20170905163926p:plain

  • Instrumented library: 各サービスのアプリケーションに組み込み、トレースIDの採番や伝播やトレースログの送信を担うライブラリ
  • Log Collector: 各サービスインスタンスから送信されるトレースログの集約を担う
  • Storage and Query:トレースデータの保存と検索を担う
  • UI: 人間がトレースデータを検索・分析する際に利用する

Instrumented library は各言語向けに整備する必要があるので、各分散トレーシングシステム普及のボトルネックになっています。この問題を緩和すべく OpenTracing*6 のように Instrumented library API の標準化を進めているプロジェクトもあります。

クックパッドでの導入

分散トレーシングシステムの選定

クックパッドで分散トレーシングを導入するに当たり、いくつかの点を考慮して AWS が提供するマネージドサービスである AWS X-Ray*7 を採用しました。クックパッド内では一般的なユースケースを想定しているので、既存の分散トレーシング実装を利用することを決めました。分散トレーシングシステム実装として採用例も多く開発も活発な Zipkin に焦点を当てましたが、大規模な環境で Zipkin を利用するには Cassandra/HBase/Manhattan いずれかの運用が必要であり、データストアを自分たちで運用するよりは、解決したい問題にフォーカスできるマネージドサービスの利用に比較優位がありました。クックパッドでは AWS を積極的に活用するインフラストラクチャを構築していることもあり AWS X-Ray の検証を始めました。

検証開始時点では AWS X-Ray が提供する Instrumented library は Java/Node.js/Python のみのサポート*8で、クックパッドではほとんどのサービスは Ruby を用いて実装されているため、そのままでは AWS X-Ray は利用できませんでした。サードパーティ製のものも特に存在しなかったのですが、Instrumented library の実装方法については目処が立っていたこと、及び Instumentation library を自分たちで管理できることで他の分散トレーシングシステムへ低いコストで移行できる余地を残せる利点があったので、自作することにしました。自作した Instrumented library である aws-xray gem は OSS として公開しています*9

現状の構成

AWS X-Ray を利用したクックパッドでの分散トレーシングは以下のような構成で実現されています:

f:id:aladhi:20170905163959p:plain

  • Instrumented library: aws-xray gem を利用
  • Log Collector: AWS X-Ray の提供する X-Ray daemon というソフトウェア*10を利用
    • ECS を利用しているアプリケーションではいわゆる Sidecar 構成を取っています
    • EC2 インスタンス上で動作しているアプリケーションについては EC2 インスタンスの上に X-Ray daemon プロセスを動作させています
    • Instrumented library から UDP で X-Ray daemon にトレースログを送信し、X-Ray daemon がバッファリングと AWS の管理する API へのトレースログの送信を担います
  • Storage and Query: Storage はこちら側からは見えません。Query として AWS X-Ray の提供する API*11を利用します
  • UI: AWS コンソールに組み込まれています*12

aws-xray gem の実装において、トレードオフを考慮しつつモンキーパッチを活用することにより、ほとんどの Rails アプリケーションでは gem の導入と X-Ray daemon への接続情報を設定するのみで、トレースログの収集を開始できるようになっています。

今後の展望

現状はデータを AWS X-Ray に集めるところまでで、まだ本格的なトレースデータの活用には至っていません。データの収集については、社内の主要なサービスをカバーしており、サービスマップのノード数は現在約70ほどです。

f:id:aladhi:20170905164033p:plain

エラートラッカーなどに記録されているトレースIDから該当リクエストに関する分析ができるようになっています:

f:id:aladhi:20170905164117p:plain

サンプリング方式については Head-based coherent sampling*13 を採用しており、ユーザーからリクエストを受ける最初のサービスで sampled/not sampled を決めて下流サービスに伝播させています。サンプリングレートについては、特に rps の高いサービスのみ1%設定、他のサービスについては100%設定で運用しています。サンプリングについては課題があり、ミッションクリティカルなサービス*14の処理を含むトレースはトラブルシューティング用途に全件保存しておきたいですが、流量の高いサービスが上流にいるケースではサンプルされるトレースの割合が少なく、トラブルシューティングを行うユースケースで支障があります。その対策として、パス毎によるサンプリング設定等を実装・導入する予定です*15

クックパッドでは Barbeque*16 という非同期ジョブシステムを利用して非同期ジョブを実行しています。多くのジョブは Web アプリケーションのリクエストによりトリガーされているので、リクエストとジョブ実行との紐付けを記録できるようにする予定です。

また、システム全体のレイテンシ変化を検知できるように、AWS X-Ray の API を利用して監視システムを構築する予定です。監視システムについては自前で実装する以外にも AWS X-Ray の機能追加にも期待しています。

おわりに

AWS X-Ray を利用した分散トレーシングの実現について、クックパッドでの事例を紹介しました。クックパッドでは比較的大規模な Web サービス開発が行われており、分散アーキテクチャ周辺に存在する興味深い問題が多々あります。このような課題解決を一緒に取り組む仲間を積極的に募集しています

*1:http://techlife.cookpad.com/entry/2016/03/16/100043

*2:M. K. Aguilera, J. C. Mogul, J. L. Wiener, P. Reynolds, and A. Muthitacharoen. Performance Debugging for Dis- tributed Systems of Black Boxes. In Proceedings of the 19th ACM Symposium on Operating Systems Principles, December 2003.

*3:ここではユーザーからリクエストを受けるフロント側を “上流"、その反対側を "下流” と呼びます

*4:https://research.google.com/pubs/pub36356.html

*5:http://zipkin.io/

*6:http://opentracing.io/

*7:https://aws.amazon.com/xray/

*8:今では Go 言語向けのライブラリもサポートされました https://aws.amazon.com/jp/about-aws/whats-new/2017/08/aws-x-ray-sdk-for-go-beta/

*9:https://github.com/taiki45/aws-xray

*10:http://docs.aws.amazon.com/xray/latest/devguide/xray-daemon.html

*11:http://docs.aws.amazon.com/xray/latest/api/Welcome.html

*12:http://docs.aws.amazon.com/xray/latest/devguide/xray-console.html

*13:http://www.pdl.cmu.edu/PDL-FTP/SelfStar/CMU-PDL-14-102_abs.shtml

*14:例えば課金系サービス

*15:http://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-configuration.html#xray-sdk-java-configuration-sampling

*16:https://github.com/cookpad/barbeque

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://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('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/