Go + Docker

2021年2月27日

【Go】DockerでGoとMySQLコンテナ間接続

Docker ComposeでGoとMySQLコンテナ間接続をする

今回はローカル環境でGoコンテナとMySQLコンテナをDocker上に構築して、
GoコンテナからMySQLコンテナのデータにアクセスすることに挑戦します。

Dockerそのものの解説は割愛させていただきます。

今回やりたいこと

  • 過去の記事で作成した最もシンプルなGo(Echo)+MySQLのコンテンツをDockerコンテナ化する。
  • ローカル環境でGoコンテナから、MySQLコンテナ内のDBテーブルのデータ読み書きをする。
  • ブラウザで表示確認ができる(Curlで動作確認ではなく、きちんとブラウザに表示する)。
  • Dockerfileとdocker-compose.ymlの書き方、他のファイルの追加変更点を確認する。

ディレクトリ構造(変更箇所とDockerfileとdocker-compose.ymlの配置)

今回はDockerコンテナ化する改修箇所のみ記載します。他は元の記事を確認してください。
この記事のコンテンツを使用する理由は、もっともシンプルで余計な要素が無いので、問題点や変更点が明確になるためです。

└── ローカル上の任意のフォルダ
    ├─── app
    │    ├── controller
    │    │   └─ user_controller.go
    │    ├── go.mod
    │    ├── go.sum
    │    ├── main.go
    │    ├── model
    │    │   ├── db.go
    │    │   └── user.go
    │    ├── public
    │    │   └── views
    │    │       ├── signup.html
    │    │       └── user.html
    │    └── router
    │        └── router.go
    ├── docker-compose.yml
    ├── Dockerfile
    └── MySQL
        ├── Dockerfile
        ├── init
        │   └── 1_create.sql
        └── my.cnf

だいぶ試行錯誤して上記のディレクトリ構造にしましたが、正直これがベストとは思っていません。
他の方が、今回のようなGo+MySQLの場合に、Dockerfileとdocker-compose.ymlをどのように配置しているかを色々と調べたのですが、皆それぞれ相違があり、どれがベストかまだ判断がついていない状態です。
とりあえず試行錯誤の末、うまく動作したものになります。

「ローカル上の任意のフォルダ」に設置するように書いていますが、ローカル環境でGoの起動確認をする場合は、/go/src/以下のフォルダに「ローカル上の任意のフォルダ」を作成した方がいいと思います。

※私の場合は元記事のコンテンツが/go/src/sample/フォルダで作成していたため、/go/src/sample/app/・・・という形で作成しました。
goファイル内のimport文とディレクトリ階層が違うのでは?と思われるかもしれませんが、実行するのはあくまでもdockerコンテナ内でgoが正常に起動するディレクトリ構造になっていればいいので、Dockerfileでのディレクトリ指定を合わせれば、ローカル環境のディレクトリと相違していても問題ありません。

Dockerfile、docker-compose.yml、追加変更したファイルソースについて

Goコンテナ用のDockerfile、MySQLコンテナ用のDockerfileがあります。
新たに追加したMySQLフォルダ内には、MySQL設定ファイル、初期化用のファイルが入っています。

docker-compose.yml

docker-compose.ymlは下記のようにGoコンテナのサービス名を「app」、MySQLコンテナのサービス名を「mysql」としています。
「depends_on – mysql」は先にmysqlサービスを起動してから、goサービスを起動させるという指定です。
「tty: true」でコンテナの永続化をしています。

MYSQL_ROOT_PASSWORD=xxxxxxxxの部分は、DBのrootアカウントのパスワードを指定してください。

docker-compose.yml

version: "3" # composeファイルのバージョン
services: 
    app: # サービス名
        build: . # ビルドに使用するDockerfileの場所
        tty: true # コンテナの永続化
        ports: # ホストOSのポートとコンテナのポート番号 
            - "8080:8080"
        volumes:
            - ./app:/go/src/app # マウントディレクトリ
        depends_on: 
            - mysql

    mysql:
        build: ./mysql/
        volumes:
            # 初期データを投入するSQLが格納されているディレクトリ
            - ./mysql/init:/docker-entrypoint-initdb.d
            # 永続化するときにマウントするディレクトリ
            - ./mysql/data:/var/lib/mysql
        environment: 
            - MYSQL_ROOT_PASSWORD=xxxxxxxx
        ports:
            - "3306:3306"
        container_name: mysql-container

Dockerfile(Goコンテナ用)

Goコンテナ用のDockerfileは下記のように記述します。
ポイントとして、golangはAlpine Linux の Go イメージを指定しています。Alpine版は軽量という話なのですが比較してないのでわかりません。(実行後では対して変わらない説もあり)
バージョンは明示的に指定しています。よくバージョン指定を割愛したり、latestと指定しているケースもあります。
しかしこの手の技術は日進月歩で、バージョン指定が違うだけで動作しないケースがあるため明示するようにしています。

またGoの「ENV GO111MODULE=on」にして、go getで各Goモジュールをダウンロード後、realizeで永続化する前に「ENV GO111MODULE=off」にしています。
これはonにしたままだと、realizeでエラーになるためです。

# golangはversion1.16のalpine版を取得
FROM golang:1.16-alpine3.13
# 最初はGO111MODULE=onにする
ENV  GO111MODULE=on
# アップデートとgitのインストール
RUN apk update && apk add git
# appディレクトリの作成
RUN mkdir /go/src/app
# ワーキングディレクトリの設定
WORKDIR /go/src/app
# ホストのファイルをコンテナの作業ディレクトリに移行
ADD . /go/src/app

