Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

GKEとgRPCで実装する多言語対応・スケーラブルな内部API

1,023 views

Published on

2018年5月26日に開催された「Jjugccc2018」での、当社セッション資料です。

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

GKEとgRPCで実装する多言語対応・スケーラブルな内部API

  1. 1. GKEとgRPCで実装する 多言語対応・スケーラブルな内部API 高橋 秀平 2018/5/26 JJUG CCC Spring
  2. 2. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 1 自己紹介 名前: 高橋秀平 所属: 株式会社ブレインパッド 仕事: インターネット広告関連ツール開発 Java歴: 5年くらい
  3. 3. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 2 話すこと インターネット広告API呼び出しサービスを Java + GKE + gRPCで開発している経験を元に、 実際のシステム開発に関するTipsをご紹介します
  4. 4. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 3 アジェンダ • gRPCとは • GKEとは • 実装上のTips • ヘルスチェック • エラーの受け渡し • シャットダウン • Java ⇔ protobuf 変換
  5. 5. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 4 gRPCとは
  6. 6. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 5 gRPCとは • Googleによって開発され、2015年にオープンソース化されたRPCフレームワーク • HTTP/2 による通信 • Protocol Buffer を IDL として採用 • クライアント・サーバともに Java, go, Python 等複数言語に対応 • 高速かつ高機能 • Cloud Native Computing Foundationの6番目のプロジェクト • Google Ads API (beta) では JSON REST形式の他にgRPC形式をサポート
  7. 7. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 6 gRPCとは • 利用の流れ • .protoファイルにサービスやデータの定義を記述する • .protoファイルから各言語のソースコードを生成する • サーバサイド・クライアントサイドを実装する
  8. 8. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 7 サービスやデータの定義 // サービスの定義 service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // リクエスト・レスポンスの型の定義 message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
  9. 9. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 8 サービスやデータの定義 // サービスの定義 service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // リクエスト・レスポンスの型の定義 message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
  10. 10. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 9 サービスやデータの定義 // サービスの定義 service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // リクエスト・レスポンスの型の定義 message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
  11. 11. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 10 .protoファイルから各言語のソースコードを生成する • protoc コマンドを利用 protoc --proto_path=src --java_out=build/gen src/greeter.proto • Mavenを利用している場合は os72 / protoc-jar-maven-plugin なども利用可能
  12. 12. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 11 サーバサイドの実装 @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} }
  13. 13. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 12 サーバサイドの実装 @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } .protoで定義したServiceに対応した クラスが生成される
  14. 14. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 13 サーバサイドの実装 @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } 対応するメソッドをオーバーライド
  15. 15. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 14 サーバサイドの実装 @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } .protoで指定した引数は第一引数 戻り値はメソッドの戻り値ではなく第二引数
  16. 16. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 15 サーバサイドの実装 @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } クライアントからのリクエストは reqからアクセス可能
  17. 17. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 16 サーバサイドの実装 @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } 戻り値はStreamObserverのonNextへ
  18. 18. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 17 クライアントサイドの実装 private ManagedChannel channel = ManagedChannelBuilder .forAddress(HOST, PORT) .usePlaintext(true).build(); private GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); public void greet(String name) { HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply response = stub.sayHello(request); logger.info("Greeting: " + response.getMessage()); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} }
  19. 19. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 18 クライアントサイドの実装 private ManagedChannel channel = ManagedChannelBuilder .forAddress(HOST, PORT) .usePlaintext(true).build(); private GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); public void greet(String name) { HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply response = stub.sayHello(request); logger.info("Greeting: " + response.getMessage()); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } アクセス先や通信方法を表す ManagedChannelオブジェクトを生成
  20. 20. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 19 クライアントサイドの実装 private ManagedChannel channel = ManagedChannelBuilder .forAddress(HOST, PORT) .usePlaintext(true).build(); private GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); public void greet(String name) { HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply response = stub.sayHello(request); logger.info("Greeting: " + response.getMessage()); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } サービスのスタブを作成
  21. 21. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 20 クライアントサイドの実装 private ManagedChannel channel = ManagedChannelBuilder .forAddress(HOST, PORT) .usePlaintext(true).build(); private GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); public void greet(String name) { HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply response = stub.sayHello(request); logger.info("Greeting: " + response.getMessage()); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } リクエストのオブジェクトを 構築
  22. 22. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 21 クライアントサイドの実装 private ManagedChannel channel = ManagedChannelBuilder .forAddress(HOST, PORT) .usePlaintext(true).build(); private GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); public void greet(String name) { HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply response = stub.sayHello(request); logger.info("Greeting: " + response.getMessage()); } // ※ 参考 service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } rpc呼び出し
  23. 23. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 22 GKEとは
  24. 24. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 23 GKEとは • コンテナ化されたアプリケーションをデプロイするためのマネージド環境 • Kubernetesのクラスタを自動でセットアップしてくれる • GCPとのネイティブな連携 • アクセス制御 • モニタリング・ロギング • ネットワーク・ファイアウォール・ロードバランサ 等
  25. 25. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 24 GKEとは Node Node Node Pod Container Container Cluster Pod Container Container Pod Container
  26. 26. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 25 GKEとは Node Node Node Pod Container Container Cluster Pod Container Container Pod Container Container … Kubernetes上で実行する Dockerコンテナ
  27. 27. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 26 GKEとは Node Node Node Pod Container Container Cluster Pod Container Container Pod Container Pod … クラスタ上で動作するプロセスに対応 いくつかのコンテナをまとめられる
  28. 28. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 27 GKEとは Node Node Node Pod Container Container Cluster Pod Container Container Pod Container Node … Podが動作する環境 VMだったり物理的なマシンだったり
  29. 29. 実装上のTips
  30. 30. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 29 ヘルスチェック
  31. 31. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 30 ヘルスチェック • Kubernetesでは2種類のヘルスチェック機能が存在 • Liveness チェック … プロセスが稼働しているか • 失敗したらPodは再起動 • Readiness チェック … リクエストを受け入れる準備が整ったか • 成功するまでリクエストが割り当てられない • httpのリクエストかコマンド実行によるチェックしかサポートされていない • → gRPCでのチェックはサポートされていない… • → ヘルスチェック用のEndPointを用意→gRPCの動作チェック • LogNet / grpc-spring-boot-starter を利用するとgRPCとHTTP のサーバを簡単に立てられて便利
  32. 32. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 31 ヘルスチェック @RestController @RequestMapping("/health") public class HealthCheck { @RequestMapping(method = RequestMethod.GET) public String get() { // gRPCのサービス呼び出し stub.healthCheck(); return "OK"; } }
  33. 33. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 32 ヘルスチェック @RestController @RequestMapping("/health") public class HealthCheck { @RequestMapping(method = RequestMethod.GET) public String get() { // gRPCのサービス呼び出し stub.healthCheck(); return "OK"; } } Readinessチェックと Livenessチェックの内容は同じ
  34. 34. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 33 ヘルスチェック @RestController @RequestMapping("/health") public class HealthCheck { @RequestMapping(method = RequestMethod.GET) public String get() { // gRPCのサービス呼び出し stub.healthCheck(); return "OK"; } } spring-boot-web-starter → 8080 grpc-spring-boot-starter → 6565
  35. 35. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 34 ヘルスチェック @RestController @RequestMapping("/health") public class HealthCheck { @RequestMapping(method = RequestMethod.GET) public String get() { // gRPCのサービス呼び出し stub.healthCheck(); return "OK"; } } ここで(DBアクセス等を伴う) gRPCサービスの呼び出し
  36. 36. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 35 ヘルスチェック spec: template: spec: containers: - livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 200 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 5
  37. 37. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 36 ヘルスチェック spec: template: spec: containers: - livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 200 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 5 Livenessチェックの設定
  38. 38. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 37 ヘルスチェック spec: template: spec: containers: - livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 200 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 5 initialDelaySeconds … 200秒待ってから 最初のチェックを行う periodSeconds … チェックの間隔
  39. 39. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 38 ヘルスチェック spec: template: spec: containers: - livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 200 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 5 Readinessチェックの設定
  40. 40. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 39 エラーの受け渡し
  41. 41. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 40 エラーの受け渡し • StreamObserver.onErrorを利用する public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) { try { // … } catch (Exception e) { responseObserver.onError(Status.INTERNAL .withDescription(“an error occurred”) .withCause(e) // causeはクライアントへは送信されない .asException()); } }
  42. 42. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 41 エラーの受け渡し 利用可能なステータス (io.grpc.Status) OK CANCELLED UNKNOWN INVALID_ARGUMENT DEADLINE_EXCEEDED NOT_FOUND ALREADY_EXISTS PERMISSION_DENIED RESOURCE_EXHAUSTED FAILED_PRECONDITION ABORTED OUT_OF_RANGE UNIMPLEMENTED INTERNAL UNAVAILABLE DATA_LOSS UNAUTHENTICATED
  43. 43. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 42 エラーの受け渡し * クライアント側ではRpcErrorの例外として受け取れる (Pythonの場合) try: stub.SayHello(request) Except grpc.RpcError as e: e.code() # => StatusCode.INTERNAL e.details() # => “an error accurred”
  44. 44. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 43 シャットダウン
  45. 45. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 44 シャットダウン • Podは様々なタイミングで終了する • Rolling update, オートスケール, kubectlコマンド → 処理中のリクエストが完了してからシャットダウンしたい!
  46. 46. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 45 シャットダウン •方針 • gRPCのリクエストにタイムアウトを設定する(例: 60秒) • ※ gRPCではクライアント側でタイムアウトを設定 • PodのterminationGracePeriodSecondsを長めに設定する (例: 90秒) • 各コンテナのpreStopでタイムアウトまで待つ
  47. 47. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 46 シャットダウン • gRPCのリクエストにタイムアウトを設定する public void greet(String name) { HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply response = stub.withDeadlineAfter(60L, TimeUnit.SECONDS) .sayHello(request); logger.info("Greeting: " + response.getMessage()); }
  48. 48. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 47 シャットダウン • PodのterminationGracePeriodSecondsを長めに設定する(例: 90秒) • 各コンテナのpreStopでタイムアウトまで待つ spec: template: spec: terminationGracePeriodSeconds: 90 containers: lifecycle: preStop: exec: command: ["sh", "-c", "sleep 60"]
  49. 49. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 48 シャットダウン リクエスト sleep 終了処理 60sec Pod削除フロー開始 90sec Pod削除フロー開始から 90秒経つとkill 削除フロー開始前に受け付けた リクエストはsleep中にタイムアウト
  50. 50. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 49 オブジェクトの変換
  51. 51. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 50 Java <-> protobuf 変換 • 既存のシステムにgRPCの機能を追加したかった • 既存クラス • → .proto定義 • → javaコード生成 • → 既存クラスと.protoから生成されたクラス間のコンバート • ⇒ 既存クラスから.protoファイルとコンバータを自動生成する処理を自前で実装した • BAData / protobuf-converter などもある 既存クラス .proto 生成された クラス
  52. 52. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 51 Java <-> protobuf 変換 • Protocol Bufferには継承を表現するような機能はない • “Don't go looking for facilities similar to class inheritance, though – protocol buffers don't do that.” – Protocol Buffers / Basics: Java
  53. 53. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 52 Java <-> protobuf 変換 nullの扱いに注意 • Protocol Bufferのmessage型はuser.hasBirthDay()のようにhas{フィールド名}で値を持 つか知ることができる • → false なら null • 文字列や数値などのプリミティブな型はhas{フィールド名}が利用できない • → wrappers.proto • Enumもhas{フィールド名}が利用できない • → .protoファイルを生成する際にnullを表す要素も生成する // wrappers.proto message StringValue { // The string value. string value = 1; } message Sample { string s1 = 1; StringValue s2 = 2; }
  54. 54. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 53 まとめ • gRPCとGKEについて紹介 • 実際の開発で工夫した点や苦労した点を紹介 • ヘルスチェック, エラー処理, シャットダウン, 変換処理… • 日本語、JavaでのgRPC, GKEの情報が増える一助になれば!
  55. 55. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 54 最後に We Are Hiring!
  56. 56. 本資料は、未刊行文書として日本及び各国の著作権法に基づき保護されております。本資料には、株式会社ブレインパッド所有の特定情報が 含まれており、これら情報に基づく本資料の内容は、御社以外の第三者に開示されること、また、本資料を評価する以外の目的で、その一部ま たは全文を複製、使用、公開することは、禁止されています。また、株式会社ブレインパッドによる書面での許可なく、それら情報の一部または全 文を使用または公開することは、いかなる場合も禁じられております。 株式会社ブレインパッド 〒108-0071 東京都港区白金台3-2-10 白金台ビル TEL:03-6721-7002 FAX:03-6721-7010 www.brainpad.co.jp info@brainpad.co.jp Analytics Innovation Company

×