go + realize + grpc-gatewayをDocker上で動かしてホットリロードできるようにする

Gopher道場 Advent Calendar 12日目のエントリになります。
昨日は、@Baki33 さんによる「[Cloud Functions/Node.jsでGoを動かす魔法の裏側](https://qiita.com/Baki33/items/55e98683549fe31e9b9b)」でした。

# 概要
この記事ではgo + realize + grpc-gatewayをDocker上で動かし、ホットリロードをローカル環境で実現する方法をまとめます。

# はじめに
gopher道場の３期生として参加しgo全般非常に勉強になったのですが、gopher道場では触れられておらず、実際に業務にあたり欲しいなと思ったのがホットリロード、つまりファイルに変更をするとビルドし直して再実行してくれる用のツールでした。

ただGoのアプリケーションを作って、それをホットリロードで動かすのであれば各ツールのチュートリアルでできるかと思うので、今回は実際に業務で使用しているのに近い構成でrealizeを動かします。

# ホットリロード用のツール
ホットリロード用のツールとして、以下のツールがあるようです。この中で比較的最近まで開発が行われておりスター数も多いrealizeを選択して使っています。

- [godo](https://github.com/go-godo/godo)
- [fresh](https://github.com/gravityblast/fresh)
- [realize](https://github.com/oxequa/realize)

# 今回の記事のソースコード一式

今回のソースコード一式は以下にあげて動くようにしています。
https://github.com/gosagawa/realize_sample

# 環境

本体サーバとgrpc-gateway,あとリバースプロクシ用のコンテナを動かします。
それをrealizeで動かせるようにrealizeの設定を追加します。

##Dockerfileの設定
dockerファイルを作成する際にrealizeを取得しておき使えるようにしておきます。

```docker/Dockerfile
FROM golang:latest

RUN go get -u github.com/tockins/realize
```

##docker-composeの設定

Goアプリの本体サーバを4000番ポートで起動し、それに接続したgrpc-gatewayを3000番ポートで起動します。その前段にproxyがありローカル環境でlocal.realize_sample.netを開いた際に接続しに行きます。

起動をrealizeで行うので以下のように本体サーバとgrpc-gatewayを起動します。以下は本体サーバでの例です。grpc-gateway側は--nameの値を変えるだけです。

```
command: bash -c "realize start --name='server' --run"
```

# realizeの設定
relizeは設定ファイル.realize.yamlの設定を元に実行されます。
ここでは、再起動するタイミング（ファイル変更後、即時か）、実行するファイルとコマンド引数、監視対象のファイル拡張子と無視するディレクトリ等を設定できます。

## realize.yamlの構成
以下のような構成になってます。

```.realize.yaml
settings:
  # 全体設定
schema:
- name: server
  # 本体サーバ用の設定
- name: gateway
  # grpc-gateway用の設定
```

## 全体設定

チェックする期間を指定できます。0sだとファイル変更があったら即時で再ビルドおよび実行が行わられます。

```.realize.yaml
settings:
  legacy:
    force: false
    interval: 0s
```

## 本体サーバ側設定
buildするファイル、実行するファイルなどを設定しています。
vendor内を監視すると監視量が膨大になるので（自分の環境ではエラーになって落ちました）外しています。

```.realize.yaml
schema:
- name: server
  path: .
  commands:
    install:
      status: true
      dir: cmd/grpc-server
    run:
      status: true
      method: /go/bin/grpc-server
  watcher:
    extensions:
    - go
    paths:
    - /
    ignored_paths:
    - .git
    - .realize
    - vendor
```

## grpc-gateway側設定
本体サーバと似たような設定ですが、パラメータでappの4000番ポートに接続する事を渡しています。

```.realize.yaml
- name: gateway
  path: .
  commands:
    install:
      status: true
      dir: cmd/grpc-gateway
    run:
      status: true
      method: /go/bin/grpc-gateway
  args:
  - --endpoint=app:4000
  watcher:
    extensions:
    - go
    paths:
    - /
    ignored_paths:
    - .git
    - .realize
    - vendor
```

# 動作確認
ここまで設定がうまくいっていればコードを変更した時点で自動反映されるようになります。

まずサンプルのソースを動かすと、以下のAPIを実行可能になります。

```
$ curl http://local.realize_sample.net/user/1
{"id":1,"name":"John","age":18}
```

これはadapter/grpc/user.goに実装されているので、内容を書き換えます。
以下でAgeを19にします。

```adapter/grpc/user.go
// Get ユーザ情報を取得する
func (u *User) Get(ctx context.Context, in *proto.GetUserRequest) (*proto.User, error) {

	user := &proto.User{
		Id:   in.Id,
		Name: "John",
		Age:  18, //ここを19に書き換えて保存する
	}

	return user, nil
}
```

すると、多少ラグはありますが自動的にAPIに反映されます。

```
$ curl http://local.realize_sample.net/user/1
{"id":1,"name":"John","age":19}
```

protoに関してはgoファイルではないためprotoのコンパイルをしないと反映されません（できるようにできるかもしれませんがそこまではできていません）。コンパイルするとgoファイルができるので、その時点で自動反映されます。

# まとめ
開発手法によるかもしれませんが、直すと勝手にbuildが走る（エラーがあればそれも確認できる）というは非常に便利です。ぜひ試してみてください。