🐳

dockerでgoのホットリロード環境を爆速で構築する

2025/01/27に公開

はじめに

はじめまして、otot_devです。
Go を業務で触ることになったので、手始めに個人で開発環境の構築を行ないながらアウトプットします。今回は、爆速でやるので開発効率的に絶対に必要なホットリロード部分をまずは構築します。

最終的なアウトプット

今回のコード一式は以下の URL にあります。
go-docker-template(v1.0.0)

バージョン情報

  • go:v1.23.5
  • air-verse/air:v1.61.7

※どちらもDockerfile上は latest でインストールします。そのため、執筆時点ではうまくいきましたが、もしもうまくいかない場合はバージョン固定で利用してみてください。

構築編

air-verse/air の README

今回は、docker でホットリロード環境を構築したいので、air-verse/air 公式の README を見ます。
途中の見出しにDockerfiledocker-compose.yamlがあるのでこちらを参考にしました。

Dockerfile

容量が気になる人はおとなしくFROM golang:1.23-alpineで軽量なものを使用した方が良いかもしれません。
go-docker-template(v1.1.0)ではやはり軽量なものに変更しました。

https://github.com/otoshimtoshi/go-docker-template/blob/v1.1.0/Dockerfile#L1-L2

# v1.23.5
- FROM golang:latest
+ FROM golang:1.23-alpine

WORKDIR /app

# v1.61.7
RUN go install github.com/air-verse/air@latest

COPY go.mod go.sum ./
RUN go mod download

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

docker-compose.yml

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8080:3000
    volumes:
      - ./:/app

.air.toml

air の設定ファイル。公式のほぼそのままにしてます。

# Config file for [Air](https://github.com/air-verse/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 = []
# 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 = []
# 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"
# Add additional arguments when running binary (bin/full_bin). Will run './tmp/main hello world'.
args_bin = ["hello", "world"]
# 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 is placed 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 execution
rerun_delay = 500

[log]
# Show log time
time = false
# Only show main log (silences watcher, build, runner)
main_only = false
# silence all logs produced by air
silent = 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

[proxy]
# Enable live-reloading on the browser.
enabled = true
proxy_port = 8090
app_port = 3000

go.mod

go のバージョンを指定してます。

module go-docker-template

go 1.23.5

go.sum

本来は依存関係が細かく記録されたファイルなんですが、空の状態で構わないのでファイルだけ作成してます。

main.go

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(":3000", nil))
}

実行編

docker compose upで以下のようなログが出力されたらサーバーは起動してます。

% docker compose up
[+] Running 1/0
 ✔ Container go-docker-template-api-1  Created                                                                                                                     0.0s
Attaching to api-1
api-1  |
api-1  |   __    _   ___
api-1  |  / /\  | | | |_)
api-1  | /_/--\ |_| |_| \_ v1.61.7, built with Go go1.23.5
api-1  |
api-1  | mkdir /app/tmp
api-1  | watching .
api-1  | !exclude tmp
api-1  | Proxy server listening on http://localhost:8090
api-1  | building...
api-1  | running...

ホットリロードの確認

  1. 以下でコンソールログが出力されるか確認。
% curl localhost:8080
Hello, World!
  1. main.go を更新
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-   fmt.Fprintf(w, "Hello, World!")
+   fmt.Fprintf(w, "Great!")
  })
  1. コンソールログの出力が変わるか確認。
% curl localhost:8080
Great!

ホットリロードができていることが確認できました。

GitHubで編集を提案

Discussion