IoTと業務システムをつなぐgRPC/RESTサービスの開発と運用

by taisuke.fujita | March 30, 2020 updated
automotive | #dena-techcon #gcp #golang #kubernetes #grpc #firestore #redis

はじめに

こんにちは。オートモーティブ事業本部の藤田泰介と吉江智弘です。

DeNA TechCon 2020 にて発表する予定だった内容をBlog記事としてお届けします。
今回は「IoTと業務システムをつなぐgRPC/RESTサービスの開発と運用」と題してお送りします。

Centralサービスの概要と全体のアーキテクチャ(前半)

目次(前半)

  • タクシー配車サービスMOVの紹介
  • MOV Centralサービスの紹介
  • Centralサービスの特徴
  • Centralサービスが扱うデータ
  • Centralサービスのアーキテクチャ

タクシー配車サービスMOVの紹介

サービスインしてから間もなく2年

MOVキービジュアル

MOVキービジュアル

MOVとは

  • 乗りたいときに、スマホひとつで簡単にタクシーが呼べるサービス
  • ITのチカラでタクシー利用を進化させる
  • 提供エリア
    • 神奈川 東京 大阪 京都 兵庫など
  • 車両台数
    • 数万台規模に拡大中

MOVはアプリ配車だけじゃない

  • 後部座席タブレット
  • MOV CALL
  • 決済機

MOVの全体像

MOVの全体像

MOVの全体像

MOVのリアルタイム車両情報を電話配車に活かして事業者様のタクシー配車を最大化したい

そんな中、生まれたMOVの新サービスがCentralサービス

MOV Centralサービスの紹介

MOVの新サービス、Centralサービス

  • タクシー事業者様の無線配車システムとMOVアプリ配車サービスを連携させる
  • 事業者様は既設無線配車システムと同等の機能をMOV乗務員タブレットで利用可能にする
  • MOVの車両ステータス情報を事業者様配車システムから利用できるようにする

Centralサービスのイメージ

従来の無線配車システムはMOVと連携することにより電話配車に専念する

Centralサービスのイメージ

Centralサービスのイメージ

Centralサービスの位置づけ

Centralサービスの位置づけ

Centralサービスの位置づけ

Centralサービスの導入メリット

  • MOV・無線配車システムそれぞれで管理していた車両ステータス情報を一元化できる
  • 無線が廃止され車内の操作機器が1つにまとまるので乗務員様の車内業務がシンプルになる
  • 1つの端末で配車室からの配車受付、アプリからの配車受付が可能になる
  • システム導入コストの削減とアプリ配車による売上アップを見込める

Centralサービスの目指すもの

アプリ配車と電話配車を連携してタクシー事業者様の配車効率を最大化する

Centralサービスの開発

  • 2019年4月から開発開始
  • 2019年11月に試験稼働(2020/2から本稼働)
  • チーム構成
    • PdM、デザイナ
    • アプリエンジニア
    • サーバエンジニア
  • サーバエンジニアは4名
    • Kubernetes未経験者のみ
    • gRPCを使ったAPI開発も全員未経験
    • うち2名は1,2年前までアプリエンジニア

Centralサービスの特徴

  • タクシー事業者様の電話配車システムは事業者ごとに仕様が違う
    • 開発しているシステムベンダーもバラバラ
  • 我々が全事業者様の配車システムの仕様を把握することは不可能に近い
  • いかに汎用的な仕様に落とすかが重要
  • 独自仕様に影響を受けないサービスを作る
  • 独自機能を作り込まない汎用的な仕様に倒す
  • APIが仕様を決めるのではなくAPIに渡ってくるデータが仕様を決める
  • データによって乗務員端末と配車システムが仕様に沿った動きをする
  • Centralサービスはデータが主役

少人数チームで開発スピードを落とさないための戦略

Centralサービスが扱うデータ

  • 大きく分けて3種類
    • マスタデータ
    • メッセージ
    • 車両ステータス

データの流れ

データの流れ

データの流れ

マスタデータとは

  • MOVと電話配車システムで共通に使うデータ
    • 営業所マスタ
    • 車両マスタ
    • 乗務員マスタ
  • Centralサービス独自のデータ
    • 車両状態
    • 定型メッセージ
    • 地区/待機場所

