DockerコンテナでgolangをホットリロードするAirを導入
golangをDockerコンテナで扱う時、ローカルでのコードの変更をコンテナ側に反映するためにリロードをする必要があります。
つまり更新コードの確認のたびにdocker restart や docker-compose restartを実行する必要があります。
Airはこのリロード作業を自動化してくれます。
普通のリロード
.
├── Dockerfile
├── docker-compose.yml
├── go.mod
└── main.go
version: "3.8"
services:
reload_test:
image: reload_test
container_name: reload_test
build: .
ports:
- 8080:8080
volumes:
- .:/app
volumesでローカルのrootディレクトリとコンテナの/appディレクトリをバインドマウントしているので、変更は即時反映されます。
FROM golang:1.17.7-alpine
WORKDIR /app
CMD ["go","run","main.go"]
package main
import (
"fmt"
"net/http"
)
func helloHander(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello Normal</h1>")
}
func main() {
http.HandleFunc("/", helloHander)
http.ListenAndServe(":8080", nil)
}
実行してブラウザのlocalhost:8080にアクセスするとHello Normalと表示されます。
% docker-compose up reload_test -d
ローカルのソースコードを更新します。
package main
import (
"fmt"
"net/http"
)
func helloHander(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello Normal Update</h1>")
}
func main() {
http.HandleFunc("/", helloHander)
http.ListenAndServe(":8080", nil)
}
ブラウザを更新してもHello Normalのままです。
Dockerをリロードしてブラウザを更新するとHello Normal Updateに変わります。
% docker-compose restart reload_test
golangのホットリロードツールAir導入
それではgolangのホットリロードツールAirを導入します。
Dockerfileを編集しgitからAirをインストールしairコマンドを実行します。
FROM golang:1.17.7-alpine
RUN apk update && apk add git
RUN go get github.com/cosmtrek/air@v1.29.0
WORKDIR /app
# air -c [tomlファイル名] // 設定ファイルを指定してair実行(WORKDIRに.air.tomlを配置しておくこと)
CMD ["air", "-c", ".air.toml"]
続いてDockerfileと同じ階層にAirの設定ファイル.air.tomlを作成します。
.air.toml
tomlは軽量な設定ファイルです。2021年1月に1.0.0がリリースされました。
table(ハッシュ)とarray of tablesだけ理解すればすぐに使えそうでした。
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format
# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "tmp"
[build]
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o ./tmp/main ."
# Binary file yields from `cmd`.
bin = "tmp/main"
# Customize binary, can setup environment variables when run your app.
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
# Watch these filename extensions.
include_ext = ["go", "tpl", "tmpl", "html"]
# Ignore these filename extensions or directories.
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
# Watch these directories if you specified.
include_dir = []
# Exclude files.
exclude_file = []
# Exclude specific regular expressions.
exclude_regex = ["_test.go"]
# Exclude unchanged files.
exclude_unchanged = true
# Follow symlink for directories
follow_symlink = true
# This log file places in your tmp_dir.
log = "air.log"
# It's not necessary to trigger build each time file changes if it's too frequent.
delay = 1000 # ms
# Stop running old binary when build errors occur.
stop_on_error = true
# Send Interrupt signal before killing process (windows does not support this feature)
send_interrupt = false
# Delay after sending Interrupt signal
kill_delay = 500 # ms
[log]
# Show log time
time = false
[color]
# Customize each part's color. If no color found, use the raw app log.
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"
[misc]
# Delete tmp directory on exit
clean_on_exit = true
Airのgithubにあったair_example.tomlファイルです。
デフォルトのままだとWORKDIRに./tmp/mainという実行ファイルが作成され、ホットリロードされる度に実行ファイルが更新されます。(WORKDIRにもmain実行ファイルが作成されますがこちらは更新されませんでした。)
それではAirを実行してみます。
やってみる
docker-compose upで実行します。
% docker-compose up reload_test
[+] Running 0/1
⠿ reload_test Error 4.2s
[+] Building 2.1s (9/9) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 32B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/golang:1.17.7-alpine 2.0s
=> [auth] library/golang:pull token for registry-1.docker.io 0.0s
=> [1/4] FROM docker.io/library/golang:1.17.7-alpine@sha256:d030a987c28ca403007a69af28ba419fca00fc15f08e7801fc8edee77 0.0s
=> CACHED [2/4] WORKDIR /app 0.0s
=> CACHED [3/4] RUN apk update && apk add git 0.0s
=> CACHED [4/4] RUN go get github.com/cosmtrek/air@v1.29.0 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:7d623f3c5f70ae48bceb1d54947472f1a3c416ec47439a1b9ee147e0de1e0031 0.0s
=> => naming to docker.io/library/reload_test 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 2/1
⠿ Network reload_test_default Created 0.0s
⠿ Container reload_test Created 0.1s
Attaching to reload_test
reload_test |
reload_test | __ _ ___
reload_test | / /\ | | | |_)
reload_test | /_/--\ |_| |_| \_ , built with Go
reload_test |
reload_test | mkdir /app/tmp
reload_test | watching .
reload_test | !exclude tmp
reload_test | building...
reload_test | running...
Airが実行され"running..."で終わっていたら正常に動作しています。
main.goの表示文字を変更しローカルで保存するだけでホットリロードがかかります。
reload_test | main.go has changed
reload_test | building...
reload_test | running...
.air.tomlのrootやtmp_dirやcmdを変更してみたりしたのですが想定通りにホットリロードが機能しなかったので、ここらへんも調べていきたいです。
参考
Discussion