概要
ネットに十分な情報はあるのだが、なんやかんや毎回詰まったりちょこちょこ調べたりしているので自分用にまとめておく。
- Goland を使う
- Docker で動かす Web アプリケーション
- air でホットリロード
- delve でリモートデバッグ
- テストも Docker 経由でデバッグ
今回のサンプルは GitHub に置いてある。
github.com
雛形の作成
mkdir go-dev-starter
cd go-dev-starter
# 初期化
go mod init
Hello World を表示するだけの Web アプリケーションをサンプルとして作成する。
ポートだけ環境変数で変えられるようにしてある。
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
err := http.ListenAndServe(
fmt.Sprintf(":%s", getEnv("PORT", "18080")),
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}))
if err != nil {
fmt.Printf("failed to terminate server: %v", err)
os.Exit(1)
}
}
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
Dockerfile の作成
delve と air を入れた Dockerfile を作成する。
今回は分かりやすくするために、開発用のみ記載している。
FROM golang:1.19
WORKDIR /app
RUN go install github.com/cosmtrek/air@latest
RUN go install github.com/go-delve/delve/cmd/dlv@latest
CMD ["air"]
docker-compose.yml の作成
docker-compose.yml を用意する。
security_opt と cap_add は delve のリモートデバッグ用に設定してある。
40000 番のポートはリモートデバッグで Listen する用。
version: "3.9"
services:
app:
container_name: dev-server
build:
context: .
environment:
PORT: 8080
volumes:
- .:/app
ports:
- "18000:8080"
- "40000:40000"
security_opt:
- "apparmor=unconfined"
cap_add:
- SYS_PTRACE
air の設定ファイルの作成
コンテナ内で air init
すると設定ファイルの雛形が作成される。
docker compose up -d
docker compose exec app air init
ビルドコマンドと full_bin を delve を使うように書き換える。
--- a/.air.toml
+++ b/.air.toml
@@ -5,14 +5,14 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
- cmd = "go build -o ./tmp/main ."
+ cmd = "go build -gcflags=\"all=-N -l\" -v -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
- full_bin = ""
+ full_bin = "dlv --listen=:40000 --headless=true --api-version=2 --accept-multiclient exec ./tmp/main"
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0s"
Docker の実行構成を作成
Goland 側から実行できるようにしておく。

リモートデバッグの実行構成を作成
ここでリモートデバッグ用のポートを設定しておく。
今回の場合だと 40000 番。

作成した Docker のリモートデバッグの構成を実行し、ブレイクポイントを貼る。
エンドポイントにアクセスするとブレイクポイントで止まってくれる。
curl localhost:18000

ホットリロードを試す
適当に書き換えると、air が自動でリビルドしてアプリケーションを起動し直してくれる。
ただリモートデバッグが一度切れてしまうので再実行する必要がある。
少し面倒だが、 Ctrl + D
のショートカットを使えるので許容範囲かな。。(うまい方法あったら教えてほしい)
テストの追加
とりあえず試すだけなので、環境変数設定のテストを書く。
package main
import (
"os"
"testing")
func Test_getEnv(t *testing.T) {
want := "9999"
key := "FOO"
err := os.Setenv(key, want)
if err != nil {
t.Fatalf("failed to set env: %v", err)
}
if got := getEnv(key, "8080"); got != want {
t.Errorf("getEnv() = %v, want %v", got, want)
}
}
Run Target に Docker を追加
docker compose 経由で実行できるターゲットを追加しておく。
デフォルトターゲットも変更しておく。

テストのデバッグ実行も Docker 経由で実行できることを確認する。
IDE からテストを実行してみる。
テスト実行後のログを見ると、Docker 経由で実行されていることが分かる(go や delve のパスで分かる)。

ブレイクポイントを認識してくれないことがあるが、一度止めずに実行すると認識してくれる。
参考