マスタデータの特性

  • MOVと共通のデータはMOVのマスタ管理システム(通称タクモニ)から連携される
  • 乗務員ログイン中のデータ更新はできない
  • データ更新頻度は少ないが連携漏れに注意が必要

メッセージとは

  • 配車室からの指示や端末の状態変更依頼をやりとりするデータ
  • 配車室から乗務員に対する指示
    • 配車指示
    • 通話依頼
    • 業務メッセージ
  • 乗務員から配車室への指示応答や依頼
    • 配車指示への応答
    • 状態変更依頼(休憩など)
    • 待機の申請
  • APIを個別に開発するのではなくメッセージという汎用的なデータをつかってやりとりする
メッセージとは

メッセージとは

メッセージの特性

  • リアルタイム性が重要
  • 取りこぼしのないデータ配信はさらに重要

車両ステータスとは

  • 車両状態を示す情報
    • 位置情報
    • タクシーメーターの状態
    • メーター以外の状態(休憩、開局、閉局 etc)
  • MOVにおける車両ステータス
    • 数秒に1回のペースでサーバに送信する
    • アプリ配車の抽選などに使用する

車両ステータスの特性

  • 車両ステータスはMOVの根幹を支える重要な情報
    • データのリアルタイム性が重要
    • 位置情報は個人情報になり得る場合があるので取り扱いに注意が必要

Centralサービスのアーキテクチャ

技術要素

  • gRPC
  • Google Cloud Endpoints for gRPC
  • Google Kubernetes Engine(GKE)
  • Redis Streams
  • Firestore
  • 開発言語はGo 1.14

データ特性ごとにサービスを分割する

  • マスタデータ
    • マスタ管理サービス
  • メッセージ
    • メッセージングサービス
  • 車両ステータス
    • 車両モニタリングサービス

マスタ管理サービスアーキテクチャ

  • マスタデータはFirestoreに保存する
  • データのCRUD操作用にREST APIを提供する
  • MOVタクモニがデータを追加更新、乗務員端末と配車システムがデータを参照する
  • Extensible Server Proxy(ESP)がREST APIをgRPC APIに変換する
マスタ管理サービスアーキテクチャ

マスタ管理サービスアーキテクチャ

メッセージングサービスアーキテクチャ

  • 電話配車システムと乗務員端末間のメッセージのやりとりを仲介する
  • データのリアルタイム性が重要なためRedis Streamsを使ってメッセージの配信をする
メッセージングサービスアーキテクチャ

メッセージングサービスアーキテクチャ

車両モニタリングサービスアーキテクチャ

  • リアルタイム配信用データと近傍検索データを管理する
  • Redis Streamsを使って車両ステータスをリアルタイムに電話配車システムに配信する
  • 迎車地に近い車両を検索する(近傍検索)ためのデータはMemorystoreに保存する
車両モニタリングサービスアーキテクチャ

車両モニタリングサービスアーキテクチャ

Centralサービスを支える技術と展望(後半)

目次(後半)

  • 前半のおさらい
  • Centralサービスを支える技術
  • Centralサービスのこれまでとこれから
  • まとめ

前半のおさらい

  • Centralサービスに求められる特性
    • 遅延や取りこぼしのないデータ配信
    • 様々なクライアントに対応できる柔軟性
    • 少人数での開発を効率的に進める工夫

Centralサービスを支える技術

Centralサービスを支える技術1

Centralサービスを支える技術1

Centralサービスを支える技術2

Centralサービスを支える技術2

遅延の少ないデータ配信

車両ステータスは数秒に1回のペースでAWS IoTを通して配信される

遅延の少ないデータ配信

遅延の少ないデータ配信

  • サービス間の効率的な通信とは?
    • 一般的なJson/HTTPなどのプロトコルではオーバヘッドが無視できない
    • オーバヘッドを最小化するには、常時接続型のサービスが望ましい
    • gRPC/WebSocket/MQTT/TCPなどを検討

CentralサービスではgRPCをメインプロトコルとして採用

  • gRPCのメリット
    • HTTP/2によるパフォーマンス面のメリット
    • 複数のプログラミング言語のサポート
    • IDLによるスキーマ共有やコード生成のエコシステム

まとめ

  • 遅延の少ないデータ配信の実現
    • gRPCを利用して遅延の少ないデータ配信を実現している

