Goの開発環境でDockerを利用する

[Go3 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/go3)の9日目を担当するtheoden9014です。よろしくお願いします。

Makefileについて書こうと思ったのですが、内容が同じような記事が先に出てしまったようなので、少し趣向を変えたタイトルにしました。

この記事では、`Makefile`と`Dockerfile`を上手く利用することによって、Docker内でのビルドやデバッグを簡単にする方法についてご紹介します。
この手法であれば、開発者は手元にはDockerをインストールするだけでGoを使った開発を行えるようになります。

今回は以下のリポジトリをベースとしてご紹介していきます。
https://github.com/theoden9014/go-startkit/tree/qiita

## 背景
Goで開発する際には`$GOPATH`の知識は必要不可欠です。
vgoの普及によって`$GOPATH`の重要性は下がってくるでしょうが、現段階では全く考慮しないわけには行きません。
初心者でも簡単に`Hello World`ができるような環境を作りたいな、と思い今回の記事を書きました。

## Dockerfile
以下の`Dockerfile`はdockerのマルチステージビルドを用い、以下のステージに分けています

- development
    - 開発を行うための周辺環境が整っているステージ
- builder
    - ビルドを行うための周辺環境が整っているステージ

ステージを指定しない場合は、`builder`ステージでビルド後、成果物を`alpine`へコピーし、バイナリを実行するだけのコンテナイメージをビルドすることができます。

```dockerfile
#
# Use multi stage build so require docker version higher 17.05.
#
FROM golang:1.11 AS development
ARG name=
ARG repository=
RUN apt-get update -y
RUN apt-get install -y doxygen doxygen-gui graphviz
WORKDIR /go/src/${repository}
COPY Makefile ./
RUN make setup
CMD ["/bin/true"]

FROM development AS builder
COPY Gopkg.toml Gopkg.lock ./
RUN make vendor
COPY . .
RUN make build
CMD ["/bin/true"]

FROM alpine
ARG name=
ARG repository=
ENV PORT 8080
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/${repository}/${name} ./app
ENTRYPOINT ["./app"]
```

## Makefile
https://github.com/theoden9014/go-makefile/blob/qiita/Makefile
上記の`Makefile`のDockerに関する部分について軽く説明していきます。

```makefile
REPOSITORY              ?= github.com/theoden9014/go-startkit
NAME                    ?= go-startkit

...

#
# Docker
#
DOCKER                          ?= docker
DOCKER_IMAGE_NAME               := $(NAME)
DEVELOP_DOCKER_IMAGE_NAME       := $(NAME)-dev
BUILD_DOCKER_IMAGE_NAME         := $(NAME)-build
DOCKERFILE                      := Dockerfile
DEVELOP_STAGE                   := development
BUILD_STAGE                     := builder
DEFAULT_DOCKER_BUILD_OPTS       := -f $(DOCKERFILE) --build-arg name=$(NAME) --build-arg repository=$(REPOSITORY)
DOCKER_BUILD_DEVELOP_STAGE_OPTS := $(DEFAULT_DOCKER_BUILD_OPTS) -t $(DEVELOP_DOCKER_IMAGE_NAME) --target $(DEVELOP_STAGE)
DOCKER_BUILD_BUILD_STAGE_OPTS   := $(DEFAULT_DOCKER_BUILD_OPTS) -t $(BUILD_DOCKER_IMAGE_NAME) --target $(BUILD_STAGE)
DOCKER_BUILD_OPTS               := $(DEFAULT_DOCKER_BUILD_OPTS) -t $(DOCKER_IMAGE_NAME)

...

# Docker targets from here
docker: ## Build server image
	docker build $(DOCKER_BUILD_OPTS) .
login: build-develop-stage ## Login to development environment with container
	docker run --rm -it \
	-v $(PWD):/go/src/$(REPOSITORY) \
	-p 8080:8080 \
	$(DEVELOP_DOCKER_IMAGE_NAME) \
	/bin/bash
docker-check: build-develop-stage ## Run 'make check' inside container
	docker run --rm -it \
	$(DEVELOP_DOCKER_IMAGE_NAME) \
	make check
docker-coverview: build-develop-stage ## Run 'make coverview' inside container
	docker run --rm -it \
	-v $(PWD):/go/src/$(REPOSITORY) \
	$(DEVELOP_DOCKER_IMAGE_NAME) \
	make coverview
docker-graph: build-develop-stage ## Run 'make graph' inside container
	docker run --rm -it \
	-v $(PWD):/go/src/$(REPOSITORY) \
	$(DEVELOP_DOCKER_IMAGE_NAME) \
	make graph
build-develop-stage:
	docker build $(DOCKER_BUILD_DEVELOP_STAGE_OPTS) .
build-build-stage:
	docker build $(DOCKER_BUILD_BUILD_STAGE_OPTS) .

.PHONY: docker login docker-check docker-coverview docker-graph build-develop-stage build-build-stage
```

上記のMakefileを利用することで以下のようなことが行えるようになります。

- `make login`
    - プロジェクトルートディレクトリをコンテナ内の`$GOPATH`配下へマウントし、コーディングはローカルのIDEで、ビルドやデバッグはコンテナ内で行えます
- `make docker-*`
    - コンテナ内でテストやパッケージ依存関係のグラフ作成等を行えるようにしています


## 最後に
以上、簡単ではありましたがGo言語をDockerで開発する方法の紹介でした。
ここまでご覧いただきありがとうございました。

明日は@pankonaさんです！