GKEとgRPCで実装する
多言語対応・スケーラブルな内部API
高橋 秀平
2018/5/26 JJUG CCC Spring
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
1
自己紹介
名前: 高橋秀平
所属: 株式会社ブレインパッド
仕事: インターネット広告関連ツール開発
Java歴: 5年くらい
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
2
話すこと
インターネット広告API呼び出しサービスを
Java + GKE + gRPCで開発している経験を元に、
実際のシステム開発に関するTipsをご紹介します
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
3
アジェンダ
• gRPCとは
• GKEとは
• 実装上のTips
• ヘルスチェック
• エラーの受け渡し
• シャットダウン
• Java ⇔ protobuf 変換
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
4
gRPCとは
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形式をサポート
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
6
gRPCとは
• 利用の流れ
• .protoファイルにサービスやデータの定義を記述する
• .protoファイルから各言語のソースコードを生成する
• サーバサイド・クライアントサイドを実装する
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;
}
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;
}
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;
}
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 なども利用可能
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) {}
}
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に対応した
クラスが生成される
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) {}
}
対応するメソッドをオーバーライド
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で指定した引数は第一引数
戻り値はメソッドの戻り値ではなく第二引数
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からアクセス可能
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へ
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) {}
}
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オブジェクトを生成
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) {}
}
サービスのスタブを作成
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) {}
}
リクエストのオブジェクトを
構築
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呼び出し
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
22
GKEとは
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
23
GKEとは
• コンテナ化されたアプリケーションをデプロイするためのマネージド環境
• Kubernetesのクラスタを自動でセットアップしてくれる
• GCPとのネイティブな連携
• アクセス制御
• モニタリング・ロギング
• ネットワーク・ファイアウォール・ロードバランサ 等
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
24
GKEとは
Node Node Node
Pod
Container
Container
Cluster
Pod
Container
Container
Pod
Container
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
25
GKEとは
Node Node Node
Pod
Container
Container
Cluster
Pod
Container
Container
Pod
Container
Container … Kubernetes上で実行する
Dockerコンテナ
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
26
GKEとは
Node Node Node
Pod
Container
Container
Cluster
Pod
Container
Container
Pod
Container
Pod … クラスタ上で動作するプロセスに対応
いくつかのコンテナをまとめられる
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
27
GKEとは
Node Node Node
Pod
Container
Container
Cluster
Pod
Container
Container
Pod
Container
Node … Podが動作する環境
VMだったり物理的なマシンだったり
実装上のTips
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
29
ヘルスチェック
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
30
ヘルスチェック
• Kubernetesでは2種類のヘルスチェック機能が存在
• Liveness チェック … プロセスが稼働しているか
• 失敗したらPodは再起動
• Readiness チェック … リクエストを受け入れる準備が整ったか
• 成功するまでリクエストが割り当てられない
• httpのリクエストかコマンド実行によるチェックしかサポートされていない
• → gRPCでのチェックはサポートされていない…
• → ヘルスチェック用のEndPointを用意→gRPCの動作チェック
• LogNet / grpc-spring-boot-starter を利用するとgRPCとHTTP
のサーバを簡単に立てられて便利
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";
}
}
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チェックの内容は同じ
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
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サービスの呼び出し
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
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チェックの設定
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 … チェックの間隔
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チェックの設定
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
39
エラーの受け渡し
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());
}
}
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
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”
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
43
シャットダウン
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
44
シャットダウン
• Podは様々なタイミングで終了する
• Rolling update, オートスケール, kubectlコマンド
→ 処理中のリクエストが完了してからシャットダウンしたい!
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
45
シャットダウン
•方針
• gRPCのリクエストにタイムアウトを設定する(例: 60秒)
• ※ gRPCではクライアント側でタイムアウトを設定
• PodのterminationGracePeriodSecondsを長めに設定する
(例: 90秒)
• 各コンテナのpreStopでタイムアウトまで待つ
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());
}
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"]
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
48
シャットダウン
リクエスト
sleep 終了処理
60sec
Pod削除フロー開始
90sec
Pod削除フロー開始から
90秒経つとkill
削除フロー開始前に受け付けた
リクエストはsleep中にタイムアウト
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
49
オブジェクトの変換
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
50
Java <-> protobuf 変換
• 既存のシステムにgRPCの機能を追加したかった
• 既存クラス
• → .proto定義
• → javaコード生成
• → 既存クラスと.protoから生成されたクラス間のコンバート
• ⇒ 既存クラスから.protoファイルとコンバータを自動生成する処理を自前で実装した
• BAData / protobuf-converter などもある
既存クラス .proto
生成された
クラス
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
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;
}
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
53
まとめ
• gRPCとGKEについて紹介
• 実際の開発で工夫した点や苦労した点を紹介
• ヘルスチェック, エラー処理, シャットダウン, 変換処理…
• 日本語、JavaでのgRPC, GKEの情報が増える一助になれば!
Analytics Innovation Company
©BrainPad Inc.
Strictly Confidential
54
最後に
We Are Hiring!
本資料は、未刊行文書として日本及び各国の著作権法に基づき保護されております。本資料には、株式会社ブレインパッド所有の特定情報が
含まれており、これら情報に基づく本資料の内容は、御社以外の第三者に開示されること、また、本資料を評価する以外の目的で、その一部ま
たは全文を複製、使用、公開することは、禁止されています。また、株式会社ブレインパッドによる書面での許可なく、それら情報の一部または全
文を使用または公開することは、いかなる場合も禁じられております。
株式会社ブレインパッド
〒108-0071 東京都港区白金台3-2-10 白金台ビル
TEL:03-6721-7002 FAX:03-6721-7010
www.brainpad.co.jp info@brainpad.co.jp
Analytics Innovation Company

