Docker+golang+Nginxでリバースプロキシ・ホットリロードな開発環境を構築する

# はじめに

こんにちは、3Dとかゲームがメインの[yoship1639](https://twitter.com/yoship1639)です。

Web知識皆無な人間だったのですが、最近Web系を触るようになり(強制)、その中でDocker+golang+NginxでWebサービスを作るという状況になったので、備忘録含めどうやったらDocker上でNginx+golangな環境を構築できるのかをご紹介したいと思います。

なるべく最小限の環境を構築したいので、Nginxは設定としてリバースプロキシ部分のみ、golangはrealizeによるホットリロード機能のみを記述します。

一応、私の環境はこんな感じです
・Win10 64bit Home
・VSCode 1.34.0
・Virtualbox 5.2.8 r121009
・Docker 18.03.0-ce

# 構築

今回作成するプロジェクト名はわかりやすく「docker_go_nginx」とします。
配置場所は`go/src/github.com/docker_go_nginx`です。
フォルダ構成は以下の通りです。

```
docker_go_nginx
 ├─ app
 │  ├─ main.go
 │  └─ .realize.yml
 ├─ nginx
 │  ├─ Dockerfile
 │  └─ nginx.conf
 ├─ docker-compose.yml
 └─ Dockerfile
```
それぞれについて見ていきます

## docker-compose.yml

docker-composeファイルは以下のようになります。

```yml:docker-compose.yml
version: "3"

services:
  golang:
    tty: true
    build: "."
    volumes:
      - ./app:/go/src/github.com/docker_go_nginx/app
    ports:
      - "3000"
    command: bash -c "cd app && realize start --server"
  nginx:
    build: ./nginx
    ports:
      - "80:80"
    depends_on:
      - "golang"
```
golangコンテナのポートはNginxのプロキシを通すので80ではなく3000を指定しています。逆にNginxのコンテナに80を指定します。また、golangコンテナ起動時にホットリロードを開始させます(realize start --server)。これによってついでにmain.goが自動で走ります。
nginxはgolangのリバースプロキシになるので`depends_on: - "golang"`を指定します。

## Dockerfile (golang用)

```dockerfile:Dockerfile
FROM golang:latest

RUN go get -u github.com/oxequa/realize && \
    go get -u github.com/gorilla/mux

WORKDIR /go/src/github.com/docker_go_nginx/
```
golangは最新のイメージを取得し、realizeパッケージをgo getします。
`WORKDIR /go/src/github.com/docker_go_nginx/`は後でコンテナのシェルに入ったときに一々cdするのが面倒なので予めワークディレクトリとして指定しておきます。

## Dockerfile (nginx用)

```dockerfile:Dockerfile
FROM nginx:latest
COPY ./nginx.conf /etc/nginx/nginx.conf
```
設定はホスト側のnginx.confを適用させたいので、コンテナ側の`/etc/nginx/nginx.conf`にコピーします。

## nginx.conf
```conf:nginx.conf
worker_processes auto;

events {
    worker_connections  1024;
}

http {
    server {
         listen       80;
         location / {
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection "upgrade";
             proxy_set_header Host $host;

             proxy_pass http://golang:3000;
         }
    }
}
```
非常に最小限なnginx.confです。80ポートをlistenしたらリバースプロキシとして`http://golang:3000`にパスします。これで、Nginx経由でwebアプリ(golang)にアクセスします。
また、`http://golang:3000`の`golang`はservicesに記述したサービス名と同じにしてください。そうしないとちゃんと動いてくれません。

## main.go
```go:main.go
package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

func rootHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Hello world!")
}

func main() {

	r := mux.NewRouter()
	r.HandleFunc("/", rootHandler)

	http.Handle("/", r)
	http.ListenAndServe(":3000", nil)
}

```
main.goに関しては特に何も言うことないですね。
3000ポートをlistenして、ルートにアクセスがあったらHello world!するだけです。

## .realize.yaml
```yaml:.realize.yaml
settings:
  legacy:
    force: true
    interval: 0s
server:
  status: true
  open: false
  port: 5002
  host: localhost
schema:
- name: app
  path: .
  commands: 
    run:
      status: true
  watcher:
    extensions:
    - go
    paths:
    - /
    ignored_paths:
    - .git
    - .realize
    - vendor
```
realizeは自動生成されたものを少し修正して、コンテナの立ち上がりと同時に自動で実行させるようにしています。

# 実行

ファイル一式そろえたら`docker-compose up`します

```
$ cd docker_go_nginx
$ docker-compose up
Starting dockergonginx_golang_1 ... done
Starting dockergonginx_nginx_1  ... done
Attaching to dockergonginx_golang_1, dockergonginx_nginx_1
golang_1  | [04:08:01][APP] : Watching 1 file/s 0 folder/s
golang_1  | [04:08:01][APP] : Install started
golang_1  | [04:08:01][REALIZE] : Started on localhost:5002
golang_1  | ⇨ http server started on 127.0.0.1:5002
golang_1  | [04:08:02][APP] : Install completed in 1.330 s
golang_1  | [04:08:02][APP] : Running..
golang_1  | [04:08:44][APP] : Hello world!
```

realizeのおかげで自動でmain.goが走るので、あとは`192.168.99.100`にブラウザでアクセスして真っ白い画面がでたら成功です。ついでにHello worldが出力されます。

これで、リバースプロキシ＋ホットリロードな開発環境ができたので、あとは煮るなり焼くなりするだけです。

# まとめ

Docker上でNginx+golangによるリバースプロキシ、ホットリロードな最小限の開発環境を構築しました。
ホットリロードを機能させておくと編集するたびにコンパイルしなおさなくてもいいのでgolangの開発がとてもスムーズになりおススメです。

Web初心者なので間違ってたらコメントください\_(:3」∠)\_