Golang + Mysql + Dockerで環境構築したった

シュタインズゲートを最近初めて見て、ダルのような生き方をしたいと思いました。

前提知識

Golang

言わずと知れたGo言語の別称。Goだと海外では紛らわしいらしい。

Echo

Golang の webフレームワーク。簡単にルーティングなどができる。

Gorm

Go 版の Active Record。 Go のOR マッパー。

GOPATH

Go で開発をするにあたり必須の環境変数。必要なライブラリはGOPATHに全てインストールされる。

vi .bash_profile
export GOPATH=/Users/sipporyusyon/Desktop/workspace/go

Docker

コンテナ型の仮想化ツール。OS までは仮想化しないので、Virtual Box とかより軽い。ちなみに Golang で書かれている。

ファイル構成

カレントディレクトリを /Users/sipporyusyon/Desktop/workspace/go とする。
.
├── Dockerfile
├── docker-compose.yml
├── main.go
└── handler── create.go

各ファイル

docker-compose.yml

version: '3'
services:
  app:
    build: .  # docker-compose.yml はgo/ 直下に置いてるので . ディレクトリでビルドする
    container_name: go_app  # 自由に決められる
    volumes:
      - ./:/go # dockerコンテナ上の変更をローカルのmacに保存
    ports:
      - 1323:1323 # 開放するポート
    depends_on: # dbコンテナに接続する ビルドするのもdbコンテナが立ち上がってからになる
      - db
  db:
    image: mysql:latest # mysql のdockerイメージを取ってくる
    container_name: go_db # 自由に決められる
    volumes:
      - ./mysql_data:/var/lib/mysql # データ永続化のためのボリューム
      - ./sqls/init:/docker-entrypoint-initdb.d  
    ports:
      - 3306:3306 # 開放するポート
    environment:
      MYSQL_ROOT_PASSWORD: docker_pwd # sqlのrootユーザーでログインする時のパスワード
      MYSQL_DATABASE: docker_db # sqlで初回起動時に作成されるデータベース名
      MYSQL_USER: docker_user # sqlで初回起動時に作成されるユーザー名
      MYSQL_PASSWORD: docker_user_pwd # sqlで初回起動時に作成されるユーザーのパスワード
volumes:
  mysql_data: # ローカルのディレクトリにデータを保存

細かいことはこの記事を見てください。(別タブ開きます)

Dockerfile

FROM golang:1.11.2-alpine3.8 AS build  # GolangのDockerイメージ取ってくる

WORKDIR /go # Docker上の作業ディレクトリ設定
ADD . /go # ローカルのファイルをdocker にコピー

RUN apk update && \
    apk add --no-cache git && \
    go get github.com/go-sql-driver/mysql && \  # go で sql 使うのに必要なライブラリ
    go get github.com/labstack/echo/middleware && \ # echo インストール
    go get github.com/jinzhu/gorm # gorm インストール

CMD ["go", "run", "main.go"] # main.go ファイル実行 後述するがmain.goでサーバーを立てる

特に話すことはないですね。docker-compose.yml よりも先に Dockerfile の方が先に実行されるのは、考えれば当たり前ですが、最近知りました。

main.go

package main

import (
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"./handler"
)

func main() {
	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	// Routes
	e.GET("/users", handler.ListUser)
	e.GET("/users/:id", handler.GetUser)
	e.POST("/users", handler.CreateUser)
	e.PUT("/users/:id", handler.UpdateUser)
	e.DELETE("/users/:id", handler.DeleteUser)

	// Start server
	e.Logger.Fatal(e.Start(":1323"))
}

echo では echo インスタンスを作った後 e.start でサーバーを建てることができます。
ルーティングの中で handler パッケージの ListUser メソッドや CreateUser メソッドなど呼び出していますが、今回では CreateUser メソッドだけ触れます。
なお、echo 関連は基本この記事コピペ勉強させていただきました。(別タブ開きます)

handler/create.go

package handler

import (
	"net/http"
	"github.com/labstack/echo"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type User struct(
  Id int `gorm:"AUTO_INCREMENT"`
  Name string `gorm:"type:varchar(50);"`
)

func gormConnect() *gorm.DB {
	db,err := gorm.Open("mysql", "docker_user:docker_user_pwd@tcp(docker.for.mac.localhost:3306)/docker_db")
	if err != nil {
    panic(err.Error())
  }
  return db
}

func CreateUser(c echo.Context) (err error) {
	db := gormConnect()
	defer db.Close()

	u := &User{}
	if err := c.Bind(u); err != nil {
		return err
	}

	u.Name = "test"

	db.Table("User").Create(&u)

	return c.JSON(http.StatusCreated, u)
}

まず、import 内で gorm, echo のライブラリを呼ぶ。今回 mysql に接続できればいいので, User の構造体は簡易なものにした。
gormConnect 関数内で mysql コンテナに接続。gormのOpen関数を使うことで接続する。docker から繋ぐのでは ホストの名前が localhost ではまずいらしく、一番ハマった。正しくは docker.for.mac.localhost。ポートは mysql コンテナのポート。
docker_user, docker_user_pwd, docker_db はdocker-compose.yml で定義した環境変数の値でおけ。

CreateUser 関数内で db インスタンスを受け取る。echo.Contect は echo でレスポンス返す時に使う変数...くらいにしか理解がありません。
u に User 型の変数を定義し、u.Name に値を代入。db.Table("User").Create(&u) で User テーブルがあれば、POSTリクエストがあった時、Userテーブル内に id が1、nameがtest のユーザーが作られるはずです。

確認

[sipporyusyon@MBP go] docker-compose up 

コンテナを起動させる。

[sipporyusyon@MBP go] docker exec -it go_db bash 

mysql コンテナ内に入る。

root@be9383fe56c8:/#  mysql -u docker_user -h localhost -D docker_db -p
# パスワードを聞かれるので入力(docker-compose.ymlで指定したdocker_user_pwd)

使用するユーザー名とデータベース名を指定して、mysql に接続。

mysql> CREATE TABLE User (Id int auto_increment, Name varchar(50));

Id と Name をカラムに持つ User テーブル作成。
コンテナを一度出て,

[sipporyusyon@MBP go] curl -v POST -H "Content-Type: application/json" "http://localhost:1323/users"

curl で CreateUser のある url へ POST リクエストを送る。
無事Userテーブルに testユーザーが作られたら成功。

あとがき

ここまで書くのに2時間かかって、現在夜の3時...
何がやばいって平日の木曜なのがやばいですね。