取りこぼしのないデータ配信

  • 車両ステータスとメッセージではデータの特性が異なる
    • 車両ステータスはデータの鮮度が重要
    • メッセージはデータをロストしないことが重要
  • サービス間のメッセージ管理
    • スケーリングやサービスの拡張性は確保したい
    • サービス間のメッセージングは非同期が望ましい
    • メッセージの管理にはメッセージブローカーを採用する
  • メッセージブローカーの選択肢
メッセージブローカーの選択肢

メッセージブローカーの選択肢

Redis Streams

  • Redis 5.0で追加されたデータ型
  • Apache Kafkaによく似たメッセージ処理のための機能を提供

  • 特徴

    • データを時系列でもつ追記型のデータ構造
    • データを取得しても過去のデータを再取得可能
    • コンシューマグループの概念により読み出しの分散が可能
    • XACKコマンドでメッセージの購読をマークできる

CentralサービスではRedis Streamsをメッセージブローカーとして採用

CentralサービスではRedis Streamsをメッセージブローカーとして採用

CentralサービスではRedis Streamsをメッセージブローカーとして採用

まとめ

  • 取りこぼしのないデータ配信の実現
    • Redis Streamsを採用しメッセージの取りこぼしを防いでいる

様々なクライアントへの対応

Protocol Buffersの活用

  • Protocol Buffers (protobuf)
    • Google製の構造化データのシリアライズフォーマット
    • IDLで記述したプロトコル定義ファイルからシリアライズ/デシリアライズ コードを生成
    • 様々なプラットフォーム / 言語で利用できる
    • C++, C#, Dart, Go, Java, Python …
  • protocol buffer compiler (protoc)
    • .protoファイルを読み込みコード生成を行うコンパイラ
    • 各言語向けソースコードをprotocコマンドを利用して生成する
  • Protocol Buffersを活用し様々なクライアントに対応する
    • IDLによるスキーマ共有
    • 様々なプラットフォーム/言語に対応したコード生成

Cloud Endpointsの活用

  • gRPCサービスならではの課題
    • 認証はどのように提供する?
    • クライアントとのドキュメント共有方法
    • 常時接続が必要のないクライアント
    • マスタデータのみを扱うクライアントがgRPCコードを追加実装するのはコスト
  • Cloud Endpointsでできること
    • gRPCに対応したAPI管理サービス
    • HTTP/JSONリクエストをgRPCに変換
    • 認証機能の提供
    • ドキュメントの生成/共有 (後述)
Cloud Endpoints

Cloud Endpoints

  • Cloud Endpointsを利用した認証
    • Cloud EndpointsはJWTを用いたいくつかの認証方法をサポートしている
    • トークンの検証はExtensibleServiceProxy(ESP)が行う
    • endpointsの構成yamlファイルに認証条件を記載するだけで利用できる
endpointsの構成yamlファイル

endpointsの構成yamlファイル

  • JSON/HTTP <-> gRPCの変換
    • Cloud Endpointsはプロトコルのコード変換をサポートしている
    • コード変換はExtensibleServiceProxy(ESP)が行う
    • 利用方法もシンプル
    • .protoファイルにアノテーションを追加する
    • endpointsの構成yamlにマッピングを記載する
JSON/HTTP &lt;-&gt; gRPCの変換

JSON/HTTP <-> gRPCの変換

Centralサービスの認証認可

  • 認証
    • 配車システムや乗務員端末、連携するMOVシステム向けの認証
    • 乗務員端末は紛失や盗難に備えたリスク管理が必要
    • クライアント毎に異なる認証方法を提供する
  • 認可
    • 配車センター同士やドライバー同士で動態情報やメッセージが共有されてはいけない
    • 適切な権限管理が必要

配車システムや社内システム向けの認証

  • 認証
    • Cloud Endpointsの認証機能を利用
    • Google ID Tokenとサービスアカウントを利用してユーザーアカウントを認証
  • 認可
    • 認可部分は個別に実装する必要がある
    • JWTからアカウント情報を参照して権限をチェックするInterceptorを実装
    • トークンの発行者によってチェック内容が切り替える

