きみはねこみたいなにゃんにゃんなまほう

ねこもスクリプトをかくなり

Protocol Buffer と gRPC を Go で使うときのリポジトリを整理する

(=˘ ꒳ ˘=) googlegolang がぱっと見ややこしい...

Protocol Buffer と gRPC を Go で利用しようとすると色んなリポジトリからツールを落としてくる必要がありますがどれも微妙に字面が似ているので混乱します。それらを整理するために org/repo の形式で一覧できるようにまとめていきたいと思います。

まず Protocol Buffer のコアなツールが含まれるリポジトリ

  • google/protobuf
    • C++ 製の Protocol Buffer のコア
    • 含まれるツール: protoc
    • 日付型などの well-known 型の .proto 定義も含まれる

ですね。Go の場合はこれに加えて Protocol Buffer と gRPC の Go 実装のためのパッケージが必要になります。 .proto 定義からコードを生成したり、そのコードから import されるパッケージが以下のリポジトリに含まれます。

C++PHPなど Go 以外の言語だと grpc のメインのリポジトリでその辺が間に合ってしまいそうです。

上に書いた通り grpc_cli は便利なので Go でもこのリポジトリの一部機能は利用する、という感じです。

...ややこしいですね。

まだ書き出しただけで、プラグインやら well-known 型の定義や実装がどこにあるかやらが整理できているとは言い難いですが、使っているうちに慣れるのでしょうか...

gRPC で日付型を利用する

(=˘ ꒳ ˘=) gRPC の import 周りを整理したい...

という記事で go-proto-validators と grpc_cli をうまく組み合わせられないことに対して試行錯誤してました。

その問題の切り分けがてら、今回はもう少し基本的な日付型の利用を通して gRPC の import 周りを整理していきたいと思います。

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 したところ...

今回対象にする hello.proto は以下のようなものです。 go-proto-validators/validator.protoimport しています。

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 を張ったか解説しておくと

ちなみに grpc_cli ls は動作しませんでした。リフレクション専用のコマンドみたいですね。

$ grpc ls -l localhost:10000 --protofiles=api/hello.proto
Received an error when querying services endpoint.

この状態からリフレクションのコメントアウトを外してみます。初めの状態と特に症状は変わりませんでした。依然として 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 を辿っていきます。

ざっと見たところ 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/
    • master + minion x 2 という構成でリモートな複数ホスト上でコマンドを一斉実行したりします
    • HTTP Proxy 環境下では vagrant up した後、無言で止まることがあるので適宜 vagrant-proxyconf あたりのプラグインで設定してやるとうまく進むようになります
  • 設定ファイルのマネジメント編: 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 的な思考を流用するとこうなるでしょうか。

それにしても Vagrant 様々です。VMにアレルギーを持っていた頃が遥か昔のことのようです。

『プロフェッショナル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)