taisablog.com

taisa's engineer blog

docker-compose&dockerizeでGo+MySQLのWebサーバーを起動する

docker-composeでGo + MySQLを起動する場合、MySQLの起動を待ってGoのWebサーバーを起動する必要があります。実現するにはどうやらスクリプトを書かないとだめらしいですが、dockerizeを使うと簡単に実現できます。

サンプル用Goサーバを作成する

起動時にusersテーブルを作成し、リクエスト時にusersテーブルにレコードをinsertする簡単なサンプルアプリケーションです。

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"

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

type User struct {
    FirstName string `json:"firstName"`
    LastName  string `json:"lastName"`
}

var db *sql.DB

func main() {
    err := godotenv.Load()
    if err != nil {
        log.Fatal(err)
    }

    db = Conn()
    defer db.Close()

    _, err = db.Exec("CREATE TABLE IF NOT EXISTS users(id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, firstname VARCHAR(255) NOT NULL, lastname VARCHAR(255) NOT NULL)")
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/users", users)
    err = http.ListenAndServe(os.Getenv("LISTEN_PORT"), nil)
    if err != nil {
        log.Fatal(err)
    }
}

func Conn() *sql.DB {
    db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(mysql:3306)/%s", os.Getenv("MYSQL_USER"), os.Getenv("MYSQL_PASSWORD"), os.Getenv("MYSQL_SCHEMA")))
    if err != nil {
        log.Fatal(err)
    }
    return db
}

func users(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    user := User{
        FirstName: "John",
        LastName:  "Doe",
    }
    var users []User
    users = append(users, user)

    stmt, err := db.Prepare("INSERT INTO users(firstname, lastname) VALUES(?, ?)")
    if err != nil {
        log.Print(err)
        return
    }

    _, err = stmt.Exec(user.FirstName, user.LastName)
    if err != nil {
        log.Print(err)
        return
    }

    json.NewEncoder(w).Encode(users)
}

Dockerfile

https://github.com/jwilder/dockerize を取得します。

FROM golang:1.14
#FROM golang:1.14-alpine

# コンテナログイン時のディレクトリ指定
WORKDIR /opt/sandbox-docker-compose-go

ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz

# ホストのファイルをコンテナの作業ディレクトリにコピー
COPY . .
# ADD . .

# ビルド
RUN go build -o app main.go

# 起動
# CMD ["/opt/sandbox-docker-compose-go/app"]

docker-compose.ymlファイル

entrypointdockerizeを使ってtcp://mysql:3306に接続できるまで待ちます。

version: '3'
volumes:
  db-volume: # 追記
services:
  mysql:
    image: mysql:5.7.30
    container_name: sandbox-docker-compose-mysql-container
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_DATABASE: sandbox-docker-compose-mysql
    ports:
    - 13306:3306
    volumes:
      - db-volume:/var/lib/mysql
  app:
    # コンテナ名
    container_name: sandbox-docker-compose-app-container
    # イメージ名
    image: sandbox-docker-compose-app-image
    # ビルドに使用するDockerfileがあるディレクトリ指定
    build: .
    # マウントディレクトリ指定
    volumes:
      - .:/go/src/github.com/taisa831/sandbox-docker-compose-go
    ports:
      - "8000:8000"
    entrypoint:
      # mysqlが立ち上がるまで待つ
      - dockerize
      - -wait
      - tcp://mysql:3306
    command: ["/opt/sandbox-docker-compose-go/app"]
#    command: ["go", "run", "main.go"]
    restart: always
    depends_on:
      - mysql

構成

環境変数は.envから呼び出しています。

.├── Dockerfile
├── .env
├── docker-compose.yml
├── go.mod
├── go.sum
└── main.go

コンテナ作成&起動

$ docker-compose build
$ docker-compose up
# or
$ docker-compose up -d

Webサーバにアクセス

http://localhost:8000/users にアクセスしてJSONが表示されます。

[{"firstName":"John","lastName":"Doe"}]

MySQLに接続

MySQLに接続してテーブルが作成されレコードがinsertされていることを確認します。

$ mysql -u user -h 127.0.0.1 -p -P 13306 sandbox-docker-compose-mysql
mysql> show tables;
+----------------------------------------+
| Tables_in_sandbox-docker-compose-mysql |
+----------------------------------------+
| users                                  |
+----------------------------------------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+-----------+----------+
| id | firstname | lastname |
+----+-----------+----------+
|  1 | John      | Doe      |
|  2 | John      | Doe      |
|  3 | John      | Doe      |
+----+-----------+----------+
3 rows in set (0.00 sec)

無事にテーブルが作成されレコードがinsertされているのが確認できました。