taisablog

taisa's engineer blog

Go

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

投稿日:August 29, 2020 更新日:


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されているのが確認できました。

-Go

執筆者:

関連記事

Go言語 GORM+GinでTODOリストのAPIを作ってみた

前回の「Go言語 GORM+GinでTODOリストを作ってみた」に続いて「GORM+Gin」でTODOリストのAPIを作ってみました。ソースコードは前回からの差分だけを記載しています。できたものは下記URLから確認できます。 http://sandbox.taisablog.com/api/v1/ GinのGithub 事前情報 Webフレームワーク:Gin (https://github.com/gin-gonic/gin) ORM:GORM (https://gorm.io/docs) DB:MySQL ルーティングは今回はAPIなので以下としました。モデルをtasksにすればよかったと思いましたが一旦このままにしておきます。 [GIN-debug] GET /todo –> main.main.func1 (3 handlers) // 一覧表示 [GIN-debug] POST /todo –> main.main.func2 (3 handlers) // 新規作成 [GIN-debug] GET /todo/:id –> main.main.func3 (3 handlers) // 編集画面表示 [GIN-debug] PUT /todo/:id –> main.main.func4 (3 handlers) // 編集 [GIN-debug] DELETE /todo/:id –> main.main.func5 (3 handlers) // 削除 ディレクトリ構成 . ├── api │   └── v1 │   └── todo.go ├── controllers │   └── todo.go ├── db │   └── db.go ├── main.go ├── models │   └── todo.go ├── router    └── router.go router.go router.goにr.Group(“/api/v1”)のAPI用のグループを追加してルーティングを追加しました。 package router import ( “github.com/gin-contrib/cors” “github.com/gin-gonic/gin” “github.com/jinzhu/gorm” v1 “github.com/taisa831/sandbox-gin/api/v1” “github.com/taisa831/sandbox-gin/controllers” …

Go言語 GORM+GinでTODOリストを作ってみた

前回の「Go言語 ORMライブラリ GORMの使い方」に続いて「GORM+Gin」でTODOリストを作ってみました。使い方は「GitHubのREADME」を参考にしました。できたものは下記URLから確認できます。装飾は別途やれればと。 http://gin.taisablog.com/todo 事前情報 Webフレームワーク:Gin (https://github.com/gin-gonic/gin) ORM:GORM (https://gorm.io/docs) DB:MySQL ルーティングは通常のフォームだとPUT/DELETEが使えないので以下のようにしました。 [GIN-debug] GET /todo –> main.main.func1 (3 handlers) // 一覧表示 [GIN-debug] POST /todo –> main.main.func2 (3 handlers) // 新規作成 [GIN-debug] GET /todo/:id –> main.main.func3 (3 handlers) // 編集画面表示 [GIN-debug] POST /todo/edit/:id –> main.main.func4 (3 handlers) // 編集 [GIN-debug] POST /todo/delete/:id –> main.main.func5 (3 handlers) // 削除 main.goだけで作成した場合 main.goに全ての処理を記述しています。 package main import ( “github.com/gin-gonic/gin” “github.com/jinzhu/gorm” _ “github.com/jinzhu/gorm/dialects/mysql” “net/http” “strconv” ) type Todo struct { gorm.Model Text string Status uint64 } func main() { db, err := gorm.Open(“mysql”, “gorm:gorm@/sandbox_gin?charset=utf8mb4&parseTime=True&loc=Local”) if err != nil { panic(“データベースへの接続に失敗しました”) } defer db.Close() db.LogMode(true) db.AutoMigrate(&Todo{}) r := …

no image

DockerでGoのWebサーバーを起動する

ミニマムにやっておかないと忘れがちなのでメモ サンプル用Goサーバを作成する package main import ( “encoding/json” “net/http” ) type User struct { FirstName string `json:”firstName”` LastName string `json:”lastName”` } 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) json.NewEncoder(w).Encode(users) } func main() { http.HandleFunc(“/users”, users) http.ListenAndServe(“:8002”, nil) } 普通に起動して動作確認をします。 $ go run main.go http://localhost:8002/usersにアクセスするとJSON結果が出力されます。 [ { “firstName”: “John”, “lastName”: “Doe” } ] Dockerfile Dockerfileを作成します。alpineをつけるとよりミニマムなイメージができます。参考(https://hub.docker.com/_/golang?tab=description) FROM golang:1.14 #FROM golang:1.14-alpine # コンテナログイン時のディレクトリ指定 WORKDIR /opt/sandbox-docker-go # ホストのファイルをコンテナの作業ディレクトリにコピー COPY . . # ADD . . # ビルド RUN go build -o app main.go # 起動 CMD [“/opt/sandbox-docker-go/app”] 構成 $ …

