Goで作ったWebアプリをDockerで動かす

## 概要
golangで作ったAPIをDocker上で動かします。
作ったAPIについて: [GolangでシンプルなRESTful APIを作ってみた](https://qiita.com/yuuulf/items/b464735dfb3486d248bf)

サンプルは[ここ](https://github.com/yuuulf/go-simple-api)


## Dockerfileを用意する


```Dockerfile
FROM golang:latest

WORKDIR /app
ADD . /app/

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags="-s -w" -installsuffix cgo -o hello main.go

CMD ["./hello"]

```

## Docker Compose ファイルを用意する

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

services:
  api:
    build: .
    ports:
      - 1323:1323
    environment:
      DATA_SOURCE_NAME: root:root@tcp(db:3306)/sample
      PORT: :1323
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample
    volumes:
      - ./migration:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"

```

## 起動
コンテナ作成に必要なファイルは揃ったのでbuildして起動する

```bash
$ docker-compose up --build -d
```
イメージが作られているか確認する

```bash
$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
helloworld_api                       latest              eaa02e55c990        11 seconds ago      908MB
```

実行できているか確認する

```bash
$ curl http://127.0.0.1:1323/users/1
{"ID":1,"Name":"suzuki"}
```

## イメージサイズを小さくする
イメージサイズを抑えるために[alpine](https://hub.docker.com/_/alpine/)を使う
[multi stage build](https://docs.docker.com/develop/develop-images/multistage-build/)という機能があるので、それを使う
次のようにFROMが2つあるようにし、中間ステージのものに対してはas builderのように名前をつけておく

```Dockerfile
FROM golang:alpine as builder

WORKDIR /app
ADD . /app/

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags="-s -w" -installsuffix cgo -o hello main.go

FROM alpine:latest

COPY --from=builder /app/hello /bin/hello

CMD ["/bin/hello"]

```

buildし、imageを確認する

```bash
$ docker-compose build
...
$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
helloworld_api                       latest              0dada9b8827e        25 seconds ago      12.2MB
```
908MB→12.2MBに!
約98.6%の削減!!

## オマケ
さらにイメージを小さくする

[scratch](https://hub.docker.com/_/scratch/)という何も含まれないイメージを元に、静的リンクしたバイナリを配置する
※ 使うときに証明書、タイムゾーン等に気をつけないといけない

参考) [GoとDockerでscratchを使うときに気をつけること](https://qiita.com/Tsuzu/items/774073bccaff32e9ee8d)

```Dockerfile

FROM golang:alpine as builder

WORKDIR /app
ADD . /app/

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags="-s -w" -installsuffix cgo -o hello main.go

FROM scratch 

COPY --from=builder /app/hello /bin/hello

CMD ["/bin/hello"]

```

```
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
helloworld_api                       latest              d96e6246972c        8 seconds ago       6.6MB
```




[upx](https://github.com/gruebel/docker-upx)を使ってさらに小さくする

```Dockerfile
FROM golang:alpine as builder

RUN apk --update add upx

WORKDIR /app
ADD . /app/

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags="-s -w" -installsuffix cgo -o hello main.go &&\
    upx /app/hello

FROM scratch 

COPY --from=builder /app/hello /bin/hello

CMD ["/bin/hello"]

```
```bash

$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
helloworld_api                       latest              0b7c833abf5c        9 seconds ago       2.58MB
```

|  | イメージサイズ | 1つ上からの削減率 | golangからの削減率 |
|:-------|-------:|-------:|-------:|
| golang | 908MB | - | - |
| alpine | 12.2MB | 98.6% | 98.6% |
| scratch | 6.6MB | 45.9% | 99.2% |
| scratch & upx | 2.58MB | 60.9% | 99.7% |

めっちゃ小さくなったw