More Related Content

PDF
Apache Struts2 における任意の Java メソッド実行の脆弱性
PPT
Struts2を始めよう!
PPTX
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
PPT
Inside mobage platform
PDF
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
KEY
Google App Engine for Java
PDF
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
PPTX
WebRTC on Native App
Apache Struts2 における任意の Java メソッド実行の脆弱性
Struts2を始めよう!
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Inside mobage platform
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
Google App Engine for Java
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
WebRTC on Native App

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

PPT
PDF
ソーシャルアプリ勉強会(第一回資料)配布用
PPTX
Selenium webdriver使ってみようず
PDF
勉強会force#4 Chatter Integration
PDF
Spring BootでHello Worldのその先へ
PDF
System3 search
PDF
Spring integration概要
PDF
Elasticsearchプラグインの作り方
PPTX
JapanDreamin24_はじめてのGraphQL×LWC.pptx
PDF
OpenShiftでJBoss EAP構築
PPTX
NGINX New Features (Japanese Webinar)
PDF
Container Storage Interface のすべて
PDF
Kubernete Meetup Tokyo #18 - Kubebuilder/controller-runtime 入門
PDF
AWS AmplifyとBedrockを活用した生成AIアプリの開発@第6回 FlutterGakkai
PDF
Pro aspnetmvc3framework chap15
PPTX
gRPCurlDotNet.pptx
PDF
Dartでサーバレスサービス
PPTX
Angular js はまりどころ
PDF
PSGIへの誘い
PDF
Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring
ソーシャルアプリ勉強会(第一回資料)配布用
Selenium webdriver使ってみようず
勉強会force#4 Chatter Integration
Spring BootでHello Worldのその先へ
System3 search
Spring integration概要
Elasticsearchプラグインの作り方
JapanDreamin24_はじめてのGraphQL×LWC.pptx
OpenShiftでJBoss EAP構築
NGINX New Features (Japanese Webinar)
Container Storage Interface のすべて
Kubernete Meetup Tokyo #18 - Kubebuilder/controller-runtime 入門
AWS AmplifyとBedrockを活用した生成AIアプリの開発@第6回 FlutterGakkai
Pro aspnetmvc3framework chap15
gRPCurlDotNet.pptx
Dartでサーバレスサービス
Angular js はまりどころ
PSGIへの誘い
Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring
Ad

More from BrainPad Inc. (20)