RUN go get -u gorm.io/gorm && \ 
    go get -u gorm.io/driver/mysql && \ 
    go get github.com/labstack/echo/v4 && \ 
    go get github.com/labstack/echo/v4/middleware 
# realizeするときはGO111MODULE=offにする
RUN GO111MODULE=off go get -u github.com/oxequa/realize

CMD ["realize", "start"]

MySQL/Dockerfile(MySQLコンテナ用)

MySQLコンテナ用のDockerfileは下記のように記述します。

※2021年2月27日現在、手元の環境ではMySQLを指定すると動作しないためMariaDBを指定していますが、問題ないのであれば普通に「FROM mysql:8.0」と記載でいいと思います。

環境

  • macOS High Sierra Version 11.2.2 チップ Apple M1
  • Docker for Mac 3.1.0 (Engine: 20.10.3)
# 使用するDockerイメージ
FROM mariadb:10.5.9

# ポート番号
EXPOSE 3306

# MySQL設定ファイルをイメージ内にコピー
ADD ./my.cnf /etc/mysql/conf.d/my.cnf

# docker runに実行される
CMD ["mysqld"]

MySQL/my.cnf

このファイルはMySQLの初期設定ファイルです。

[mysqld]
character-set-server=utf8

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8

MySQL/init/1_create.sql

Docker起動時にDBを作成します。
このGoコンテンツは空のDBが作成されていれば、マイグレーションで自動的にusersテーブルを作成するので、CREATE DATABASEとUSEのみ実行しています。
もし、初期データも挿入しておく必要があるアプリケーションであれば、ここでINSERTしておきます。

db_nameの部分も作成したDB名に適宜変更してください。

CREATE DATABASE db_name;
USE db_name;

app/model/db.go

過去の記事との変更箇所だけ説明します。

ハマるポイントとして、

dsn := “root:password@tcp(mysql-container:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local”

の箇所です。
@tcp(127.0.0.1:3306)/を、@tcp(mysql-container:3306)にしています。
この「mysql-container」は、docker-compose.ymlで指定したMySQLのコンテナ名になります。

※DB接続部分のroot:passwordはDBユーザー名とパスワードに適宜変更。
/db_name?の部分も作成したDB名に適宜変更してください。

package model

import (
	"log"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB
var err error

func init() {
	dsn := "root:password@tcp(mysql-container:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local"
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalln(dsn + "database can't connect")
	}
	DB.AutoMigrate(&User{})
}

それ以外のファイルに関しては、import文のディレクトリ名の部分を、sampleからappにしています。
変更箇所だけ記載します。

app/controller/user_controller.go

元記事のファイルから、import部分変更。(sampleからappに変更)

import (
	"net/http"
	"strconv"

	"app/model"

	"github.com/labstack/echo/v4"
)

app/router/router.go

元記事のファイルから、import部分を変更。(sampleからappに変更)

import (
	"io"
	"app/controller"
	"text/template"

	"github.com/labstack/echo/v4"
)

app/main.go

元記事のファイルから、import部分を変更。(sampleからappに変更)

import (
	"app/router"
)

Docker起動までの手順

DockerDocker Composeは事前にインストール、起動しておいてください。
ここではDockerとDocker Composeそのものの説明は割愛させていただきます。

コンテンツの保存先に移動します。

$ cd go/src/コンテンツの保存先

Dockerイメージのビルドします。

$ docker-compose build

イメージが作成できたか確認。

$ docker-compose images

コンテナを起動します。

$ docker-compose up -d

コンテナが起動できたか確認します。

$ docker-compose ps

Goコンテナのmain.goを実行します。

$ docker-compose exec app go run main.go

問題なければ、DockerからGoが起動するはずです。

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.2.0
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:8080

あとはブラウザから、http://localhost:8080/にアクセスすれば、元記事のようにユーザー名と年齢の入力フォームが表示されるはずです。

補足として・・・

MySQLコンテナ内に入りたい場合は下記のコマンド

$ docker-compose exec mysql bash

Goコンテナ名に入りたい場合は下記のコマンド(最後がbashで動かない場合はshにしてみるといいです)

$ docker-compose exec app sh

コンテナを停止したい場合は下記のコマンド

$ docker-compose stop

コンテナを削除したい場合は下記のコマンド

$ docker-compose rm

上記のコマンドで削除できない場合は強制削除コマンド

$ docker-compose rm -f

ここまで辿り着くまでに、GoコンテナとMySQLコンテナの接続に、ずいぶん手間取りました。
結果的にすべてのDockerコンテナとイメージを全部削除してやり直したら成功しました。
Dockerfileとdocker-compose.ymlの記述は間違っていないはずなのに、エラーを吐いて起動しない場合は、
いったんとDockerコンテナとイメージを全部削除してやり直すのも一つの解決方法かと思います。(本番用プロダクトとかではなく、学習用や開発用の場合に限りますが・・・)

今回の記事はここまでとなります。

LINEで送る
Pocket

label

Written by
isaka

バックエンドエンジニア

CONTACT

お問い合わせ、ご依頼などは下記電話番号かメールアドレスまでご連絡ください。
※内容により回答までお時間をいただく場合がございます、予めご了承ください。

tel. 06-6534-9333

10:00-19:00(※土日祝を除く)