こんにちは LOVOT の基盤となるサービスを Go で書いているチームのひとり id:atotto です。
gRPC や Protocol Buffers (以下 protobuf)、と聞くとクラウドサービスで使うマイクロサービス間のメッセージングで活用するイメージが多いかと思います。もちろん、 LOVOTのクラウドサービスで gRPC を活用した開発 をしています。 本稿では、クラウドサービスではなく、 LOVOT 内部のサービス開発においても gRPC 、 protobuf を活用している事例を紹介します!
対象読者:
- Web APIを設計・実装しているひと
- 組み込みLinuxのデバイスにAPIをもつサービスを作っている、または、作りたいひと
- gRPC を知っていて、活用事例を見たいひと
gRPC / Protocol Buffers
gRPC についてはさまざまな説明資料がでているのでここでは簡単に説明します。 gRPC は異なるサービス間で RPC(Remote Procedure Call)を実現するための仕組みで、現在ではマイクロサービスの内部メッセージングや、サービスのAPIとして幅広く利用されるようになりました。
gRPC では、メッセージやサービスを定義するために、 protobuf を IDL (インタフェース記述言語) として、データ構造やサービスの RPC のメソッドを protoファイルへ記述します。そして、 protoc (コンパイラ)の gRPC プラグインで、 protoファイルから様々なプログラミング言語のコードが生成できるようになります。
LOVOT での活用パターンとしては:
- LOVOT内部の通信
- LOVOT と Nest 間の通信
- LOVOT / Nest の HTTP API サービス
- 互換性を重視したサービス
などがあります。順番に見ていきましょう!
LOVOT内部の通信
システム間のデータストリーム
LOVOT のサービスは、 x86系のCPU(メインコンピュータ)、そして、ARM系のCPU(サブコンピュータ)上で Linux を動かしています。サブコンピュータに数多くのMCU(マイコン)を接続し、各MCUにセンサやアクチュエータなどを接続しています。 接続したセンサから上がってくるデータは、サブコンピュータを通じ、メインコンピュータで処理します。また、アクチュエータ(サーボ、モーターなど)へ指示するために、指示値をメインコンピュータからサブコンピュータ、そして各MCUへと伝えていきます。(MCU、センサ、アクチュエータの詳細については 「LOVOTを動かすファームウェア」 を参照)
LOVOTを動かすファームウェア より抜粋
このメインコンピュータとサブコンピュータ間のやりとりで gRPC の stream を活用しています:
- サブコンピュータでセンサデータ(入力)とりまとめ、メインコンピュータへ伝送する
- メインコンピュータからアクチュエータ指示値(出力)をサブコンピュータへ伝送する
protobuf により、 JSON や XML に比べるととても効率的なシリアライズとなっています。また stream は容易に双方向通信できるため、入出力処理にマッチしました。
疎結合なアーキテクチャ
LOVOT のシステム内ではさまざまなプログラミング言語でサービスを実装しています。そのため、例えば特定のセンサデータを取得したい、といった API を使いたい場合、その API のクライアントライブラリを各プログラミング言語で提供する必要が出てきます。 このような API を gRPC の根幹となる protobuf の IDL で API を定義しているため、各プログラミング言語向けにクライアントライブラリを容易に生成できるようになります。
参考: PyConJP 2019 らぼっと開発におけるPythonとGo より抜粋
独自プロトコルの提供
LOVOT のシステム内では LOVOT を動かすために独自のプロトコルを用意しています。 gRPC のコードを protoc から生成するのと同じように、 protobuf の IDL から独自のプロトコルのコードを生成するコードを実装し、 protoc のプラグインとして実行しています。protoc プラグインの作り方については 「protocプラグインの書き方」 が参考になると思います。
LOVOT と Nest 間の通信
LOVOT の内部の話からやや離れますが、 LOVOT のプロダクトでは、 LOVOT 本体だけではなく、Nest(巣) があり、3つの CPU が存在しています(3つの CPU の話は 「LOVOTのOSのつくりかた」を参照)。
LOVOTとNest(LOVOT ANATOMY より一部抜粋)
LOVOT は Nest をアクセスポイントとした WiFi で接続しています。 WiFiネットワークを介し、 LOVOT と Nest 間のメッセージのやり取りも gRPC を利用し、 TLS 通信しています。 gRPC のサーバを Go で書いていますが、 TLS の暗号化処理が容易に実装できるのはありがたいです。
LOVOT / Nest の HTTP API サービス
LOVOT や Nest には数多くの AP Iを実装しています。一般には公開していませんが、LOVOT や Nest を生産する上で欠かせない組み立て後の検査のための API や、検査用の WebUI があります。( LOVOT の生産に関する話は 「LOVOTがつくられて、お客様に届くまでの仕組み」 を参照) このような API の定義に protobuf の IDL を利用し、さらに grpc-gateway を活用して REST ライクな API を実装しています。
互換性を重視したサービス
gRPC を使うメリットのひとつとして互換性の維持、があります。 LOVOTのアップデートは LOVOTOS という OS を入れ替える仕組みがあり、アップデートの指示を出すクライアントと、アップデートを実行するサーバが存在しています。gRPC/protobuf をつかうことで、例えばアップデート前とアップデート後の LOVOT の RPC のメッセージフィールドに変更が入っていたとしても、メッセージ自体を破壊すること無くリクエストとレスポンスができます。また、サービス自体に大きな変更があったとしても、gRPC の定義では簡単にバージョニングができます。成長するサービスを支える上では必要不可欠な要素です。
gRPCの採用の歴史
かなり端折っていますが、参考になれば幸いです。
2017
LOVOT の開発で ROS(ROS1) をつかっていたため、メッセージングには ROS の pubsub をメインで利用していました。
2018
gRPC、 protobuf のしくみを取り入れはじめたのは、 2018/04 頃です。最初は LOVOT に対する API の定義と、 LOVOT が利用するクラウドの API を定義するところからでした。この頃まではクラウドのサービス自体は OpenAPI で定義し、 Go で HTTP サーバの実装をしていましたが、徐々に protobuf での記述と gRPC へ移行していきます。
2019
脱ROS *1 を進める一環で、効率的なメッセージングへの置き換え先として gRPC を採用する箇所が増えていきました*2。 また、 LOVOT 自体のソフトウェアアップデートサービスに gRPC を適用して実装していきました。
2019-12
LOVOT 出荷開始!
個人的に感じているメリット
id:atotto が感じているメリットをこちらにメモしておきます! ( LOVOT のシステム開発に限らず、クラウドでの開発の感想も含みます)
- API 定義がきちっと決まる(型がある安心感)
- すばらしい設計指針がある
- protocol buffer の効率的なシリアライズが使える
- proto ファイルから APIドキュメントを生成できる
- protoc を通じたプラグインが簡単に作れる(コードジェネレータやアノテーション付与など)
- gRPC + Go の効率的な開発エコシステムが使える
最後に
Web API を支える技術のひとつ、となったと言える gRPC を、このような組み込み Linux のシステム上で活用することで、効率的、かつ、変更に強いシステムとして構築できるようになりました。クラウドも LOVOT も Go で開発しているサービスが多いこともあり、 API 定義で型がきちっと決まり、クライアントとサーバが実装しやすい gRPC のエコシステムは開発フローとしてとても便利です。Web やクラウド上での技術を、組み込みの世界で活用したい方はぜひ一緒に開発を進めましょう!
最後に、このような技術選定と実践を自分たちで進められることはとてもすばらしいことです。この仕組みを共に開発したメンバに感謝いたします。 id:atotto
*1:100億円集めた日本最大のロボットベンチャー、GROOVE Xのロボットを大解剖/ソフトの半数はGo言語製、脱ROS、クラウドはGCP : 2020.03.10 日経Robotics
*2:ROS2 で gRPC / protobuf を採用すればよかったのに(個人の感想です)