gRPC-Web をやってみるための環境を作る

gRPC-Web を改めてやってみようと思ったがかなり変わっていそうなので、まず環境を Docker で作る。

開発環境

  • Windows 10 Pro バージョン20H2
  • docker desktop 3.3.3
  • WSL2 のディストリビューションは Ubuntu 20.04

参考文献

環境構築にあたり、まず gRPC-Web の 公式サイト は情報が古くてあまり参考にならなかった。

よって本記事では下記記事を大いに参考にした。

本文

最初に完成品は下記にあるので細かいことは実際に動かしてみてほしい。

https://gitlab.com/k1350/daybreak_grpc_web_test/-/tree/helloworld

protocol buffer の内容は以前やった Go 言語で gRPC をはじめてみる - 環境構築編 と同じで、サーバーサイドのコードも同じである。
今回から Docker コンテナで動かすユーザーを root 以外にするようにしたので Dockerfile は多少違ったり、protoc コマンドの内容を最適化したりしたが、基本的には前にやったことと同じ。

よって本記事ではクライアントサイドと Envoy に着目する。

gRPC-Web protoc plugin のインストールとコンパイル

以前やったときは Go 用のコンパイルだけすればよかったが、今回は js 用のコンパイルが必要なので、Protocol Buffers をコンパイルする環境に追加のプラグインが必要になる。

2021/06/06現在、gRPC-Web の公式サイトの Basics tutorial を見るとコンパイルしろと書いてあるのだが、そんなことはしなくていい。
gRPC-Web のリポジトリ の README.md に記載されているが、ちゃんと公式にバイナリが配布されているので使う。

前回までと変える点は下記 Dockerfile では 11-13 行目で、配布されているバイナリをダウンロードして protoc-gen-grpc-web という名前で /usr/local/bin にコピーし、実行権限を付与する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 2021/06/05 現在の最新バージョン
FROM golang:1.16.5-buster

ARG UID
ARG GID
RUN groupadd -g $GID docker
RUN useradd -u $UID -g $GID -s /bin/bash -m docker

RUN apt-get update && apt-get install -y protobuf-compiler

RUN wget https://github.com/grpc/grpc-web/releases/download/1.2.1/protoc-gen-grpc-web-1.2.1-linux-x86_64
RUN mv protoc-gen-grpc-web-1.2.1-linux-x86_64 /usr/local/bin/protoc-gen-grpc-web
RUN chmod +x /usr/local/bin/protoc-gen-grpc-web

USER $UID

RUN go get -u google.golang.org/protobuf/cmd/protoc-gen-go
RUN go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc

WORKDIR /go/src

protoc-gen-grpc-web という名前は一字一句違わないように気を付けること。(ここで typo して、後で protoc-gen-grpc-web コマンドが見つからないというエラーになりハマった。)

これで gRPC-Web 用のコンパイルが可能となる。

コンパイルコマンドは gRPC-Web のリポジトリ の README に記載の通り、下記のようになる。

protoc -I=proto/api \
    --js_out=import_style=commonjs:./client/generated/helloworld \
    --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./client/generated/helloworld \
    proto/api/helloworld.proto

これは proto/api/helloworld.proto を commonjs スタイルでコンパイルし、./client/generated/helloworld に出力するというコマンドである。
他のオプションは gRPC-Web のリポジトリ の README に書いてある。

クライアントサイド

次はクライアントサイドの実装だが、本記事はあくまでも環境構築の内容だし参考記事を丸パクリしたので流す。こんな感じ

クライアントサイドを実行するための環境構築は参考記事には書いていなかったので取り扱う。

今回は webpack でコードをまとめて、http-server というやつでサーバーを立てることにした。
当初は参考記事の通り yarn でサーバーを立てようとしたのだが、私の知識不足でうまくいかなかったので http-server にした。

Dockerfile はシンプルに下記の通り。ほとんど素の node イメージのまま。

# 2021/06/06 現在の最新バージョン
FROM node:16-alpine3.11

# デフォルトで入っている node ユーザーを使う
USER node

WORKDIR /client

EXPOSE 8081

このコンテナ内で npm install するための package.json が下記の通り。

{
    "name": "grpc-web-client",
    "dependencies": {
        "google-protobuf": "^3.17.2",
        "grpc-web": "1.2.1"
    },
    "devDependencies": {
        "browserify": "^17.0.0",
        "http-server": "^0.12.3",
        "webpack": "^5.38.1",
        "webpack-cli": "^4.7.0"
    }
}

Basics tutorial にもこんな内容が書いてあるのだが、2021/06/06現在の gRPC-Web の最新バージョンは 1.2.1 なのに 0.4.0 が指定してあって本当に古いので要注意。
当然コンパイルに使ったバージョンと同じバージョンでないとうまく動かない。

あとはコンテナ起動後に

npm install
npx webpack --mode=development ./client.js
npx http-server -p 8081 

という内容を実行すればよいので、これを scripts/make.sh という名前に記述しておき、docker-compose.yml のクライアント部分は下記のようになる。

client:
    container_name: client
    image: grpc_web_client:1.0
    build: 
        context: ./client/build
    volumes:
        - type: bind
            source: "./client"
            target: "/client"
    ports:
        - "8081:8081"
    tty: true
    depends_on:
        - proto
        - server
    command:
        sh -c "chmod -R +x ./scripts/ && ./scripts/make.sh && /bin/sh"

Envoy

最後に gRPC-Web を実行するために必要なプロキシである Envoy だが、これについてもほぼ参考記事を丸パクリしたので流す。設定ファイルは こんな感じ で、docker-compose.yml の Envoy 部分は下記のようになる。

envoy:
    container_name: envoy
    image: envoyproxy/envoy:v1.18.3
    ports:
        - "10000:10000"
    volumes:
        - type: bind
            source: "./proxy/envoy.yaml"
            target: "/etc/envoy/envoy.yaml"

実行

完成品の README に記載の通りに実行し、localhost:8081 にアクセスすると画面に “Hello World” と出るはず。

余談なのだが先日 新しい docker compose なる記事を見て、“docker-compose” を “docker compose” に読み替えて本環境構築を行っていたところバグを踏み抜いてハマってしまった。
issue 立ってるのだが “docker compose build” は build arg を反映しないので、2021/06/06現在ではまだ “docker-compose” を使うほうがハマらないと思う。

今回はこれで終わり。