概要
ネットに十分な情報はあるのだが、なんやかんや毎回詰まったりちょこちょこ調べたりしているので自分用にまとめておく。
今回のサンプルは GitHub に置いてある。
雛形の作成
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) } } // getEnv は指定した key の環境変数を取得します // 環境変数が未設定の場合 fallback の値を返します 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 のパスで分かる)。
ブレイクポイントを認識してくれないことがあるが、一度止めずに実行すると認識してくれる。