乗務員端末の認証

  • 配車システムやMOVシステム向けと同様の仕組みは利用できない
    • MOVで管理する乗務員端末の数は数千台
    • 端末1台毎に紛失 / 盗難のリスク管理が必要
    • 独自認証局を構築し権限管理を行う

独自認証局を利用した認証

  • 独自認証局を利用した認証
    • 端末のキッティング時に認証局がクライアント証明書を発行
    • 端末はクライアント証明書を利用して各サービス向けのアクセストークン発行が行える
    • Centralサービスはアクセストークンの内容から車両を識別し権限チェックを行う
  • リスク管理
    • 認証局はクライアント証明書の発行や管理を行う
    • 端末と認証局とのコネクションは1本となるよう制限
    • 問題が発生した場合は証明書を無効にすることでリスク管理が行える
  • クライアント証明書の発行フロー
    1. MOVの車両登録APIを実行し車両トークンを受け取る
    2. 認証局に証明書発行依頼
    3. 認証局はトークンを検証し証明書を発行する
クライアント証明書の発行フロー

クライアント証明書の発行フロー

  • アクセストークンの発行フロー
    1. 認証局にトークン発行依頼
    2. 認証局はクライアント証明書を検証
    3. 発行されたトークンを利用してCentralサービスにアクセス
アクセストークンの発行フロー

アクセストークンの発行フロー

まとめ

  • 様々なクライアントへの対応
    • ProtocolBuffersで様々なクライアント実装に対応している
    • CloudEndpointsでJSON/HTTPとgRPCの選択肢を用意している
    • クライアントに応じた認証方法を提供している
    • CloudEndpointsによる認証
    • 独自認証局による認証

少人数での開発を効率的に進める

Infrastructure as Code

  • インフラ管理のコストは最小化したい
    • インフラ構成をチーム全員が把握している
    • 誰でも環境を立ち上げられる
    • いつでも素早く環境を用意できる
  • Deployment Manager
    • GCPが提供するInfrastructure as Code(IaC)ツール
    • 特徴
    • AWSでいうCloudFormation的なサービス
    • PythonやJinja2でテンプレートの仕組み
    • 状態ファイルを管理しなくてOK
    • Deployment Managerを利用したインフラ設定の自動化
    • base : ネットワークやファイアウォールの設定を管理
    • service : GKEクラスタやNodePoolなどのサービス設定を管理
    • iam : IAMやユーザーアカウント周りの設定を管理
    • Deployment Managerを利用したインフラ設定の自動化
    • コンテキスト毎のコンフィグをマージして最終的な構成情報を生成する
Deployment Manager

Deployment Manager

クライアントとの情報共有

  • クライアントとの情報共有は時間をとられやすい部分
    • APIドキュメントの作成や共有のコスト
    • protoスキーマの共有や生成物の管理
  • APIドキュメントの共有
    • Cloud Endpoints Portalを利用したドキュメントの生成
    • protoファイル情報からドキュメントを生成できる
    • Json/HTTPに対応するAPIはOpenAPI形式でも出力可能
Cloud Endpoints Portalを利用したドキュメントの生成

Cloud Endpoints Portalを利用したドキュメントの生成

  • protoスキーマの共有や生成物の管理
    • Protocol Buffersを運用する際の課題
    • protoファイルのバージョン管理の問題
    • コンパイル場所やコンパイラバージョンの統一
    • コンパイル後のソースコードの配布
  • protoスキーマの共有や生成物の管理
    • .protoファイルの管理やバージョニングはCIで自動化する
    • .protoファイルを管理するリポジトリ
      • .protoファイル自体のバージョン管理
      • 更新があると各言語向けのビルドジョブを実施
    • コンパイル後の生成物を管理するリポジトリ
    • protoファイルのビルド + 配布方法を管理
コンパイル後の生成物を管理するリポジトリ

コンパイル後の生成物を管理するリポジトリ

  • protocプラグインの活用
    • protocプラグインを利用したコード生成で開発を効率化する
    • protocは各言語向けコードを生成するだけのツールではない
    • プラグインによる拡張が可能な設計
protocプラグインの活用

protocプラグインの活用

  • protocプラグインを利用したコード生成で開発を効率化する
    • 既存のprotocプラグインを利用して使いやすいコードを生成する
    • 自分たちのプロジェクトにあったprotocプラグインを自作して利用する

