SDN開発エンジニアを目指した活動ブログ

〜SDNなオープンソース製品を実際に使って試してみる〜

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アプリ連携にチャレンジしてみます。