この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ども、大瀧です。
先日GAを迎えたGoogle製RPCフレームワークgRPCはマイクロサービスやモバイルアプリの文脈で紹介されることが多いですが、IoT用途でも使えないかなぁと思い立ち、SORACOM Beamとの組み合わせを考えてみました。
IoTでgRPCを利用するメリット
IoTシステムの開発では、センサーやIoTゲートウェイ(クライアントサイド)を開発する組み込みエンジニアとクラウド(サーバーサイド)を開発するWebエンジニアではスキルセットが異なることがままあると思います。クラウドとの通信はRESTful APIを利用することが多く、Web技術に明るくない組み込みエンジニアにとってHTTPのステートレスなAPIは敷居が高く感じられるかもしれません。
gRPCはWeb技術の中でも新しい部類で、仕組みとしてはHTTP/2を利用しますが、プログラミング言語に依存しないクライアント/サーバー間のインターフェース定義や複数の言語に対応するコードジェネレータがあり、通信部分の実装の多くをgRPCに任せることができます。また、認証やステートフル接続などgRPCが独自に提供する機能も豊富にあるので、エンジニアがセンサーデータの整形やアルゴリズムのコーディングに集中できるとも言えるでしょう。
SORACOM Beamの活用
gRPCは通信プロトコルとしてHTTP/2を採用し、TLSによる暗号化を前提としています。TLSは接続時の処理にCPUリソースを多く消費するため、頻繁に接続/切断する用途の場合、IoTゲートウェイ(gRPCクライアント)のリソースを多く使ってしまう恐れがあります。
SORACOM Beamは、3G/LTEモバイル網を利用するSORACOM Air SIMからのトラフィックに暗号化やヘッダ付与などの処理を施してサーバーに転送するプロキシサービスです。今回は、gRPCクライアント(暗号化なし)からの通信をBeamで暗号化し、TLSでListenしているgRPCサーバーに送信する構成をやってみます。
以前紹介した以下の記事と同様、インターネットを経由する箇所をセキュアにデータ転送させるための構成です。
検証環境
- クライアント : Macbook / macOS Sierra
- サーバー : Amazon EC2 / Amazon Linux 2016.09
- gRPC : grpc-go バージョン1.0.1-GA
検証手順
1. gRPCサーバーの構成
まずはEC2でgRPCのサンプルプログラムをビルド、実行します。今回はTLSでListenするので、サーバー証明書一式をCertbot(Let's EncryptのCLIツール)で取得しておき、以下のように実装しました。ハイライト行が変更・追加した部分です。
greeter_server/main.go
import (
"log"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
port = ":50051"
certFile = "./greeter_server/server.crt"
keyFile = "./greeter_server/server.key"
)
// server is used to implement helloworld.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()
var opts []grpc.ServerOption
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
s := grpc.NewServer(opts...)
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
$ sudo yum install -y go
:(略)
$ export GOPATH=~/.go
$ go get golang.org/x/net/context google.golang.org/grpc
$ cd .go/src/google.golang.org/grpc/examples/helloworld/
$ cp ~/{server.crt,server.key} greeter_server/ # あらかじめ作成した証明書をコピー
$ go build -o greeterd greeter_server/main.go
$ ./greeterd > greeterd.log 2>&1 &
[1] 32077
$ sudo netstat -ltnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 2278/rpcbind
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2497/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2527/sendmail
tcp 0 0 0.0.0.0:35620 0.0.0.0:* LISTEN 2299/rpc.statd
tcp 0 0 :::40334 :::* LISTEN 2299/rpc.statd
tcp 0 0 :::111 :::* LISTEN 2278/rpcbind
tcp 0 0 :::22 :::* LISTEN 2497/sshd
tcp 0 0 :::50051 :::* LISTEN 32077/./greeterd
$
50051番ポートをgreeterd
がListenしていればOKです。作成した証明書のホスト名に合わせて、EC2インスタンスのPublic IP(もしくはElastic IP)にDNSのAレコードを向けておきましょう。
2. SORACOM Beamの構成
続いて、使用するAir SIMが所属するグループをSORACOMユーザーコンソールで作成し、Beamの[TCP→TCP/TCPSエントリーポイント]を追加します。[転送先]設定に、[プロトコル]は「TCP(over TLS)」、[ホスト名]をDNSで登録したホスト名、[ポート番号]はサーバーがListenする「50051」をセットします。
クライアントの接続先は固定なので[エントリポイント]の各項目をメモしておきましょう。
3. gRPCクライアントの構成
サーバーと同じく、今回はGo版gRPCのサンプルプログラムを利用します。こちらは、接続先を上記のエントリポイントに設定するだけです。
greeter_client/main.go
package main
import (
"log"
"os"
"golang.org/x/net/context"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
address = "beam.soracom.io:8023"
defaultName = "world"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
では、実行してみましょう。
$ go run greeter_client/main.go
2016/09/29 00:24:57 Greeting: Hello world
$
gRPCサーバーから正常にレスポンスが返って来ました!
おまけ: ELBの組み合わせ
gRPCサーバーとELBの組み合わせをこちらの記事で紹介しましたが、Classic Load BalancerをL4転送として利用しているため、今回のTLS通信も転送することが可能です。DNSをELBに向けてTCP:50051
→TCP:50051
で転送設定しましょう。ELBのSSL TerminationはClassic Load BalancerがTLSのALPN拡張をサポートしないため不可、ということに注意しましょう。
まとめ
gRPCの変わったユースケースとして、IoT向けにSORACOM Beamとの組み合わせをご紹介しました。SORACOM BeamのTCPSエントリポイントがクライアント証明書をサポートするとgRPCの双方向認証に対応できそうなので、ちょっと期待したいところです。