PDF
Oss LT会_20210203
PDF
Business utilization of real estate image classification system using deep le...
PDF
ブレインパッドにおける機械学習プロジェクトの進め方
PDF
機械学習システムのアーキテクチャアラカルト
PDF
機械学習システム開発案件の事例紹介
PDF
れこめん道~とあるエンジニアの苦闘の日々
PDF
DMPの分析機能を実現する技術
PDF
機械学習システムを受託開発 する時に気をつけておきたい事
PDF
システム開発素人が深層学習を用いた画像認識で麻雀点数計算するLINEbotを作ったハナシ
PDF
Python研修の作り方 - teaching-is_learning-
PDF
2018 builderscon airflowを用いて、 複雑大規模なジョブフロー管理 に立ち向かう
PDF
2018.08.21-機械学習工学研究会 現場を交えた勉強会発表資料
PDF
実証実験報告セミナー資料 20180328(抜粋版)
PPTX
エンジニア勉強会資料_⑥エンジニアが主導する組織マネジメントや開発体制の継続的改善
PDF
エンジニア勉強会資料_⑤広告プロダクトとプラットフォームの開発
PDF
エンジニア勉強会資料_④Rtoaster×Myndエンジンによる興味キーワード分析機能開発事例
PDF
エンジニア勉強会資料_③Rtoasterの11年
PDF
エンジニア勉強会資料_②エンジニア・デザイナ・プロダクトオーナーが推薦するプロトタイプドリブン開発
PDF
エンジニア勉強会資料_①ブレインパッドの中で僕たちは何を開発しているのか?
PDF
Big Data Analytics Tokyo講演資料
Oss LT会_20210203
Business utilization of real estate image classification system using deep le...
ブレインパッドにおける機械学習プロジェクトの進め方
機械学習システムのアーキテクチャアラカルト
機械学習システム開発案件の事例紹介
れこめん道~とあるエンジニアの苦闘の日々
DMPの分析機能を実現する技術
機械学習システムを受託開発 する時に気をつけておきたい事
システム開発素人が深層学習を用いた画像認識で麻雀点数計算するLINEbotを作ったハナシ
Python研修の作り方 - teaching-is_learning-
2018 builderscon airflowを用いて、 複雑大規模なジョブフロー管理 に立ち向かう
2018.08.21-機械学習工学研究会 現場を交えた勉強会発表資料
実証実験報告セミナー資料 20180328(抜粋版)
エンジニア勉強会資料_⑥エンジニアが主導する組織マネジメントや開発体制の継続的改善
エンジニア勉強会資料_⑤広告プロダクトとプラットフォームの開発
エンジニア勉強会資料_④Rtoaster×Myndエンジンによる興味キーワード分析機能開発事例
エンジニア勉強会資料_③Rtoasterの11年
エンジニア勉強会資料_②エンジニア・デザイナ・プロダクトオーナーが推薦するプロトタイプドリブン開発
エンジニア勉強会資料_①ブレインパッドの中で僕たちは何を開発しているのか?
Big Data Analytics Tokyo講演資料
Ad

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

  • 2. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 1 自己紹介 名前: 高橋秀平 所属: 株式会社ブレインパッド 仕事: インターネット広告関連ツール開発 Java歴: 5年くらい
  • 3. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 2 話すこと インターネット広告API呼び出しサービスを Java + GKE + gRPCで開発している経験を元に、 実際のシステム開発に関するTipsをご紹介します
  • 4. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 3 アジェンダ • gRPCとは • GKEとは • 実装上のTips • ヘルスチェック • エラーの受け渡し • シャットダウン • Java ⇔ protobuf 変換
  • 5. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 4 gRPCとは
  • 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 6 gRPCとは • 利用の流れ • .protoファイルにサービスやデータの定義を記述する • .protoファイルから各言語のソースコードを生成する • サーバサイド・クライアントサイドを実装する
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 22 GKEとは
  • 24. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 23 GKEとは • コンテナ化されたアプリケーションをデプロイするためのマネージド環境 • Kubernetesのクラスタを自動でセットアップしてくれる • GCPとのネイティブな連携 • アクセス制御 • モニタリング・ロギング • ネットワーク・ファイアウォール・ロードバランサ 等
  • 25. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 24 GKEとは Node Node Node Pod Container Container Cluster Pod Container Container Pod Container
  • 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 26 GKEとは Node Node Node Pod Container Container Cluster Pod Container Container Pod Container Pod … クラスタ上で動作するプロセスに対応 いくつかのコンテナをまとめられる
  • 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だったり物理的なマシンだったり
  • 30. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 29 ヘルスチェック
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 39 エラーの受け渡し
  • 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. 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. 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 43 シャットダウン
  • 45. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 44 シャットダウン • Podは様々なタイミングで終了する • Rolling update, オートスケール, kubectlコマンド → 処理中のリクエストが完了してからシャットダウンしたい!
  • 46. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 45 シャットダウン •方針 • gRPCのリクエストにタイムアウトを設定する(例: 60秒) • ※ gRPCではクライアント側でタイムアウトを設定 • PodのterminationGracePeriodSecondsを長めに設定する (例: 90秒) • 各コンテナのpreStopでタイムアウトまで待つ
  • 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. 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 48 シャットダウン リクエスト sleep 終了処理 60sec Pod削除フロー開始 90sec Pod削除フロー開始から 90秒経つとkill 削除フロー開始前に受け付けた リクエストはsleep中にタイムアウト
  • 50. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 49 オブジェクトの変換
  • 51. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 50 Java <-> protobuf 変換 • 既存のシステムにgRPCの機能を追加したかった • 既存クラス • → .proto定義 • → javaコード生成 • → 既存クラスと.protoから生成されたクラス間のコンバート • ⇒ 既存クラスから.protoファイルとコンバータを自動生成する処理を自前で実装した • BAData / protobuf-converter などもある 既存クラス .proto 生成された クラス
  • 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. 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. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 53 まとめ • gRPCとGKEについて紹介 • 実際の開発で工夫した点や苦労した点を紹介 • ヘルスチェック, エラー処理, シャットダウン, 変換処理… • 日本語、JavaでのgRPC, GKEの情報が増える一助になれば!
  • 55. Analytics Innovation Company ©BrainPad Inc. Strictly Confidential 54 最後に We Are Hiring!
  • 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