Breaking News

Default Placeholder Default Placeholder

GoのWeb FrameworkであるGinを使った環境をdocker-composeで作っていきたいと思います。

その際、ホットリロードが可能になるようにAirという技術も取り入れていきます。

筆者の環境

  • macOS BigSur 11.4
  • M1 Mac
  • docker-compose version 1.29.2, build 5becea4c
  • Docker version 20.10.7, build f0df350

Goのバージョン

  • 1.19(2022-08-02リリース)

やることリスト

  1. go.modを作成
  2. main.goを作成
  3. Dockerfileを作成
  4. docker-compose.yamlを作成
  5. イメージをビルド
  6. .air.tomlを作成
  7. 依存関係の整理
  8. コンテナ起動
  9. Hot Reloadを確認

各手順の時点で、ディレクトリ構成がどうなっているべきかを記載するので都度確認してみてください。

1. go.modを作成

go.modはライブラリを管理するファイルです。JSでいうところのpackage.jsonに当たります。

$ touch app/src/go.mod

moduleは何かしらユニークな名前であればなんでも良いです。この名前が各ファイルのモジュール呼び出しに使用されます。

module nothing-behind.com/sample_gin

go 1.19

今のツリーはこんな感じです

.
└── app
    └── src
        └── go.mod # New!!

2. main.goを作成

/にアクセスすると {"message": "SUCCESS!!!!"}と表示するように設定します。

$ touch app/src/main.go
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "SUCCESS!!!!",
		})
	})
	r.Run()
}

今のツリーはこんな感じです

.
└── app
    └── src
        ├── go.mod
        └── main.go # New!!

3. Dockerfileを作成

イメージを作成する元となるDockerfileを作成します。

ここですでにAirの導入が始まっています。

$ touch app/Dockerfile
FROM golang:1.19-alpine

WORKDIR /go/src
COPY ./app/src .

RUN apk upgrade --update && apk --no-cache add git

RUN go get -u github.com/cosmtrek/air && go build -o /go/bin/air github.com/cosmtrek/air

CMD ["air", "-c", ".air.toml"]

今のツリーはこんな感じです

.
└── app
    ├── Dockerfile # New!!
    └── src
        ├── go.mod
        └── main.go

4. docker-compose.yamlを作成

今後、このアプリケーションにいろんなもの(例えばMySQLとか)を追加することを想定し、今は1つのコンテナしか使わないですが、ここで docker-compose.yamlを作成します。

どんな設定をしているか

  • app/Dockerfileを見ている
  • 3000番ポートでアクセス可能
$ touch docker-compose.yaml
version: '3'

services:
  app:
    build:
      context: .
      dockerfile: ./app/Dockerfile
    ports:
     - "3000:8080"
    volumes:
      - ./app/src/:/go/src
    tty: true

今のツリーはこんな感じです

.
├── app
│   ├── Dockerfile
│   └── src
│       ├── go.mod
│       └── main.go
└── docker-compose.yaml # New!!

5. イメージをビルド

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

$ docker-compose build

イメージが作成されているか、確認してみましょう。以下のコマンドでイメージが作成されているかが確認できます。

$ docker images

筆者はsample_ginというディレクトリで作業をしているのでこんな感じのイメージが作成されました。

$ docker images
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
sample_gin_app          latest    83a3bbc14066   46 seconds ago   398MB

6. airの初期化

作成したイメージでairを初期化(init)します。ここで、.air.tomlファイルが生成されます。

ここでは.air.tomlの説明は割愛します。

$ docker-compose run --rm app air init
Creating network "sample_gin_default" with the default driver
Creating sample_gin_app_run ... done

  __    _   ___  
 / /\  | | | |_) 
/_/--\ |_| |_| \_ , built with Go 

.air.toml file created to the current directory with the default settings

今のツリーはこんな感じです。

.
├── app
│   ├── Dockerfile
│   └── src
│       ├── .air.toml # New!!
│       ├── go.mod
│       └── main.go
└── docker-compose.yaml

7. 依存関係の整理

諸々の依存関係をいい感じにしていきます。

以下のコマンドを実行します。すると下記のような出力がなされ、go.sumというファイルが作成されます。

$ docker-compose run --rm app go mod tidy
Creating sample_gin_app_run ... done
go: finding module for package github.com/gin-gonic/gin
go: downloading github.com/gin-gonic/gin v1.8.1
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.8.1
go: downloading github.com/gin-contrib/sse v0.1.0
go: downloading github.com/mattn/go-isatty v0.0.14
go: downloading golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
go: downloading github.com/stretchr/testify v1.7.1
go: downloading google.golang.org/protobuf v1.28.0
go: downloading github.com/go-playground/validator/v10 v10.10.0
go: downloading github.com/pelletier/go-toml/v2 v2.0.1
go: downloading github.com/ugorji/go/codec v1.2.7
go: downloading gopkg.in/yaml.v2 v2.4.0
go: downloading github.com/goccy/go-json v0.9.7
go: downloading github.com/json-iterator/go v1.1.12
go: downloading golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
go: downloading github.com/go-playground/universal-translator v0.18.0
go: downloading github.com/leodido/go-urn v1.2.1
go: downloading golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
go: downloading golang.org/x/text v0.3.6
go: downloading github.com/go-playground/locales v0.14.0
go: downloading github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
go: downloading github.com/modern-go/reflect2 v1.0.2
go: downloading github.com/go-playground/assert/v2 v2.0.1
go: downloading github.com/google/go-cmp v0.5.5
go: downloading gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
go: downloading github.com/kr/pretty v0.3.0
go: downloading golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
go: downloading github.com/rogpeppe/go-internal v1.8.0
go: downloading github.com/kr/text v0.2.0

今のツリーはこんな感じです。

.
├── app
│   ├── Dockerfile
│   └── src
│       ├── .air.toml
│       ├── go.mod
│       ├── go.sum # New!!
│       └── main.go
└── docker-compose.yaml

8. コンテナ起動

さて、準備ができましたのでコンテナを起動していきましょう!docker-compose upを実行し、以下のような出力がされたらOKです。

$ docker-compose up
...
...
...

app_1  | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
app_1  |  - using env:  export GIN_MODE=release
app_1  |  - using code: gin.SetMode(gin.ReleaseMode)
app_1  | 
app_1  | [GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
app_1  | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
app_1  | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
app_1  | [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
app_1  | [GIN-debug] Listening and serving HTTP on :8080

ブラウザで http://localhost:3000/ にアクセスしてみてください。

{"message":"SUCCESS!!!!"}

が表示されたら成功です!

9. Hot Reloadを確認

最後に、Hot Reloadが有効になっているか確認してみましょう。

main.goを修正し、変更が適用されているかを確認します。

以下のように修正して、保存してみましょう。

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "WOW, SUCCESS!!!!", # ← ここを変えた
		})
	})
	r.Run()
}

ここで、ファイルを保存すると

app_1  | main.go has changed
app_1  | building...
app_1  | running...

↑のような感じになって変更を検知していることがわかります。

では再度ブラウザで localhost:3000 にアクセスしてみましょう。

ブラウザで以下のように表示されれば成功です。

{"message":"WOW, SUCCESS!!!!"}

以上、簡単ではありますが、Ginフレームワークの環境をdocker-composeで構築する手順まとめでした。

最後までお読みいただきありがとうございました。