Go + gRPCで開発を始められるDockerfileを作る

# 概要
Go + gRPCで開発を行う為の環境構築方法を解説します。

環境構築はDockerを利用します。

gRPCの詳しい説明はこの記事では行いません。

# 想定読者

- Dockerの基礎知識をお持ちの方
- Goの基礎知識をお持ちの方
- Go + gRPCでの開発に興味がある方

# サンプルコードをGitHubに登録してあります

以下にサンプルコードを用意してあります。

https://github.com/keitakn/golang-grpc-server

[こちら](https://github.com/keitakn/golang-grpc-server/blob/master/Dockerfile) がDockerfileの中身になります。

以後はこのDockerfileを作り上げるまでに実行した手順を解説しますので、 [こちら](https://github.com/keitakn/golang-grpc-server/blob/master/Dockerfile) を見て理解出来る方はこの記事をこれ以上読む必要はありません。

# Dockerfileを作るまでの手順

Alpine 3.11系のGoLangイメージをベースにします。

- ホストOS（MacOS Catalina 10.15.2）
- Dockerの実行環境（Docker for Mac 2.1.0.5）

## protobufのインストール

gRPCでは初めにインタフェース定義言語 (IDL) でAPIの定義ファイルを作成します。

ここではgRPCでデファクトスタンダードになっている [Protocol Buffers](https://developers.google.com/protocol-buffers/) を利用出来るようにします。

https://github.com/protocolbuffers/protobuf/releases から最新版ダウンロードを行いインストールを行います。

### packageの最新化と関連packageのインストール

packageの最新化とコンパイルに必要なpackageをインストールします。

```sh
apk update
apk add git curl build-base autoconf automake libtool
```

### `/tmp` に `tar.gz` をダウンロードする

```sh
curl -L -o /tmp/protobuf.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/v3.11.2/protobuf-cpp-3.11.2.tar.gz
```

### Build、インストールを行う

`cd /tmp` で `/tmp` に移動します。

`tar xvzf protobuf.tar.gz` を実行し `.tar.gz` を解凍します。

その後 `cd protobuf-3.11.2` で移動します。

以下のコマンドを順番に実行します。

```sh
./autogen.sh
./configure
make -j 3
make check
make install
```

補足ですが `make -j` は並行処理を行う事でコンパイルを高速化する為のオプションです。
`make -j 3` は3並行処理を行うという意味になります。
お使いの環境の「CPUの数×2」を目安に調整するのが良いと思います。

ちなみに、Build、インストールにはかなりの時間がかかります。

`protoc --version` で以下のように表示されれば成功です。

```
libprotoc 3.11.2
```

## protocのGo用のプラグインをインストール

protocをGoで利用する為にプラグインをインストールします。

```sh
go get -u github.com/golang/protobuf/protoc-gen-go
```

## protocによるdocument生成（任意）

こちらは任意ですが、 `.proto` からHTML形式等でドキュメントを生成出来るのでインストールしておくと良いでしょう。

```sh
go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc
```

# protobufの動作確認

[こちら](https://github.com/keitakn/golang-grpc-server) に動作確認用のGitリポジトリを用意しました。

プロジェクトルートの `pb/` 配下に `dog.proto` を作成します。

```protobuf:pb/dog.proto
syntax = "proto3";

service Dog {
    rpc FindCuteDog (FindCuteDogMessage) returns (FindCuteDogResponse) {}
}

message FindCuteDogMessage {
    string DogId = 1;
}

message FindCuteDogResponse {
    string name = 1;
    string kind = 2;
}
```

プロジェクトルート（私のサンプルプロジェクトだと `/go/app` ）で以下のコマンドを実行します。

```sh
protoc --go_out=plugins=grpc:. pb/dog.proto
```

そうすると `pb/dog.pb.go` というファイルが作成されているかと思います。

以下のようなファイルです。（内容を一部載せています。）

```golang:pb/dog.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: pb/dog.proto

package dog

import (
	context "context"
	fmt "fmt"
	proto "github.com/golang/protobuf/proto"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
	math "math"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

type FindCuteDogMessage struct {
	DogId                string   `protobuf:"bytes,1,opt,name=DogId,proto3" json:"DogId,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *FindCuteDogMessage) Reset()         { *m = FindCuteDogMessage{} }
func (m *FindCuteDogMessage) String() string { return proto.CompactTextString(m) }
func (*FindCuteDogMessage) ProtoMessage()    {}
func (*FindCuteDogMessage) Descriptor() ([]byte, []int) {
	return fileDescriptor_993586c250cd4a03, []int{0}
}

// 以下略
```

ここまでの手順でGo + gRPCで開発出来る環境が整いました。

最終的なDockerfileを載せておきます。（[こちら](https://github.com/keitakn/golang-grpc-server/blob/master/Dockerfile) と同様の内容です）

```dockerfile:Dockerfile
FROM golang:1.13-alpine3.11 as build

WORKDIR /tmp

ENV PROTOBUF_VERSION 3.11.2
ENV PROTOBUF_URL https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/protobuf-cpp-$PROTOBUF_VERSION.tar.gz

RUN set -eux && \
  apk update && \
  apk add --no-cache git curl build-base autoconf automake libtool && \
  curl -L -o /tmp/protobuf.tar.gz $PROTOBUF_URL && \
  tar xvzf protobuf.tar.gz

WORKDIR /tmp/protobuf-$PROTOBUF_VERSION

RUN set -eux && \
  ./autogen.sh && \
  ./configure && \
  make -j 3 && \
  make install && \
  go get -u github.com/golang/protobuf/protoc-gen-go && \
  go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc

WORKDIR /go/app

COPY . .

RUN set -eux && \
  go build -o golang-grpc-server && \
  go get gopkg.in/urfave/cli.v2@master && \
  go get github.com/oxequa/realize && \
  go get -u github.com/go-delve/delve/cmd/dlv && \
  go build -o /go/bin/dlv github.com/go-delve/delve/cmd/dlv

FROM alpine:3.11

WORKDIR /app

COPY --from=build /go/app/golang-grpc-server .

RUN set -x && \
  addgroup go && \
  adduser -D -G go go && \
  chown -R go:go /app/golang-grpc-server

CMD ["./golang-grpc-server"]
```

以前 [Docker上のGo製Webアプリケーションをリモートデバッグする](https://qiita.com/keitakn/items/f46347f871083356149b) という記事を書きましたが、この記事で紹介しているデバッガー等のインストールもこの `Dockerfile` で行っています。

# protocによるdocument生成

[こちらのプロジェクト](https://github.com/keitakn/golang-grpc-server) を例に説明します。

`docs` 配下にHTML形式のドキュメントを生成する為には下記のコマンドを実行します。

```sh
protoc --doc_out=html,index.html:./docs pb/*.proto
```

結果として `docs/index.html` が出力されます。

見た目はこんな感じです。

<img width="999" alt="ProtocolDocumentation.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71899/ab1235a1-7f45-82a9-5be2-8a2f98740e29.png">


# おまけ gRPCサーバーの動作確認（[grpc_cli](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md) を利用）

gRPCはcurl等で手軽に動作確認が出来ませんでしたが、[gRPC command line tool](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md) を利用すると動作確認が簡単です。

Mac上にインストールします。

```
brew install gflags

brew tap grpc/grpc

brew install grpc
```

`which grpc_cli` を実行して `/usr/local/bin/grpc_cli` 等が表示されればインストール出来ています。

## grpc_cliの簡単な使い方

以下でServiceの内容を取得します。

```bash
grpc_cli ls localhost:9998
```

以下のようにgRPCサーバーの内容が表示されます。

```
Cat
grpc.reflection.v1alpha.ServerReflection
```

次のようにメソッドを指定するとそのインターフェースを確認出来ます。

```bash
grpc_cli ls localhost:9998 Cat.FindCuteCat -l
```

これらのgRPCサーバーの情報を取得する為にはgRPCサーバー側で `reflection.Register` が実行されている必要があります。

詳しくは [こちらのコード](https://github.com/keitakn/golang-grpc-server/blob/5df29d624f4046e2a3dac636fad83ff7503376dd/main.go#L27) を確認して下さい。

実際にgRPCのメソッドを呼び出す際は下記のように実行します。

```bash
grpc_cli call localhost:9998 Cat.FindCuteCat 'catId: "moko"'
```

# おわり

以上がGo + gRPCでの開発環境構築方法です。

最近はマイクロサービスでの開発が多いので、Go + gRPCという組み合わせの採用例が増えているように感じます。

この記事が少しでもお役に立てたら幸いです。

今回の記事を書く為に以下の記事を参考にさせて頂きました。

- [Goで始めるgRPC入門](https://qiita.com/marnie_ms4/items/4582a1a0db363fe246f3)
- [gRPC ドキュメント](https://grpc.io/)
- [Protocol Buffers 公式ドキュメント](https://developers.google.com/protocol-buffers/docs/proto3)