目次
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起動までの手順
DockerとDocker 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コンテナとイメージを全部削除してやり直すのも一つの解決方法かと思います。(本番用プロダクトとかではなく、学習用や開発用の場合に限りますが・・・)
今回の記事はここまでとなります。