Protocol Buffer と gRPC を Go で使うときのリポジトリを整理する
(=˘ ꒳ ˘=) google と golang がぱっと見ややこしい...
Protocol Buffer と gRPC を Go で利用しようとすると色んなリポジトリからツールを落としてくる必要がありますがどれも微妙に字面が似ているので混乱します。それらを整理するために org/repo の形式で一覧できるようにまとめていきたいと思います。
まず Protocol Buffer のコアなツールが含まれるリポジトリが
- google/protobuf
- C++ 製の Protocol Buffer のコア
- 含まれるツール: protoc
- 日付型などの well-known 型の
.proto
定義も含まれる
ですね。Go の場合はこれに加えて Protocol Buffer と gRPC の Go 実装のためのパッケージが必要になります。
.proto
定義からコードを生成したり、そのコードから import
されるパッケージが以下のリポジトリに含まれます。
- golang/protobuf
- 含まれるツール: protoc-gen-go とその grpc プラグイン
- 生成された
.pb.go
のコードからimport
される Protocol Buffer の Go 実装 - 日付型などの well-known 型の Go 実装も含まれる
- その日付型と time パッケージの日付型を変換するためのユーティリティなども含まれる
- grpc/grpc-go
C++やPHPなど Go 以外の言語だと grpc のメインのリポジトリでその辺が間に合ってしまいそうです。
上に書いた通り grpc_cli は便利なので Go でもこのリポジトリの一部機能は利用する、という感じです。
...ややこしいですね。
まだ書き出しただけで、プラグインやら well-known 型の定義や実装がどこにあるかやらが整理できているとは言い難いですが、使っているうちに慣れるのでしょうか...
gRPC で日付型を利用する
(=˘ ꒳ ˘=) gRPC の import 周りを整理したい...
という記事で go-proto-validators と grpc_cli をうまく組み合わせられないことに対して試行錯誤してました。
その問題の切り分けがてら、今回はもう少し基本的な日付型の利用を通して gRPC の import 周りを整理していきたいと思います。
- プロジェクト: https://github.com/asa-taka/hello-validated-grpc
- 日付型を組み込んだコミット: https://github.com/asa-taka/hello-validated-grpc/commit/4d3d2251f5da298f6d64534b0aa7fdc29da63914
hello.proto をベースに日付型をレスポンスのタイムスタンプとして追加しています。
syntax = "proto3"; package hello; import "google/protobuf/timestamp.proto"; service GreetingService { rpc Hello(GreetingRequest) returns (GreetingResponse); } message GreetingRequest { string name = 1; } message GreetingResponse { string message = 1; google.protobuf.Timestamp date = 2; }
注意点としては Timestamp
ではなくて google.protobuf.Timestamp
と指定するところくらいです。
サーバ側の実装は今回も Go で、日付型の変換に https://godoc.org/github.com/golang/protobuf/ptypes#TimestampProto を利用しています。
Go の time
パッケージの日付型はそのままだと gRPC 用に吐き出されたコードには利用できません。
詳しい実装は冒頭のコミットのリンクをご参照ください。
で、リフレクションサービスは組み込み済みで肝心の grpc_cli の動作確認ですが...
$ grpc ls -l localhost:10000 filename: grpc_reflection_v1alpha/reflection.proto package: grpc.reflection.v1alpha; service ServerReflection { rpc ServerReflectionInfo(stream grpc.reflection.v1alpha.ServerReflectionRequest) returns (stream grpc.reflection.v1alpha.ServerReflectionResponse) {} } filename: api/hello.proto package: hello; service GreetingService { rpc Hello(hello.GreetingRequest) returns (hello.GreetingResponse) {} } $ grpc call localhost:10000 Hello "name: 'asa-taka'" connecting to localhost:10000 message: "Hello, asa-taka." date { seconds: 1527964860 nanos: 432266000 } Rpc succeeded with OK status
なんか普通に動いてしまって拍子抜けです。
import パス周り、意外と単純なのか根が深いのか... gRPC と Protocol Buffer 難しい...
gRPC で go-proto-validators を .proto に import したら grpc_cli がうまく使えない
(=˘ ꒳ ˘=) 最近 gRPC を始めたけどパス解決周りが Protocol Buffer 層も相まって複雑で混乱する...
hello.proto に簡単なバリデーションを組み込んで Go で吐き出して grpc_cli で動作確認しようとしたらハマって、そしてそのまま解決していない件になります。バリデーション処理ではなく主に Protocol Buffer の import 周りの試行錯誤のお話になります。
- 素朴に go-proto-validators を import したところ...
- とりあえずリフレクションを無効化して試行錯誤する
- symlink を張った状態でリフレクションを有効化してみる
- Issue を漁ってみる...
素朴に go-proto-validators を import したところ...
- プロジェクト: https://github.com/asa-taka/hello-validated-grpc
- バリデーションを組み込んだあたりのコード: https://github.com/asa-taka/hello-validated-grpc/commit/0badbe6c2bf7ff49a411e50d0b642573e64a6147
今回対象にする hello.proto は以下のようなものです。
go-proto-validators/validator.proto
を import
しています。
syntax = "proto3"; package hello; import "github.com/mwitkow/go-proto-validators/validator.proto"; service GreetingService { rpc Hello(GreetingRequest) returns (GreetingResponse); } message GreetingRequest { string name = 1 [(validator.field) = {string_not_empty: true}]; } message GreetingResponse { string message = 1 [(validator.field) = {string_not_empty: true}]; }
これを protoc
して Go ライブラリを吐き出してサーバを実装して grpc_cli で叩いたところ、症状としては以下のようになりました。
grpc_cli ls
は動くgrpc_cli ls -l
は動かないgrpc_cli call ...
も動かない
$ grpc_cli ls localhost:10000 grpc.reflection.v1alpha.ServerReflection hello.GreetingService $ grpc_cli ls -l localhost:10000 [libprotobuf ERROR google/protobuf/descriptor.cc:3592] Invalid proto descriptor for file "api/hello.proto": [libprotobuf ERROR google/protobuf/descriptor.cc:3595] api/hello.proto: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors. filename: grpc_reflection_v1alpha/reflection.proto package: grpc.reflection.v1alpha; service ServerReflection { rpc ServerReflectionInfo(stream grpc.reflection.v1alpha.ServerReflectionRequest) returns (stream grpc.reflection.v1alpha.ServerReflectionResponse) {} } $ grpc_cli call localhost:10000 Hello "name: 'test'" [libprotobuf ERROR google/protobuf/descriptor.cc:3592] Invalid proto descriptor for file "api/hello.proto": [libprotobuf ERROR google/protobuf/descriptor.cc:3595] api/hello.proto: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors. Method name not found
どれも github.com/mwitkow/go-proto-validators/validator.proto が見つからないと言われていますね。
gRPC や Protocol Buffer 周りのパス解決をよく理解しておらず、さらにリフレクション経由というローカルとはいえネットワークを噛ませたパス解決がどうなるかも相まって二重三重で混乱します。
とりあえずリフレクションを無効化して試行錯誤する
問題を簡略化したいので、とりあえずリフレクションはコメントアウトして grcp_cli --protofile
を直接指定するやり方で実行してみます。
https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md#call-a-remote-method の「User local proto file」のやり方です。
grpc_cli
にも --proto_path
を指定できるみたいですが protoc
のように複数指定してもうまく行かず、結局 .proto のパスを同一起点から解決できればいいんだろうということで、プロジェクトルートから依存パッケージを辿れるような symlink を張ったら動くことには動きました。
$ echo $GOPATH /Users/asa-taka $ pwd /Users/asa-taka/src/github.com/asa-taka/hello-validated-grpc $ tree . ├── Makefile ├── api │ ├── hello.pb.go │ ├── hello.proto │ └── hello.validator.pb.go ├── github.com -> /Users/asa-taka/src/github.com/ ├── google -> /Users/asa-taka/src/github.com/google/protobuf/src/google └── server.go $ grpc call localhost:10000 Hello "name: 'test'" --protofiles=api/hello.proto connecting to localhost:10000 message: "Hello, test." Rpc succeeded with OK status
流石に不便なのでもっといい方法が用意されているか、何かしら自分が間違えているのを期待しています。
一応どの symlink を張ったか解説しておくと
github.com -> ~/src/github.com/
はgo-proto-validators
を辿る用google -> ~/src/github.com/google/protobuf/src/google/
は protobuf の基本定義に必要らしいです- 中身(詳細は把握してません...): https://github.com/google/protobuf/tree/master/src/google/protobuf
ちなみに grpc_cli ls
は動作しませんでした。リフレクション専用のコマンドみたいですね。
$ grpc ls -l localhost:10000 --protofiles=api/hello.proto Received an error when querying services endpoint.
symlink を張った状態でリフレクションを有効化してみる
この状態からリフレクションのコメントアウトを外してみます。初めの状態と特に症状は変わりませんでした。依然として github.com/mwitkow/go-proto-validators/validator.proto が見つからないと言われています。
$ grpc ls localhost:10000 grpc.reflection.v1alpha.ServerReflection hello.GreetingService $ grpc ls -l localhost:10000 [libprotobuf ERROR google/protobuf/descriptor.cc:3592] Invalid proto descriptor for file "api/hello.proto": [libprotobuf ERROR google/protobuf/descriptor.cc:3595] api/hello.proto: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors. filename: grpc_reflection_v1alpha/reflection.proto package: grpc.reflection.v1alpha; service ServerReflection { rpc ServerReflectionInfo(stream grpc.reflection.v1alpha.ServerReflectionRequest) returns (stream grpc.reflection.v1alpha.ServerReflectionResponse) {} } $ grpc call localhost:10000 Hello "name: 'test'" [libprotobuf ERROR google/protobuf/descriptor.cc:3592] Invalid proto descriptor for file "api/hello.proto": [libprotobuf ERROR google/protobuf/descriptor.cc:3595] api/hello.proto: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors. Method name not found
苦し紛れに --proto_path=.
を指定してみても改善せず。
$ grpc ls -l localhost:10000 --proto_path=. [libprotobuf ERROR google/protobuf/descriptor.cc:3592] Invalid proto descriptor for file "api/hello.proto": [libprotobuf ERROR google/protobuf/descriptor.cc:3595] api/hello.proto: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors. filename: grpc_reflection_v1alpha/reflection.proto package: grpc.reflection.v1alpha; service ServerReflection { rpc ServerReflectionInfo(stream grpc.reflection.v1alpha.ServerReflectionRequest) returns (stream grpc.reflection.v1alpha.ServerReflectionResponse) {} } $ grpc call localhost:10000 Hello "name: 'test'" --proto_path=. [libprotobuf ERROR google/protobuf/descriptor.cc:3592] Invalid proto descriptor for file "api/hello.proto": [libprotobuf ERROR google/protobuf/descriptor.cc:3595] api/hello.proto: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors. Method name not found
リフレクションサービスは動いているけれど、import が解決できず GreetingService は動かないという...
リフレクション経由で .proto の import を解決する方法ってないんでしょうか。今のところ symlink を張るくらいの解決策しか見つかっておらず、このままだとちょっと不便なんですよね...何かあるはず、というかみんなどうやってるのか...
Issue を漁ってみる...
コマンドのヘルプもドキュメントもあまり充実してはいないので、諦めて Issue を辿っていきます。
- grpc_cli does not work if proto file contains google.protobuf.Timestamp · Issue #1163 · grpc/grpc-go · GitHub
- https://github.com/grpc/grpc-go/issues/1163#issuecomment-292168123
grpc_cli ls
は--proto_path
や--protofile
に対応していないらしい
- https://github.com/grpc/grpc-go/issues/1163#issuecomment-292168123
- grpc_cli does not work if proto file contains google.protobuf.Timestamp · Issue #10304 · grpc/grpc · GitHub
- 今回のとは少しエラーの様子が違うけれど
google.protobuf.Timestamp
を使った時も、ちょっとした対応を覚悟しなければならなそう
- 今回のとは少しエラーの様子が違うけれど
- ptypes: Path to proto file in timestamp.pb.go makes use of grpc awkward · Issue #298 · golang/protobuf · GitHub
- 色々辿ってこの Issue にたどり着きましたがもう何がなんだか...
ざっと見たところ google.protobuf.Timestamp
というよく使われる type/message を使った時も面倒なことになっているらしいですが、詳しいことは読み取る気力がわきませんでした。こちらの方が事象としてはよりシンプルな気もするので、こちら側から攻めてみてもいいかもしれませんね。どうせ日付型も使うことになるでしょうし。
...で、別記事で日付型の import を試してみたところ普通に動いてしまったというわけで、本件に関しては継続して探っていきたいと思います...
それにしても Issue を辿っていると protobuf と grpc と grpc-go をたらい回しになって、なんか、その、疲れます...
GraphQL のため息
GraphQL とは一体何だったのか、半年ほど触れ続けていまだ掴みあぐねています。
やりたいことは分かる、が、どこに使えばいいのかわからない。
サービスに導入するとして、GraphQL で実装したい場所が見つからない。
夢は見せてくれたが、壮大な社会実験だった気がしなくはない、そんな不安。
GraphQL は夢だけ見せて立ち去っていった。
いや、距離をとったのは自分の方だったのだけれど。
とりあえず、しばらくは別の何かを追いかけてみたい。
SaltStack を使ってみる
(=˘ ꒳ ˘=) ちょっと興味が湧いたので塩を舐めてみる...
https://docs.saltstack.com/en/latest/ に従ってチュートリアルを一通り触ってみました。
- 基本的な動作編: https://docs.saltstack.com/en/getstarted/fundamentals/
- 設定ファイルのマネジメント編: https://docs.saltstack.com/en/getstarted/config/
- Pillar や Jinja テンプレートなど、設定ファイルやそこで使えるパラメータの管理方法についてです
- mysqld をインストールする場面で
mysql-server
を指定しないと動きませんでした
- イベント管理編: https://docs.saltstack.com/en/getstarted/event/
- イベント処理の概要や Reactor などイベントをトリガに発火する処理の記述についてです
ざっと触ってみた感じは宣言的定義が主体であるプロビジョニングツールといった感じでした。 設定ファイルに Jinja テンプレート処理を書き込めるあたり、かなり凝った処理も行えそうです。
宣言的定義に関連してひとつ気になったのですが、チュートリアルの途中まで install vim
のように命令的な Session ID が振られていたのが、途中から vim installed
のように宣言的な Session ID に変わったところです。
SaltStack の中でもブレがあるのでしょうか、どちらが SaltStack 流なのか気になるところですね。
個人的には pkg.installed
という命名から伺える意向を汲んでいるように見える宣言的な Session ID の方が好みです。
命令を定義するのではなく、あくまで状態の定義を行い、SaltStack はホストの状態と定義された理想状態を比較して適宜処理を行う。 install という処理が走るのはあくまで結果であって、大元にあるのはあくまで状態定義である、...と Kubernetes 的な思考を流用するとこうなるでしょうか。
『プロフェッショナルSSL/TLS』を読む - 「第1章 SSL/TLSと暗号技術」から読む
(=˘ ꒳ ˘=) とりあえず Alice と Bob と Mallory だけ覚えておこう...
最近 Kubernetes Dashboard など、HTTPSが有効化されたツールをセットアップする機会が増えてきました。 なのでそろそろきちんと証明書などその辺の技術基盤を理解しないといけない... というか理解した方がそういったツールのトラブルシュートも捗るだろうという目論見でSSL/TLSについて興味を持ち始めております。
というわけで、今読んでいるのは『プロフェッショナルSSL/TLS』という本です。
「はじめに」で実用的な所を知りたければ8、9章を読みましょうとありますが、理論的な所に興味があるのでおとなしく冒頭から順に読んでいきます。
1.3 までは暗号技術の必須要件や歴史なので、とりあえず今は簡単に目を通して飛ばします。 ある程度読み進めてから戻って来たいと思います。
今回は「1.4 暗号技術」の「1.4.1 技術要素」までを読みました。
まず Alice など登場人物の凡例を押さえておく
初めは順序もバラバラに斜め読みしたせいで Alice と Bob と Mallory という登場人物の解釈で迷いましたが、 1.4 の冒頭に
Alice と Bob は暗号技術をわかりやすくせつめいするためによく使われている名前です。
と書かれていました。これだけ押さえたら途端に読みやすくなりました。
1.4 暗号技術
1.4 節の冒頭に暗号技術で解決したいセキュリティにおける重要な問題として
- 機密性(confidentiality): 秘密が守られること
- 真正性(authenticity): 本人であることの検証ができること
- 完全性(integrity): データが改ざんされることなく転送されること
が挙げられていますが、こうやって並べられているだけでは「へぇ、そうなんだ」で通り過ぎてしまいますが、 続く「1.4.1 要素技術」でこれらの言葉がどう使われているかを見ることで、だんだんと具体的なイメージがつかめてきます。
1.4.1 要素技術
1.4.1項で挙げられている要素技術を並べてみると
- 共通鍵暗号化方式
- ストリーム暗号化方式、ブロック暗号化方式、パディング
- ハッシュ関数
- 原像計算困難性、第二原像計算困難性、衝突耐性
- MAC(Message Authentication Code、メッセージ認証コード)
- 暗号化利用モード
- ECB(Electronic Codebook)モード、CBC(Cipher Block Chaining)モード
- 公開鍵暗号化方式
- ディジタル署名
- 乱数生成器
となりますが、見た感じ機密性と完全性を提供するものに見えますね。 あとは暗号強度を改善する技術でしょうか。 真正性の例は、次の「1.4.2 プロトコル」で Alice と Bob が互いの認証を行う過程で触れられていました。
1.4.1項、1.4.2項の冒頭で繰り返し述べられていることですが
要素技術は、単体ではそれほど便利ではありませんが、 それらをスキームやプロトコルとして組み合わせることで強固なセキュリティを実現できます。
ということで、ここから先はここまで読んできた要素技術の応用編、ということになりそうです。 おそらく度々ここに戻ってきて「公開鍵暗号化方式って具体的にどうだっけ」と読み返すことになるんでしょうね。
一点、機密性、完全性は要素技術で提供されるけれど、真正性はプロトコルで提供される、という違いが気になりました。 節の冒頭で機密性、完全性、真正性と並列に並べられていたのと比較して、ちょっと興味深かったです。 この認識で正しいのかも含めて、この先の内容が楽しみです。
とりあえず読み始めてみて
...と、ここまで読むのに4時間くらいかかりました。
こう言った本にありがちですが、流し読みで理解できる内容ではないんですよね。 ちゃんと読もうとすると一文一文がとにかく重く、行間に気を配っていないとすぐに認識ズレを起こしそうで気力を持って行かれます。 そしてどう読んだところで認識ズレは起こすんだろうなという諦めもあります。
反面、それが楽しくもあるのですけどね。 各項が授業ひとコマ分くらいの密度を持っていてとても読み応えがあります。
みたいな感じで、当サイトでは個々の技術についてはまとめず、どこまで読んでどう思ったかの感想をふわっと書いていこうと思います。 各用語については Wikipedia を見た方がよほど詳しいですしね。
vagrant-hosts を使って互いに名前引き可能なVMをさっくり立てる
(=˘ ꒳ ˘=) らくちんなのである...
Kubernetes のクラスタをVMで組んでみたくて Vagrant のお勉強中です。だんだん Ruby が読めるようになってきました。
クラスタを組みたいので当然互いにアドレスなり名前解決可能なホスト名なりで相互接続したいですね。
Vagrant にはDNS的な仕組みはなさそうなので名前解決をしたければ /etc/hosts
にレコードを登録する必要があります。
自前でも生成できそうですが、上手いこと自動設定してくれるプラグインが networking - Can Multiple Vagrant VMs communicate by VM hostname? - Stack Overflow で紹介されていたので使ってみます(4年前ですか、結構昔ですね...)。
GitHub - oscar-stack/vagrant-hosts: Manage static DNS on vagrant guests
これを利用して master 1台と node 3台のVMを立ててみます。
Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.define "master" do |m| m.vm.network "private_network", ip: "192.168.0.1" m.vm.provision :hosts, sync_hosts: true end (1..3).each do |i| config.vm.define "node-#{i}" do |n| n.vm.network "private_network", ip: "192.168.1.#{i}" n.vm.provision :hosts, sync_hosts: true end end end
vagrant ssh master
して /etc/hosts
を見てみると
[vagrant@master ~]$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 master 192.168.0.1 master 192.168.1.1 node-1 192.168.1.2 node-2 192.168.1.3 node-3
作成したVMとアドレスが問題なく登録されています。ping
も問題なく通りました。
環境メモ
$ vagrant version Installed Version: 2.0.3 Latest Version: 2.0.3 $ vagrant plugin list vagrant-hosts (2.8.0)