Slide 1

Slide 1 text

gRPC in Cookpad Cookpad Tech Kitchen #20

Slide 2

Slide 2 text

自己紹介 ● 岩間雄太 ● @ganmacs ● 技術部開発基盤グループ ○ 2017年度新卒入社

Slide 3

Slide 3 text

目次 ● これまでの Cookpad のサービス間通信 ● Cookpad での gRPC 運用 ● Ruby で gRPC

Slide 4

Slide 4 text

gRPC 導入経緯

Slide 5

Slide 5 text

これまでの Cookpad ● Cookpad ではマイクロサービスを推進してきた ● 社内では Garage, GarageClient で API 共通化 ● Autodoc でドキュメンテーション ● 参考 ○ https://speakerdeck.com/eagletmt/web-application-development-in-cookpad-2017 ○ https://speakerdeck.com/adorechic/how-cookpad-shifts-to-microservices

Slide 6

Slide 6 text

gRPC 導入理由 ● IDL とスキーマがほしい ○ 各フィールドの型を定義、参照したい ● REST なエンドポイントへのマッピングが困難 ○ サービス間通信は REST よりも RPC のほうが適している事が多い ● 多言語化 ○ Ruby 以外の言語の使用実績の増加

Slide 7

Slide 7 text

gRPC ● オープンソースの RPC フレームワーク ● IDL として Protocol Buffers が使える ● Protocol Buffers で定義を書くとクライアント、サーバのコードを生成 ● 様々な言語で実装が存在する ● https://grpc.io

Slide 8

Slide 8 text

gRPC(2) ● HTTP2 上で動作 ○ https://github.com/grpc/grpc/blob/v1.17.0-pre2/doc/PROTOCOL-HTTP2.md ● 4種類の RPC ○ Unary RPC ○ Server streaming RPC ○ Client streaming RPC ○ Bidirectional streaming RPC ● Interceptor でログ、認証、エラー処理など

Slide 9

Slide 9 text

定義(Protocol Buffers)の例 https://grpc.io/docs/guides/concepts.html#service-definition

Slide 10

Slide 10 text

gRPC の運用

Slide 11

Slide 11 text

Cookpad の gRPC アプリケーション動作環境 ● 基本的に hako 上で動作 ● cookpad.com でも使用 ● cookpad/sds + Envoy を使用して Client side Loadbalancing ○ https://blog.envoyproxy.io/service-mesh-and-cookpad-ba4d5d915dbd ○ Envoy の load_balancing_weight を使って Slow Start をサポート ● Envoy 経由でリクエストをうける ● 開発環境から staging 環境へのアクセスも許可

Slide 12

Slide 12 text

Cookpad の gRPC 動作環境(サービスイン) cookpad/sds registrator コンテナが service 起動時に front-envoy のエンドポイントを登録 (e.g. 10.0.0.1:77777) app Service A envoy front-envoy サービス名 エンドポイント weight Service B 10.0.0.1:6789 100 Service C 10.0.0.1:7896 100 Service A 10.0.0.1:7777 100 registrator

Slide 13

Slide 13 text

Cookpad の gRPC 動作環境(Slow Start) cookpad/sds app Service A envoy front-envoy サービス名 エンドポイント weight Service B 10.0.0.1:6789 100 Service C 10.0.0.1:7896 100 Service A 10.0.0.1:7777 2 registrator コンテナが weight を徐々に大きくしていく registrator

Slide 14

Slide 14 text

Cookpad の gRPC 動作環境(Running) cookpad/sds 1. 定期的に registrator コンテナが app に health check 2. health check がとおったら sds に再登録 app Service A envoy front-envoy registrator サービス名 エンドポイント weight Service B 10.0.0.1:6789 100 Service C 10.0.0.1:7896 100 Service A 10.0.0.1:7777 100

Slide 15

Slide 15 text

app Service A envoy front-envoy Cookpad の gRPC 動作環境(通信) envoy が定期的に sds を見に 行って リクエスト先を見つける app Service B envoy front-envoy cookpad/sds

Slide 16

Slide 16 text

Cookpad の gRPC 動作環境(サービスアウト) cookpad/sds registrator コンテナが service 終了時に front-envoy のエンドポイントを削除 app Service A envoy front-envoy サービス名 エンドポイント weight Service B 10.0.0.1:6789 100 Service C 10.0.0.1:7896 100 registrator

Slide 17

Slide 17 text

サービス定義(Protocol Buffers) の管理 ● 一つのリポジトリで管理 ● サービスごとにディレクトリを切る運用 ○ service A のサービス定義だったら service_a/ 以下に定義を記述 ● 使用したいアプリケーションは git submodule で使用 ● ckaznocha/protoc-gen-lint による Lint ● pseudomuto/protoc-gen-doc を使って自動でドキュメント生成 ○ hako-console にドキュメントへのリンクを配置 Web アプリケーションを把握するためのコンソール , https://techlife.cookpad.com/entry/2018/04/02/140846

