docker-composeでgolangとMySQLを繋ぐ
【環境】
MacBook Air (M1, 2020)
OS: MacOS Big Sur version11.6
Docker Desktop for Mac version4.5.0
docker-composeでgolangとMySQLのコンテナを作り、Dockerのnetworkを通じてgolangからMySQLを操作します。
MySQLコンテナの初期化処理でarticleテーブルを作成しデータを2つ登録、golangからそれらを確認するところまで進めます。
ディレクトリ構成
go_docker
├── docker-compose.yml
├── golang
│ ├── Dockerfile
│ └── src
│ ├── article
│ │ └── article.go
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── mysql
├── .env
├── Dockerfile
└── init
└── create_table.sh
mysqlディレクトリ内に関してはdocker-composeでMySQLを起動するで作成したものとほとんど同じですので、それらについては割愛します。
docker-compose.yml
version: '3.8'
services:
go:
container_name: go
build:
context: ./golang
dockerfile: Dockerfile
tty: true
ports:
- 8080:8080
env_file:
- ./mysql/.env
depends_on:
- db
volumes:
- type: bind
source: ./golang/src
target: /go/src
networks:
- golang_test_network
db:
container_name: db
build:
context: ./mysql
dockerfile: Dockerfile
tty: true
platform: linux/x86_64
ports:
- 3306:3306
env_file:
- ./mysql/.env
volumes:
- type: volume
source: mysql_test_volume
target: /var/lib/mysql
- type: bind
source: ./mysql/init
target: /docker-entrypoint-initdb.d
networks:
- golang_test_network
volumes:
mysql_test_volume:
name: mysql_test_volume
networks:
golang_test_network:
external: true
- MySQL(dbサービス)についてはdocker-composeでMySQLを起動するとほとんど同じです。
- ymlファイルの最下部にgolang_test_networkというDockerのnetworksを記述しました。
各サービスのnetworksに指定することで、サービス名を使ってサービス間の通信を行うことができます。
今回で言うとgolangでMySQLへ通信する際、hostに「db」というサービス名を指定することで通信を行うことができます。 - goサービスのdepends_onはサービス起動の順番を設定します。
ここで指定したサービス(db)が起動した後に自身(go)が起動します。
golang/Dockerfile
FROM golang:1.17.7-alpine
RUN apk update && apk add git
WORKDIR /go/src
CMD ["go", "run", "main.go"]
BaseImageはalpineサーバーのgolangです。gitだけ導入しておきます。
作業ディレクトリを設定し、go run main.goを実行します。
main.go
package main
import (
"database/sql"
"fmt"
"go_docker/article"
"log"
"os"
"time"
_ "github.com/go-sql-driver/mysql"
)
func open(path string, count uint) *sql.DB {
db, err := sql.Open("mysql", path)
if err != nil {
log.Fatal("open error:", err)
}
if err = db.Ping(); err != nil {
time.Sleep(time.Second * 2)
count--
fmt.Printf("retry... count:%v\n", count)
return open(path, count)
}
fmt.Println("db connected!!")
return db
}
func connectDB() *sql.DB {
var path string = fmt.Sprintf("%s:%s@tcp(db:3306)/%s?charset=utf8&parseTime=true",
os.Getenv("MYSQL_USER"), os.Getenv("MYSQL_PASSWORD"),
os.Getenv("MYSQL_DATABASE"))
return open(path, 100)
}
func main() {
db := connectDB()
defer db.Close()
article.ReadAll(db)
}
database/sql
database/sqlはgolangでデータベースに接続する時に使うパッケージです。
今回はMySQLに接続するのでドライバとして"github.com/go-sql-driver/mysql"もインポートします。パッケージの前に_(アンダースコア)を付けるとパッケージ内のinitメソッドだけが実行されます。(プログラム中では使わない。)
func connectDB() *sql.DB
mainメソッドの1行目で呼び出しています。
path変数にはdatabase/sqlで指定する接続情報です。
[user名]:[password]@tcp([host名]:[port])/database名
という形で記述します。
docker-composeのnetworks設定したので、host名はdbで接続することができます。
os.Getenv("環境変数")で環境変数を使うことができます。
func open(path string, count uint) *sql.DB
sql.Open()でデータベースと接続します。
第一引数にデータベースの種類を、第二引数に先ほど設定したpathを指定します。
db.Ping()でdbサービスへの接続確認をし、失敗したらcountを減らし、openメソッドを再起で呼びます。
article.go
package article
import (
"database/sql"
"fmt"
"log"
)
type Article struct {
id int
title string
body string
}
func ReadAll(db *sql.DB) {
var articles []Article
rows, err := db.Query("select * from article;")
if err != nil {
panic(err)
}
for rows.Next() {
article := Article{}
err = rows.Scan(&article.id, &article.title, &article.body)
if err != nil {
panic(err)
}
articles = append(articles, article)
}
rows.Close()
fmt.Println(articles)
}
func ReadAll(db *sql.DB)
mainメソッド3行目から呼ばれます。
sql.databaseパッケージで複数の検索結果を取得する時(SELECT等)はQuery、単一データを取得する時はQueryRow、データを取得しない時はExec(INSERT, CREATE, UPDATE, DELETE等)を使います。
今回はarticleテーブルの全データを取得するのでQueryメソッドを使います。
やってみる
それでは実際にdocker-composeを実行してみます。
% docker-compose up -d
[+] Running 3/3
⠿ Volume "mysql_test_volume" Created 0.0s
⠿ Container db Started 0.3s
⠿ Container go Started
goコンテナのLogを確認してみます。
go: downloading github.com/go-sql-driver/mysql v1.6.0
retry... count:99
retry... count:98
retry... count:97
retry... count:96
retry... count:95
retry... count:94
retry... count:93
retry... count:92
retry... count:91
retry... count:90
retry... count:89
retry... count:88
retry... count:87
db connected!!
[{1 記事1 記事1です。} {2 記事2 記事2です。}]
MySQLの起動に時間がかかり何度かretryしていますが、無事articleテーブルのデータを取得することができました。
参考
Discussion
良記事ありがとうございます!
最初に一秒待たせるだけでもできました!同じ状況発生してたのですが・・助かりました。