はじめに
最近、色んな企業のサービスを見ていて、Go言語が使われている事が多くどんなものかと勉強しています。
まずは環境構築してみたので下記へ手順をまとめておきます。
目次
・DockerでGoを起動する
・GoでWebページを作成して、「Hello, World!」を出力する
・DockerでMySQLを起動する
・GoとMySQLを接続する
・GoでCRUD処理を実装
・Dockerでphpmyadminを起動する
・参考文献
DockerでGoを起動する
まずはDockerでGoを起動させます。
1. 作業ディレクトリ直下へ以下ファイルを作成します。
・Dockerfile
・docker-compose.yml
・main.go
FROM golang:1.17.1-alpine3.13 as builder
WORKDIR /go/src
version: '3'
services:
go:
build:
context: .
dockerfile: Dockerfile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/app
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
2. ターミナルを開いて、docker-compose up
を実行します。
Hello, World!と出ればGoの起動は成功です!
・Dockerfile
Goのコンテナを作成します。
WORKDIR /go/src
の指定により以降全ての動作は/go/src
以下で行われます。
・docker-compose.yml
Dockerfileで作成したコンテナの起動設定を記載します。
Dockerfileに記載されたコンテナ起動後、go run main.go
のコマンドを叩いてmain.goを起動します。
・main.go
Goの処理を書くところです。
GoでWebページを作成して、「Hello, World!」を出力する
次にGoでWebページを作っていきます。フレームワークはGinを使います。
1. 以下の通り、各ファイルの作成・更新を行います。
・DockerfileへGinインストールを追記
・docker-compose.ymlへportsの記述を追記
・main.goの内容を変更
・templates/index.htmlを作成
FROM golang:1.17.1-alpine3.13 as builder
WORKDIR /go/src
RUN go get github.com/gin-gonic/gin
version: '3'
services:
go:
build:
context: .
dockerfile: Dockerfile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/app
ports:
- 8080:8080
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*.html")
router.GET("/", func(ctx *gin.Context){
ctx.HTML(200, "index.html", gin.H{})
})
router.Run()
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample App</title>
</head>
<body>
<h1>Hello World!!!</h1>
</body>
</html>
2. 次にdocker-compose build
したい所ですが、main.goでBrokenImportエラーが出ています。
というかそもそもgo.modがなかったので作成していきます。
go.modの作成、go getによるモジュールとそのバージョン追記
・go mod init github.com/「importpath」
あまり意識してなかったですが、 go mod init github.com/
の後は「importpath」の使用が推奨のようです。(詳しくはこちら)
・ go get
使用するモジュールとそのバージョンをgo.modファイルに追記するコマンド
BrokenImportエラーの原因、go.workの作成
今回はgo work init
コマンドでgo.workを作成後、workspaceを示すuseを手入力しました。自分も理解がまだ曖昧なので別途記事を投稿しようと思います。(詳しくはこちら)
3. docker-compose build
、 docker-compose up -d
の順に行いコンテナを立ち上げます。
up
実行後にdocker ps
してみましたが、コンテナがうまく起動していないようです。
何が原因???
ymlのvolumesとDockerfileのWORKDIRのパスが異なるのが原因みたいです。
再度試した所、問題なくコンテナが立ち上がりました!!!
http://localhost:8080へアクセスしてみると...
ご覧の通り、Webページが表示されました👏
DockerでMySQLを起動する
GoでのWebページ作成ができました。次はサービスづくりにどうしても必要なDBへの接続、MySQLのコンテナを立ち上げていきます。
1. docker-compose.ymlへ以下を追記します。
・dbコンテナの記述
・volumesの記述
version: '3'
services:
go:
build:
context: .
dockerfile: Dockerfile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/go/src
ports:
- 8080:8080
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: go_database
MYSQL_USER: go_test
MYSQL_PASSWORD: password
TZ: 'Asia/Tokyo'
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
- db-data:/var/lib/mysql
- ./db/my.cnf:/etc/mysql/conf.d/my.cnf
ports:
- 3306:3306
volumes:
db-data:
driver: local
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_bin
default-time-zone = SYSTEM
log_timestamps = SYSTEM
default-authentication-plugin = mysql_native_password
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
2. docker-compose up -d
を叩くと、MySQLのコンテナが立ち上がリます。
GoとMySQLを接続する
次はGoとMySQLを繋いでいきましょう。
接続にはsqlドライバーとGORMというフレームワークを使います。
1. 以下を行います。
・DockerfileにsqlドライバーとGORMのインストールを追記
・docker-compose.ymlへ依存関係の記述を追記
・main.goへDB接続の処理を追記
FROM golang:1.17.1-alpine3.13 as builder
WORKDIR /go/src
RUN go get github.com/gin-gonic/gin
RUN go get github.com/go-sql-driver/mysql
RUN go get github.com/jinzhu/gorm
version: '3'
services:
go:
build:
context: .
dockerfile: Dockerfile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/go/src
ports:
- 8080:8080
depends_on:
- "db"
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: go_database
MYSQL_USER: go_test
MYSQL_PASSWORD: password
TZ: 'Asia/Tokyo'
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
- db-data:/var/lib/mysql
- ./db/my.cnf:/etc/mysql/conf.d/my.cnf
ports:
- 3306:3306
volumes:
db-data:
driver: local
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db := sqlConnect()
defer db.Close()
router := gin.Default()
router.LoadHTMLGlob("templates/*.html")
router.GET("/", func(ctx *gin.Context){
ctx.HTML(200, "index.html", gin.H{})
})
router.Run()
}
func sqlConnect() (database *gorm.DB) {
DBMS := "mysql"
USER := "go_test"
PASS := "password"
PROTOCOL := "tcp(db:3306)"
DBNAME := "go_database"
CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
count := 0
db, err := gorm.Open(DBMS, CONNECT)
if err != nil {
for {
if err == nil {
fmt.Println("")
break
}
fmt.Print(".")
time.Sleep(time.Second)
count++
if count > 180 {
fmt.Println("")
fmt.Println("DB接続失敗")
panic(err)
}
db, err = gorm.Open(DBMS, CONNECT)
}
}
fmt.Println("DB接続成功")
return db
}
2. docker compose up
を行い、コンソールに「DB接続成功」と出たら成功です。
残念ながらエラーのようです(o^^o)
go.modへバイナリ(ライブラリ)のインストールが必要とのこと。
なぜかDockerfileのRUNコマンドが実行されてない模様...。今回は以下を手動で実行します。
go get github.com/go-sql-driver/mysql
go get github.com/jinzhu/gorm
再度 docker compose up
した所、成功しました🎉
GoでCRUD処理を実装
いよいよラストです!GoでCRUD処理を実装していきます。
1. main.goとindex.htmlを以下の通り変更します。
main.go
・Userの定義を作成
・Userテーブルを作成
・post処理(Userデータの登録・削除)の実装
index.html
・ユーザー登録・削除機能の実装
package main
import (
"fmt"
"strconv"
"time"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
)
type User struct {
gorm.Model
Name string
Department string
Email string
Phone string
}
func main() {
db := sqlConnect()
db.AutoMigrate(&User{})
defer db.Close()
router := gin.Default()
router.LoadHTMLGlob("templates/*.html")
router.GET("/", func(ctx *gin.Context) {
db := sqlConnect()
var users []User
db.Order("created_at asc").Find(&users)
defer db.Close()
ctx.HTML(200, "index.html", gin.H{
"users": users,
})
})
router.POST("/new", func(ctx *gin.Context) {
db := sqlConnect()
name := ctx.PostForm("name")
department := ctx.PostForm("department")
email := ctx.PostForm("email")
phone := ctx.PostForm("phone")
fmt.Println("create user " + name + " with email " + email)
db.Create(&User{Name: name, Department: department, Email: email, Phone: phone})
defer db.Close()
ctx.Redirect(302, "/")
})
router.POST("/delete/:id", func(ctx *gin.Context) {
db := sqlConnect()
n := ctx.Param("id")
id, err := strconv.Atoi(n)
if err != nil {
panic("id is not a number")
}
var user User
db.First(&user, id)
db.Delete(&user)
defer db.Close()
ctx.Redirect(302, "/")
})
router.Run()
}
func sqlConnect() (database *gorm.DB) {
DBMS := "mysql"
USER := "go_test"
PASS := "password"
PROTOCOL := "tcp(db:3306)"
DBNAME := "go_database"
CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
count := 0
db, err := gorm.Open(DBMS, CONNECT)
if err != nil {
for {
if err == nil {
fmt.Println("")
break
}
fmt.Print(".")
time.Sleep(time.Second)
count++
if count > 180 {
fmt.Println("")
panic(err)
}
db, err = gorm.Open(DBMS, CONNECT)
}
}
return db
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My App</title>
<style>
label {
width: 200px;
float:left;
margin: 0 50px 25px 0;
}
p {
margin-bottom: 5px;
}
input {
height: 20px;
margin-bottom: 10px;
}
tr {
width: 50%;
}
th,td{
border:1px #333 solid;
}
table{
margin-top: 5px;
width: 100%;
border-collapse: collapse;
}
#tdSample {
margin-left: 5px;
}
button {
width: 75px;
height: 30px;
color: #FFF;
background-color: #3CB371;
border: 0;
border-radius: 3px;
cursor: pointer;
font-weight: bold;
}
</style>
</head>
<body>
<h2>ユーザー一覧</h2>
<form method="post" action="/new" style="margin: 10px 50px 10px 50px;">
<div style="width: 100%;">
<label>
<p>氏名</p>
<input type="text" name="name" size="30" placeholder="入力してください">
</label>
<label>
<p>所属部署</p>
<input type="text" name="department" size="30" placeholder="入力してください">
</label>
<label>
<p>メールアドレス</p>
<input type="text" name="email" size="30" placeholder="入力してください">
</label>
<label>
<p>電話番号</p>
<input type="text" name="phone" size="30" placeholder="入力してください">
</label>
<div style="padding-top: 43px;">
<button type="submit">登録</button>
</div>
</div>
</form>
<table style="width: 90%;margin: 10px 50px 10px 50px;">
<tr>
<th style="width: 50px;"></th>
<th>氏名</th>
<th>所属部署</th>
<th>メールアドレス</th>
<th>電話番号</th>
</tr>
{{ range .users }}
<tr>
<td>
<form method="post" action="/delete/{{.ID}}">
<div align="center">
<button type="submit">削除</button>
</div>
</form>
</td>
<td><div id="tdSample">{{ .Name }}</div></td>
<td><div id="tdSample">{{ .Department }}</div></td>
<td><div id="tdSample">{{ .Email }}</div></td>
<td><div id="tdSample">{{ .Phone }}</div></td>
</tr>
{{ end }}
</table>
</body>
</html>
2. docker compose up
を行い、http://localhost:8080へアクセスします。
ユーザー登録フォームが表示され、ユーザーを登録すると一覧へ登録情報が表示されます。
Dockerでphpmyadminを起動する
先程、ラストと言いましたがついでなのでdb管理ツールであるphpmyadminの立ち上げまで行って終わりとします。
1. docker-compose.ymlへphpmyadminのコンテナ設定を追記します。
phpmyadmin:
image: phpmyadmin/phpmyadmin
environment:
- PMA_ARBITRARY=1
- PMA_HOST=db # mysqlのサービス名を指定
- PMA_USER=go_test # phpmyadminへログインするユーザ
- PMA_PASSWORD=password # phpmyadminにログインするユーザのパスワード
links:
- mysql
ports:
- 4040:80
volumes:
- ./phpmyadmin/sessions:/sessions
2. docker compose up -d
を行い、http://localhost:4040 へアクセスします。
ユーザー一覧画面で登録したユーザー情報がusersテーブルで見れるかと思います。
参考文献
-
構築で参考にした記事
【初心者向け】3分でgolang + docker(docker-compose)環境を構築する! -
構築エラー解決で参考にした記事
go.modの作成、go getによるモジュールとそのバージョン追記
BrokenImportエラーの原因、go.workの作成