Slide 18

Slide 18 text

メトリクス ● 前述の2つの Envoy コンテナでメトリクスをとっている ○ Envoy Stats https://blog.envoyproxy.io/envoy-stats-b65c7f363342 ○ アクセスログ ● メトリクス、ログ情報を Prometheus に入れており grafana でみれる ● アクセスログに対して google/mtail を使用して Prometheus から読めるように している ● hako-console に grafana へのリンクを配置

Slide 19

Slide 19 text

Prometheus w/ envoy アクセスログ ● サービス開発者が(ほぼ)何もしなくても動作 ● front-envoy コンテナが mtail コンテナをマウント ○ mtail が envoy のアクセスログ Prometheus 用に出力 ○ https://github.com/ganmacs/prom-mtail-test ● ECS 上のサービスディスカバリーサービス(sds)を作成 ○ Prometheus が sds をもとに mtail コンテナを見つける app Service A envoy front-envoy mtail

Slide 20

Slide 20 text

Image Area

Slide 21

Slide 21 text

Image Area

Slide 22

Slide 22 text

Ruby (on Rails) で gRPC

Slide 23

Slide 23 text

grpc gem ● C と C++ で作られたものを Ruby の拡張ライブラリとして使用 ○ grpc/grpc は C++, Python, Ruby, Objective-C, PHP, C# をサポートしている ● grpc gem はマルチスレッド (シングルプロセス) ○ Ruby には GIL があるので並列に動かない ○ マルチプロセス化は直近のロードマップになさそう ■ https://github.com/grpc/grpc/issues/15334 ● https://rubygems.org/gems/grpc/

Slide 24

Slide 24 text

grpc gem(2) ● grpc-tool gem でサーバ、クライアントのコードを生成 ● ユーザはサーバを実装するだけ https://grpc.io/docs/quickstart/ruby.html

Slide 25

Slide 25 text

Ruby で gRPC in Cookpad ● 公式の gRPC 実装を使用 ○ bigcommerce/gruf みたいなフレームワークは使ってない ● アプリケーションレイヤーでアクセスログが取れないので Interceptor を自作 ● シグナルを受けると死んでしまうので起動/停止を行うライブラリを自作 ○ https://github.com/grpc/grpc/issues/14043 ● その他様々な Interceptor を自作 ○ 例外処理 ○ New Relic などのツールもサポートされてないので interceptor として自作 ○ Go の grpc-ecosystem/go-grpc-middleware みたいなもの

Slide 26

Slide 26 text

Ruby on Rails で gRPC in Cookpad ● app/ 以下にサービス定義を配置 ● lib/ 以下に自動生成コードを配置 ● config/initializers に gRPC の設定を配置 ○ Listen ポート、Interceptor の設定など ● 起動には rails runner を使用している ○ e.g. $ bundle exec rails runner run_grpc_server.rb ○ 最初は rake task を作成していたが eager_load 周りでハマりどころが多い ○ https://github.com/rails/rails/blob/v5.2.1/railties/lib/rails/application.rb#L518 ● ActiveRecord の connection pool との相性

Slide 27

Slide 27 text

grpc gem と ActiveRecord ● grpc gem はマルチスレッド(シングルプロセス) ● interceptor で AR の connection pool から pickup して終わったら返す ○ 並行数の上限が connection pool 数になる

Slide 28

Slide 28 text

grpc gem の問題 ● CPU を使い切れない ○ オートスケールが他のアプリに比べてうまくいかない ○ スケールアウトするより先に gRPC の ResourceExhausted エラーが発生する ● Graceful Shutdown がない ○ 急にサーバが死ぬので Envoy を使ってサービスアウトしてからサービスを止めている ○ 当然ストリーム系の Graceful shutdown もない ● その他いろいろ

Slide 29

Slide 29 text

griffin ● 自作の gRPC ライブラリ ● https://github.com/ganmacs/griffin ○ nghttp2 を使用して作成 ● grpc gem との互換性を気にした ○ Protocol Buffers から生成されるコードをそのまま使用できる ○ grpc gem が改善されたら戻れるように ● 社内のアプリで動き出している ○ 現在移行中 ● unary RPC のベンチマークでは griffin のほうが早い ○ https://gist.github.com/ganmacs/5c7857b03ee80cf78e638f2a2a74fe01

Slide 30

Slide 30 text

まとめ ● Cookpad ではサービス間通信に gRPC を使うようになってきた ● Envoy を活用して gRPC サービスの基盤を構成している ● Rails と gRPC を組み合わせて使用している ● 新しい gRPC gem (griffin) を作って検証中

Slide 31

Slide 31 text

今後 ● Envoy の機能を使ったカナリアリリース ● griffin のプロダクション導入 ● grpc-gateway の導入