Go + docker で Mysqlを使う(multi-stage builds & docker-composeで)

## やりたいこと
普通にイメージを作成するとどうしても大きくなってしまうGoイメージですが、multi-stage buildsにすると
格段にサイズが小さくすることができます。
今回はdocker(docker-compose)で環境を準備し Go から MySQL にアクセスする方法について書いていきます。

基本的に、以前自分が勉強でtodolist用のAPIを作成するために作成したリポジトリからの出典になります。
https://github.com/t0w4/toDoListBackend

### Dockerfile
```dockerfile
FROM golang:1.11.2-alpine3.8 AS build

WORKDIR /
COPY . /go/src/github.com/t0w4/toDoListBackend
RUN apk update \
  && apk add --no-cache git \
  && go get github.com/go-sql-driver/mysql \
  && go get github.com/google/uuid \
  && go get github.com/gorilla/mux
RUN cd /go/src/github.com/t0w4/toDoListBackend && go build -o bin/todolist main.go

FROM alpine:3.8
COPY --from=build /go/src/github.com/t0w4/toDoListBackend/bin/todolist /usr/local/bin/
CMD ["todolist"]
```
[goのdockerの公式サイト](https://hub.docker.com/_/golang/)も見ましょう。
docker上では$GOPATHが /go になることも注意しましょう。

### docker-compose.yml
```
version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql
      - ./sqls/init:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: gerhuhaer
      MYSQL_DATABASE: todoList
      MYSQL_USER: t0w4
      MYSQL_PASSWORD: faweufhli

  todolist:
    build: .
    depends_on:
      - db
    ports:
      - "8080:8080"
    environment:
      MYSQL_DB_HOST: db
      MYSQL_DB: todoList
      MYSQL_PORT: 3306
      MYSQL_USER: t0w4
      MYSQL_PASSWORD: faweufhli
    restart: always

volumes:
  mysql_data:
```
注意！！！！！！！
今回特に外部公開もしていないのに勉強用だからいいですが、外部から見えるような場所でID, パスワードをほいほい公開しないようにしましょう。
各環境変数の要不要、意味については[公式](https://hub.docker.com/_/mysql/)を読みましょう。下手解説より100倍いいです。

`/docker-entrypoint-initdb.d` にsqlを配置すると初期起動時に実行してくれるますので、DBやテーブルの作成、初期データの投入などの用途で使えます。
また永続化のためMySQLのデータはローカルにマウントさせています。

### GoでのMySQLへのアクセス
GoでMySQLを扱う部分については[RevelでGORMを使ったMySQL Associationの実装](https://qiita.com/sawadashota/items/8059ecf0415985b12a56)が参考になったので
そちらを見てください。。。。だと不親切なので、以下に自分がORMを使わないで書いた実装があるんで載せます。

```golang
package db

import (
	"database/sql"
	"fmt"
	"os"
	"strings"

	_ "github.com/go-sql-driver/mysql"
)

func Init() (*sql.DB, error) {
	connectionString := getConnectionString()
	db, err := sql.Open("mysql", connectionString)
	if err != nil {
		return nil, err
	}
	return db, nil
}

func getParamString(param string, defaultValue string) string {
	env := os.Getenv(param)
	if env != "" {
		return env
	}
	return defaultValue
}

func getConnectionString() string {
	host := getParamString("MYSQL_DB_HOST", "localhost")
	port := getParamString("MYSQL_PORT", "3306")
	user := getParamString("MYSQL_USER", "root")
	pass := getParamString("MYSQL_PASSWORD", "")
	dbname := getParamString("MYSQL_DB", "todoList")
	protocol := getParamString("MYSQL_PROTOCOL", "tcp")
	dbargs := getParamString("MYSQL_DBARGS", " ")

	if strings.Trim(dbargs, " ") != "" {
		dbargs = "?" + dbargs
	} else {
		dbargs = ""
	}
	return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s%s",
		user, pass, protocol, host, port, dbname, dbargs)
}
```
差分は、initの部分と、参考記事が設定ファイルから取得していたところを環境変数から取得するようにしたところくらいです。
自分が開発していた時は、この`getConnectionString`が返すMySQLの接続情報がわからず時間を食ってしまいました。。。

## 終わりに
記事としてはとても短いので「えっ！これだけ？」と思われるかもしれないですが、意外と上記の内容がまとまった記事というのが見つからなかったので、今後Goをdockerで作っていきたい人たちの参考になれば嬉しいです。