Go-langのAPIサーバをalpine Linuxでコンテナにする～EHW2018「アプリ④」

# 概要

このエントリは、「Enterprise "hello, world" 2018 Advent Calendar 2018」の12/18向けのものです。このAdvent Calendarでは、複数個のエントリにまたがる話の流れも鑑みつつ、なるべく1エントリで１つのトピックをカバーできるようにする予定です。

このエントリで記載するトピックは、Go-langのAPIサーバを[alpine Linux](https://alpinelinux.org/)でコンテナにすることです。Advent Calendarの第16日目「[Go-lang+GinでAPIサーバを作る一歩目～EHW2018「アプリ②」](https://qiita.com/hrkt/items/a524910b14fc8c05038f)」で作ったアプリをコンテナ化の対象とします。

# 前提

## おことわり

- このEnterpfise "hello, world"シリーズは、ネタのためのエントリです。実環境でそのまま利用ことを目的とはしていません。
- 動かしやすさを優先してセキュリティを意図的に低くする設定など入れてありますのでご注意ください。

## 想定読者

「Enterprise "hello, world" 2018」的なネタとしては、下記のような状況を想定しています。

> 第17日目でフロント側をコンテナにしたので、次はAPIサーバ側をコンテナにする必要がある。

# Dockerコンテナに押し込める

## Dockerfile

Advent Calendar第17日目と同じような手法で、ビルド作業用のコンテナと、本番用のコンテナの2種類を使って構成します。

```dockerfile
# BUILD
FROM golang:1.11.4-alpine as build-stage
ARG version
ARG revision
RUN touch /${version} && touch /${revision}
COPY . /go/src/ehw2018/greetings-server
RUN go install -ldflags="-s -w -X \"main.Version=${version}\" -X \"main.Revision=${revision}\" -extldflags \"-static\"" ehw2018/greetings-server

# Production
FROM alpine as production-stage
COPY --from=build-stage /go/bin/greetings-server .
ENV PORT 8080
ENV GIN_MODE=release
CMD ["./greetings-server"]

```

ポイントは、下記です。

- Dockerfileの[ARG](https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact)を使い、Dockerfile内で変数を指定しています。この値は、docker build実行時の引数で指定します。

```makefile
(第16日目のMakefileに追記した部分)
build-container:
        sudo docker build -t $(LABEL) --build-arg version=$(VERSION) --build-arg revision=$(REVISION) .

run-container:
        sudo docker run -p 8080:8080 $(LABEL)

check-container:
        curl http://localhost:8080/api/greeting
```

## ビルド

ビルドしてみます。

```
$ make build-container
sudo docker build -t greetings-server-test:latest --build-arg version=v0.0.1 --build-arg revision=67d77b1 .
Sending build context to Docker daemon  19.03MB
Step 1/11 : FROM golang:1.11.4-alpine as build-stage
 ---> f56365ec0638
Step 2/11 : ARG version
 ---> Running in f5c9f5e45c48
Removing intermediate container f5c9f5e45c48
 ---> 05b214ba4e2b
Step 3/11 : ARG revision
 ---> Running in eb42949332cd
Removing intermediate container eb42949332cd
 ---> aa2ab18d4bf1
Step 4/11 : RUN touch /${version} && touch /${revision}
 ---> Running in 47d5fb254294
Removing intermediate container 47d5fb254294
 ---> 4651732e2516
Step 5/11 : COPY . /go/src/ehw2018/greetings-server
 ---> c920c25c5c42
Step 6/11 : RUN go install -ldflags="-s -w -X \"main.Version=${version}\" -X \"main.Revision=${revision}\" -extldflags \"-static\"" ehw2018/greetings-server
 ---> Running in 22f4ee877c04
Removing intermediate container 22f4ee877c04
 ---> c8b5498bf1c1
Step 7/11 : FROM alpine as production-stage
 ---> 3f53bb00af94
Step 8/11 : COPY --from=build-stage /go/bin/greetings-server .
 ---> a49f6d43e870
Step 9/11 : ENV PORT 8080
 ---> Running in 6e873c85b2f8
Removing intermediate container 6e873c85b2f8
 ---> 17533ef298ea
Step 10/11 : ENV GIN_MODE=release
 ---> Running in cd532354b657
Removing intermediate container cd532354b657
 ---> 7c8ff932e1f6
Step 11/11 : CMD ["./greetings-server"]
 ---> Running in d4e2e8cbf5cf
Removing intermediate container d4e2e8cbf5cf
 ---> a0af4779a165
Successfully built a0af4779a165
Successfully tagged greetings-server-test:latest
```

1つめのビルド用のコンテナの中でgoのプログラムが作られ、2つめのコンテナを作る際にプログラムがコピーされました。

## 動かしてみる

ターミナルを2つ用意し、一方でサーバを動かします。

```shell-session
$ make run-container
sudo docker run -p 8080:8080 greetings-server-test:latest
Greetings Server : Version:v0.0.1 Revision:67d77b1
1 :8080
[GIN] 2018/12/23 - 01:06:07 | 200 |     341.924μs |      172.17.0.1 | GET      /api/greeting
[GIN] 2018/12/23 - 01:06:07 | 200 |     686.401μs |      172.17.0.1 | GET      /api/greeting
```

もう一方で、クライアントを動かしてアクセスしてみます。

```shell-session
$ make check
curl http://localhost:8080/api/greeting
{"message":"hello, world"}echo ""
```

無事APIからJSONを取得できました。

## コンテナのサイズ

alpineを使っているので、コンテナは15.4MBと、かなり小さめに作ることができました。

```shell-session
$ sudo docker images | grep greetings
[sudo] password for hiroki:
greetings-server-test                                        latest              a0af4779a165        6 minutes ago       15.4MB

```

## 補足

エントリを書くにあたり、第16日目で作ったバイナリをそのままalpineにおいてあげれば動くんじゃないの、というつもりで作業を始めたのですが、
↓のような事態が起きたりするので、手抜きせずに、ビルド用のコンテナも用意することにしました。

```
/ # ldd greetings-server
        /lib64/ld-linux-x86-64.so.2 (0x7f3e0c652000)
        libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f3e0c652000)
        libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f3e0c652000)
Error relocating greetings-server: __vfprintf_chk: symbol not found
Error relocating greetings-server: __fprintf_chk: symbol not found
```


# まとめ

このエントリでは、「Enterprise "hello, world" 2018 Advent Calendar 2018」(EHW2018)の18日目として、Go-langのAPIサーバをalpineでコンテナにすることをトピックとして取り上げました。

本エントリのソースコードは、[https://github.com/hrkt/greetings-server/releases/tag/0.0.2](https://github.com/hrkt/greetings-server/releases/tag/0.0.2)にあげてあります。

EHW2018のネタとしては、このあと、設定できる項目を増やしたうえで、k8sにデプロイすることを考えています。