GoBGPでの内部構造で活用されている"gRPC"の仕組みを体験してみる
[ 2020.5.24修正:gRPC最新版への対応 ]
最近、GoBGPでの内部構造として活用されている"gRPC"に興味を持ち始めました。
GoBGP実践活用として、gRPC技術の修得が必要不可欠になりそうなので、まずは、gRPCを体験するところから始めたいと思います。
◆ gRPCとは?
googleが開発したオープンソースベースのRPCフレームワークだそうです。各種プログラミング言語に対応しているみたいで、データ通信層には、HTTP/2が活用されるらしいです。
なお、サーバ/クライアント間でのシリアライズ化などの通信処理は、特に意識することなく、IDLのプリコンパイルによって自動生成されるスタブが担うことになります。
さらに、IDLの記述仕様は、以下のサイトが参考になります。
developers.google.com
このあたりの開発作業スタイルは、その昔の、"DCE/RPCプログラミング"による分散処理システム構築時の実装スタイルとそっくりな感じです。
... まず、rpcgenで、IDLファイルをプリコンパイルしてスタブを自動生成して、サーバ・クライアントアプリに組み込むあたりが...
◆ gRPC環境準備
今回は、Golang/Python版でのgRPC動作環境を作成してみます。
0. Ubuntu環境準備
Ubuntuサーバ環境を準備します。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"
1. GoBGPのインストール
今回は、gRPCサンプルアプリをサーバ側で動作させる際には、golangが必要になります。
GoBGP環境も、同時に作成しておきます。
$ vi $HOME/.profile ————————— ... (snip) export GOPATH=$HOME/golang export PATH=$GOPATH/bin:/usr/local/go/bin:$PATH $ wget --no-check-certificate https://storage.googleapis.com/golang/go1.13.5.linux-amd64.tar.gz $ sudo tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz $ mkdir $HOME/golang $ source .profile $ go version go version go1.13.5 linux/amd64 $ sudo apt-get update $ sudo apt-get install git $ go get github.com/osrg/gobgp/cmd/gobgpd $ go get github.com/osrg/gobgp/cmd/gobgp $ go get github.com/golang/protobuf/protoc-gen-go
2. Protocol Buffersのインストール
Protocol Buffersをインストールします。
$ cd $HOME $ sudo apt-get install unzip $ wget https://github.com/google/protobuf/releases/download/v3.12.1/protoc-3.12.1-linux-x86_64.zip $ unzip protoc-3.12.1-linux-x86_64.zip -d protoc3 $ sudo mv protoc3/bin/* /usr/local/bin/ $ sudo mv protoc3/include/* /usr/local/include/ $ protoc --version libprotoc 3.12.1
3. gRPC for Pythonのインストール
python版gRPCをインストールします。
$ sudo apt-get install python-dev $ wget https://bootstrap.pypa.io/get-pip.py $ sudo python get-pip.py $ sudo pip install pip==9.0.3 $ sudo pip install grpcio $ sudo pip install grpcio-tools
◆ gRPCのサンプルアプリ"hello world"を動かしてみる
ここでのサンプルアプリの動作環境の特徴としては、サーバ/クライアント間で異なるプログラミング環境でも、gRPCサーバ/クライアント間での相互運用が可能になっております。
すなわち、サーバ側をgolangで実行し、クライアント側をpythonで実行させています。
1. IDLファイル準備
以下のIDLファイル”helloworld.proto”を、任意ディレクトリ(ここでは、"helloworld"とします)に保存します。
$ cd $HOME $ mkdir helloworld $ cd helloworld $ vi helloworld.proto ------------ syntax = "proto3"; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }
2. クライアントアプリ準備
まずは、クライアント作業ディレクトリ"client"に移動して、先ほどのIDLファイルをコンパイルします。
すると、"helloworld_pb2.py"が自動生成されます。
$ cd $HOME/helloworld $ mkdir client $ cd client/ $ python -m grpc_tools.protoc -I .. --python_out=. --grpc_python_out=. ../helloworld.proto $ ls -l total 8 -rw-r--r-- 1 root root 3733 May 24 00:19 helloworld_pb2.py -rw-r--r-- 1 root root 2196 May 24 00:19 helloworld_pb2_grpc.py
さらに、"greeter_client.py"を同じディレクトリに配備します。
$ vi greeter_client.py ---------------------------- import grpc import helloworld_pb2 import helloworld_pb2_grpc def run(): # NOTE(gRPC Python Team): .close() is possible on a channel and should be # used in circumstances in which the with statement does not fit the needs # of the code. with grpc.insecure_channel('localhost:50051') as channel: stub = helloworld_pb2_grpc.GreeterStub(channel) response = stub.SayHello(helloworld_pb2.HelloRequest(name='you')) print("Greeter client received: " + response.message) if __name__ == '__main__': run()
3. サーバアプリ準備
つぎに、サーバ作業ディレクトリ"server"に移動して、先ほどのIDLファイルをコンパイルします。
すると、"helloworld.pb.go"が自動生成されます。
$ cd $HOME/helloworld $ mkdir server $ cd server/ $ protoc -I .. --go_out=plugins=grpc:. ../helloworld.proto $ ls -l total 12 -rw-r--r-- 1 root root 9579 May 24 00:27 helloworld.pb.go $ mkdir helloworld $ mv helloworld.pb.go helloworld
さらに、"greeter_server.go"を配備します。
$ vi greeter_server.go ---------------------------- package main import ( "log" "net" pb "./helloworld" "golang.org/x/net/context" "google.golang.org/grpc" ) const ( port = ":50051" ) // server is used to implement hellowrld.GreeterServer. type server struct{} // SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.Name}, nil } func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) s.Serve(lis) }
4. サンプルアプリ起動
まずは、サーバ側サンプルアプリを起動します。
$ go run greeter_server.go
続いて、別ターミナルから、クライアントアプリを起動します。
"Greeter client received: Hello you"と出力されるとサンプル起動は、成功です。
$ python greeter_client.py Greeter client received: Hello you
◆ 終わりに
GoBGP活用として、Ryu SDN Framework連携を目指した初めの一歩として、gRPCによるサンプルアプリを動作させてみました。
次回は、GoBGP実践活用の前段として、Pythonアプリ連携にチャレンジしてみます。