[Golang] Goを始めたらまずはioパッケージを知るべし

Goを書いているとio.writerとio.readerを扱うケースが頻繁に出てきますが、これはioパッケージが多くの他のパッケージのインターフェースになっているからなのでioパッケージを知っておくことで開発が楽になります。 参考書籍 本書はGoのio.Writer、io.Readerからはじまりシステムの深いところまで丁寧に説明されているのでとてもおすすめです。ioパッケージから始まっているのも納得です。Webで無料で見れますし書籍版、PDF版で購入も可能です。 Web版 https://ascii.jp/elem/000/001/235/1235262/ 書籍版 Goならわかるシステムプログラミング ioパッケージのインターフェース一覧 ioパッケージのインターフェース一覧です。Goのインターフェースの実装は、明示的にインターフェースを明示的にimplementsせず、インターフェースを満たしていたらimplementsしていることになります。 Reader(インターフェース)Writer(インターフェース)Seeker(インターフェース)Closer(インターフェース)ReadWriter(複合インターフェース)ReadCloser(複合インターフェース)WriteCloser(複合インターフェース)ReadSeeker(複合インターフェース)WriteSeeker(複合インターフェース)ReadWriteCloser(複合インターフェース)ReadWriteSeeker(複合インターフェース)ReaderFrom(インターフェース)WriterTo(インターフェース)ReaderAt(インターフェース)WriterAt(インターフェース)ByteReader(インターフェース)ByteScanner(インターフェース)ByteWriter(インターフェース)RuneScanner(インターフェース)StringWriter(インターフェース) 複合インターフェース表 Goではインターフェースにインターフェースを食わせることができ、ioパッケージで作られている複合インターフェースは以下となります。 インターフェースio.Readerio.Writerio.Seekerio.Closerio.ReadWriter◯◯  io.ReadSeeker◯ ◯ io.ReadCloser◯  ◯io.WriteSeeker ◯◯ io.WriteCloser ◯ ◯io.ReadWriteSeeker◯◯◯ io.ReadWriteCloser◯◯ ◯ インターフェースを満たしている一覧を確認する方法 以下のコマンドを叩くと対象のインターフェースを満たしているものの一覧が確認できます。 $ GOPATH=/ godoc -http “:6060” -analysis type ## 実行後以下のURLにアクセスすると`io`パッケージが確認できる http://localhost:6060/pkg/io/ Readerのimplements一覧 テストも含んでいますが沢山あります pointer type *archive/tar.Reader implements Readerpointer type *archive/tar.regFileReader implements Readerpointer type *archive/tar.sparseFileReader implements Readerpointer type *archive/tar.testFile implements Readerpointer type *archive/zip.checksumReader implements Readerpointer type *archive/zip.pooledFlateReader implements Readerpointer type *bufio.Reader implements Readerpointer type *bufio_test.StringReader implements Readerpointer type *bufio_test.emptyThenNonEmptyReader implements Readerpointer type *bufio_test.errorThenGoodReader implements Readerpointer type *bufio_test.negativeReader implements Readerpointer type *bufio_test.rot13Reader implements Readerpointer type *bufio_test.scriptedReader implements Readerpointer type *bufio_test.slowReader implements Readerpointer type *bufio_test.testReader implements Readerpointer type *bytes.Buffer implements Readerpointer type *bytes.Reader implements Readerpointer type *bytes_test.negativeReader implements Readerpointer …

no image

【3分で作れる】Goのコマンドラインツール by Cobra

同僚のおすすめでコマンドラインツール作成にCobraを使いました。ものすごく簡単につくれるのですが、それでも少しハマったところがあったので、コマンドラインツールを作るまでの流れを書いておきます。 前提 go version go1.14.2 darwin/amd64利用ライブラリ:https://github.com/spf13/cobra本記事のサンプルコード:https://github.com/taisa831/sandbox-cobra Index 雛形を作成するコマンドを追加するコンフィグを追加するサブコマンドを追加するまとめ 雛形を作成する 雛形作成にはgeneratorを使います。go getしてcobraコマンドを利用可能にします。 go get -u github.com/spf13/cobra/cobra cobraコマンドを実行すると以下のようなusageが表示されます。 $ cobra Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application. Usage: cobra [command] Available Commands: add Add a command to a Cobra Application help Help about any command init Initialize a Cobra Application Flags: -a, –author string author name for copyright attribution (default “YOUR NAME”) –config string config file (default is $HOME/.cobra.yaml) -h, –help help for cobra -l, –license string name of license for the project …