👋

Goでホットリロード可能なローカル開発環境をDockerとAirで構築する

2023/12/02に公開

記事の内容

ホットリロード(ファイルを変更すると自動で変更が反映される)可能なGoのローカル開発環境をAirとDockerで構築します。

記事を読むと得られるもの

  • ホットリロードが可能なローカル開発環境の構築
  • GoのDockerfileの作り方
  • docker composeを使ったローカル開発方法

対象読者

  • Go初心者
  • これからGoで開発をする人

記事の長さ

3分で読めます

Go Projectを作成する

まずは、Goのプロジェクトを作成します。

Go Projectの初期化

$ mkdir go-project
$ cd go-project
$ go mod init my-app
go: creating new go.mod: module my-app
$ ls
go.mod

これで、Goのファイルを配置するgo-projectディレクトリの準備ができました。

main.goの作成

次に、goのソースを書いていく、main.goファイルにWebサーバーを起動するGoプログラムを書いてきます。

$ touch main.go

main.go

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, World!")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

上記ソースを実行すると、localhost:8080で起動するWebサーバーが立ち上がります。

$ go run main.go
$ curl localhost:8080
Hello, World!%

go run main.goでWebサーバーを立ち上げて、curlコマンドで無事にアクセスできました!(curlコマンドを使用せずに、ブラウザから、http://localhost:8080にアクセスしても大丈夫です。)

以上で、Goのプロジェクトの初期構築は完了です。

プロジェクトフォルダの中は以下のようになっているはずです。

$ tree
.
├── go.mod
└── main.go

ローカル開発環境をDockerとAirで構築する

Goのプロジェクトが動作するDocker環境を構築していきます。

Dockerfileを作成する

Goが動作するDockerコンテナのベースとなるDockerfileを作成してきます。
Dockerfileという名前のファイルを作成し、以下のように編集してください

Dockerfile

FROM golang:1.21.3
WORKDIR /app
RUN go install github.com/cosmtrek/air@latest
CMD ["air"]

docker-composeファイルを作成する

次に、先ほど作成したDockerfileを元に、docker-compose.ymlファイルを作成します。

docker-compose.yml

version: "3.9"
services:
  go:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - .:/app

airの設定ファイルを追加する

最後にAirの設定ファイルを追加します。

.air.toml

# 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]
# Array of commands to run before each build
pre_cmd = ["echo 'hello air' > pre_cmd.txt"]
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o ./tmp/main ."
# Array of commands to run after ^C
post_cmd = ["echo 'hello air' > post_cmd.txt"]
# 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 = []
# Watch these files.
include_file = []
# 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"
# Poll files for changes instead of using fsnotify.
poll = false
# Poll interval (defaults to the minimum interval of 500ms).
poll_interval = 500 # ms
# It's not necessary to trigger build each time file changes if it's too frequent.
delay = 0 # 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 # nanosecond
# Rerun binary or not
rerun = false
# Delay after each executions
rerun_delay = 500
# Add additional arguments when running binary (bin/full_bin). Will run './tmp/main hello world'.
args_bin = ["hello", "world"]

[log]
# Show log time
time = false
# Only show main log (silences watcher, build, runner)
main_only = 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

[screen]
clear_on_rebuild = true
keep_scroll = true

※ Airの公式GithubのExampleのままです ( https://github.com/cosmtrek/air/blob/master/air_example.toml )

起動してみる

これで準備が完了しました。本記事の通りに、ファイルが作成できていたら、以下のようなディレクトリ構成になっているはずです。

$ tree -a
.
├── .air.toml
├── Dockerfile
├── docker-compose.yml
├── go.mod
├── main.go

docker compose upコマンドを実行してください。

$ docker compose up
[+] Running 1/0
 ✔ Container go-project-go-1  Created                                                                                                                                                                                                                                      0.0s
Attaching to go-project-go-1
go-project-go-1  |
go-project-go-1  |   __    _   ___
go-project-go-1  |  / /\  | | | |_)
go-project-go-1  | /_/--\ |_| |_| \_ v1.49.0, built with Go go1.21.3
go-project-go-1  |
go-project-go-1  | mkdir /app/tmp
go-project-go-1  | watching .
go-project-go-1  | !exclude tmp
go-project-go-1  | > echo 'hello air' > pre_cmd.txt
go-project-go-1  | building...
go-project-go-1  | running...

Airを使って、GoのProjectが起動するのがわかります。

$ curl localhost:8080
Hello, World!%

curlコマンドでアクセスすると、正常に文字列が返却されます。

ファイル内容を変更してみる

次に、ホットリロードが正常に機能するかを確かめるため、Dockerが起動したままファイル内容を変更します。

main.go

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Good!")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

文字列をHell, World!からGood!に変更しました。このファイルを保存すると、自動でAirが変更内容をDockerコンテナに反映してくれます。
正常に反映されたか、curlコマンドで確かめます。

$ curl localhost:8080
Good!%

正常に変更されていることが確認できました!

Sample Source

今回使用したソースは、以下です!

https://github.com/rara-tan/zenn-go-docker-air-local

note

勉強法やキャリア構築法など、エンジニアに役立つ記事をnoteで配信しています。

https://note.com/ring_belle/membership

Discussion