まとめ

  • 少人数での開発を効率的に進める工夫
    • 少人数だからこそ積極的に自動化していく
    • 属人化を防ぎたい部分
    • 繰り返しコミュニケーションが必要な部分
    • 既存の便利な仕組みを積極的に取り入れる
    • protobufのコード生成周辺は便利な仕組みが揃っている
    • 小さな部分でも積み重ねて効率化していく

Centralサービスのこれまでとこれから

これまでの振り返り

  • gRPCのタイムアウトやスケールの問題を解決できた
  • インフラ構築やデプロイ自動化が進んでよかった
  • 新しい技術を使う機会が多くて開発が楽しい
  • 検証のフローやログの整備が後回しになってしまった

これまで解決できたこと

gRPCのロードバランシング

  • gRPCのロードバランシングの問題
gRPCのロードバランシングの問題

gRPCのロードバランシングの問題

  • スケールアウトしても負荷分散しない
スケールアウトしても負荷分散しない

スケールアウトしても負荷分散しない

  • Proxy Model
    • HTTP/2対応のリバースプロキシを配置して負荷分散をする
    • L4 ProxyではgRPCのリクエスト分散が行えないので注意する
  • Balancing-aware Clinet
    • クライアントサイドで接続先サーバの一覧を把握しリクエスト時に負荷分散する
    • 高パフォーマンスだが, バランシングポリシーの分散など考慮事項は多くなる
  • External LoadBalancing Service
    • 上記のハイブリットパターン
    • 外部LBサーバに接続先サーバを問い合わせてからリクエストして負荷分散
  • GKE + gRPC構成のProxy Modelロードバランシング
    • HTTP(S) ロードバランサ
    • GKE IngressControlerが作成するL7LB(HTTP/2対応)
    • GCPサービスとの連携が可能 : ManagedSSL / CloudArmor / CloudCDN
    • Centralサービスでは主にこちらを採用
    • ロードバランサのヘルスチェックに少し工夫が必要なので注意
      • /へのGETリクエストもしくはreadnessProbeをhttpGetで作成する必要がある
      • gRPCサービスの場合はバックエンドをHTTP/HTTP2両受けできるようにすることで対応可能
  • GKE + gRPC構成のProxy Modelロードバランシング
    • TCP/UDPロードバランサ
    • type: LoadBalancer のServiceが作成するL4LB
    • バランシングするには別途EnvoyやIngressControler等のL7プロキシが必要
    • コネクションを1本に絞りたいなど、特定のユースケースではこちらを採用
  • gRPCのロードバランシングの問題
    • Podがスケールアウトしても負荷分散しない場合があるので注意する
    • ロードバランシングは自分たちの目的にあったものを選択する
    • 常時接続型サービスとはいえ、切断を許容できるようにするのも重要
    • 定期的にサーバ側でコネクションをリセットしてリバランスを行う
    • クライアント側は突然の切断や定期的なリセットに対応できるようにしておく
  • gRPCサービスのコネクションの維持の問題
    • grpc-goではKeepaliveはデフォルトで無効なので注意する
    • Client-side Keepaliveの設定が必要
    • サーバーはgraceful shutdownできるようにしておくことも重要
  • 実際は間に入っているLBやプロキシの設定にも影響を受ける
    • 設定の際は実際にやり取りされるフレームを確認しながら設定を行うとよい◎
    • Goの場合は GODEBUG=‘http2debug=2’; を有効にするとframe dumpを出力できる
    • パケットキャプチャツールを使うことも有効
gRPCデバッグ

gRPCデバッグ

今後の取り組み

  • 自動化の取り組みは今後も取り組んでいく
    • デプロイの選択肢を増やす(Blue/Green, Canary)
    • k8sと親和性の高いCDツールの導入
  • 運用を効率化するための仕組みの導入
    • 緊急度は低いが重要度は高いタスク
    • 本番の動態データの再現
    • 運用の実態に合わせたログ, 監視ツールの整備

まとめ

  • 配車アプリ以外のサービスもいろいろ作っています!
  • 新しい技術も積極的に採用しながらアップデートしています!
    • サービスやデータの特性を考えながら技術選定をおこなっています
    • 開発を効率化するための取り組みも大事にしています
  • 統合後もDeNAと連携しながら更にスピードアップしていきます!