21
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HRBrainAdvent Calendar 2022

Day 25

M1 Mac において Go のリモートデバッグができない問題を解決し原因を考察した

Last updated at Posted at 2022-12-24

本記事は HRBrain Advent Calendar 2022 の25日目の記事です。

はじめに

こんにちは。今年の11月から HRBrain のエンジニアとして働いている清水です。
本記事では、特定のプロダクトの開発環境で起こっていた、M1 Mac において Go のリモートデバッグができない問題を解決し原因を考察しました。

対象読者

  • M1 Mac において Go のリモートデバッグができない方
  • Go のデバッグや Tilt について理解を深めたい方

上記のような方に読んでいただけると嬉しいです!

開発環境

  • macOS 12.6
  • チップ Apple M1
  • Go 1.18.4
  • Tilt 0.30.10
  • Docker 4.13.1

HRBrain では Tilt を使って、マイクロサービスをローカルの開発環境に構築しています。
開発環境や Tilt の詳細については、弊社のテックブログを見ていただけると幸いです。

リモートデバッグできない問題の解決と考察

では、リモートデバッグできなかった状態と解決方法を記載します。

問題

VS Code からリモートデバッグ接続することはできていたのですが、ブレークポイントが止まってくれない状態でした。
エラーは見られなかったため、手探りで Dockerfiledocker-compose.yaml を見て解決方法を探していました。

解決方法

Intel Mac では正常にデバッグできていたことから、M1 Mac のみに不都合な設定がされていると予想し Dockerfile を見てみると、amd64 の一行を発見しました。
AMD64 は AMD によって開発された64ビットプロセッサアーキテクチャで、Intel CPU で採用されていますが、M1 には採用されていないため M1 Mac のみに問題が起こると予想できます。

Dockerfile
ENV GOARCH=amd64

上記一行を消してデバッグしてみると無事に、ブレイクポイントが止まりデバッグに成功しました!

ここでは解決方法のみ記載しましたが、トライ&エラーを繰り返して、やっと解決できました。

原因の考察

ここでは、ローカル環境の適切な状態と、考えられる2つの原因の考察を記載します。

ローカル環境の適切な状態

原因を考察する前に、ローカル環境がどのような状態であるべきだったのか考えます。

まず、HRBrain の開発環境(Tilt環境)ではビルド環境と実行環境が同一で golang:1.18.4-alpine3.16 が使われています。そのため、ビルドする Go の実行バイナリは、golang:1.18.4-alpine3.16 で動くようにターゲットである GOARCHGOOS を指定する必要があります。

GOARCHGOOS の有効な組み合わせは Go の公式サイトに記載されています。
(参照: https://go.dev/doc/install/source#environment$GOOS and $GOARCH

GOARCHGOOS はそれぞれ Go のプログラムをビルドする際にターゲットとする CPU、OS の種類を表す環境変数であるため、Intel Mac、M1 Mac などの環境に応じて適切に設定する必要があります。
したがって、GOARCHGOOS が CPU や OS に応じて適切に設定され、有効な組み合わせの状態でビルドされることが、ローカル環境の適切な状態と考えられます。

つまり、Go のビルドターゲットがそれぞれ以下を指定された状態でビルドされることが、ローカル環境の適切な状態と言えます。

Intel Mac: GOARCH='amd64' GOOS='linux'
M1 Mac   : GOARCH='arm64' GOOS='linux'

考察1: 環境変数が Intel CPU 用になっていたことが原因の可能性

では、以上のローカル環境の適切な状態を踏まえて原因を考察していきます。

ENV コマンドは、Docker イメージ内で使用される環境変数を設定するものです。そのため、今回問題が起きていた DockerfileENV GOARCH=amd64 という行が記述されていたことにより、Docker イメージ内で環境変数 GOARCH の値が amd64 に設定されていたことになります。
GOARCH 環境変数によって、ビルドされるプログラムがどのようなアーキテクチャの CPU で動作するかが決まるため、この変数の指定が原因で M1 Mac でのみリモートデバッグができなかった可能性が考えられます。

実際に、Tilt を起動した状態で Docker コンテナ内の Go に関する環境変数を Dockerfile 修正前と修正後で比較してみました。

修正前
❯ docker exec <container id> go env | egrep -i 'os=|arch='
GOARCH="amd64"
GOHOSTARCH="arm64"
GOHOSTOS="linux"
GOOS="linux"
修正後
❯ docker exec <container id> go env | egrep -i 'os=|arch='
GOARCH="arm64"
GOHOSTARCH="arm64"
GOHOSTOS="linux"
GOOS="linux"

修正前は GOARCH の値が amd64 に設定されていますが、DockerfileENV GOARCH=amd64 という行を削除したことにより(修正後)、GOARCH の値が arm64 に設定されています。
したがって、Docker コンテナで環境変数 GOARCH の値が arm64 ではなく amd64 に設定されていたことが原因で、リモートデバッグが正常にできなかった可能性が考えられます。

なぜ ENV GOARCH=amd64 の一行を削除しただけで、GOARCH="arm64" に設定されているか

ここで、なぜ ENV GOARCH=amd64 の一行を削除しただけで、GOARCH="arm64" に設定されているか考察しました。
現状、開発環境(Tilt環境)では、ビルド環境と実行環境が同一で golang:1.18.4-alpine3.16 が使われています。そこで、実際に docker-library のソースコードを見てみると、Dockerfile に下記の記述が見つかりました。

docker-library/golang/1.18/alpine3.16/Dockerfile
	arch="$(apk --print-arch)"; \
	url=; \
	case "$arch" in \
		'x86_64') \
			export GOAMD64='v1' GOARCH='amd64' GOOS='linux'; \
			;; \
		'armhf') \
			export GOARCH='arm' GOARM='6' GOOS='linux'; \
			;; \
		'armv7') \
			export GOARCH='arm' GOARM='7' GOOS='linux'; \
			;; \
		'aarch64') \
			export GOARCH='arm64' GOOS='linux'; \
			;; \
		'x86') \
			export GO386='softfloat' GOARCH='386' GOOS='linux'; \
			;; \
		'ppc64le') \
			export GOARCH='ppc64le' GOOS='linux'; \
			;; \
		's390x') \
			export GOARCH='s390x' GOOS='linux'; \
			;; \
		*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \

上記の Dockerfile から、golang:1.18.4-alpine3.16 では、環境変数の GOARCHGOOS はデフォルトで決められていることわかります。

"$(apk --print-arch)" の部分は Docker を動かしている物理マシンの CPU で変わります。

Intel Mac
❯ docker exec <container-id> apk --print-arch
x86_64
M1 Mac
❯ docker exec <container-id> apk --print-arch
aarch64

つまり、Intel Mac と M1 Mac とで変わることがわかりました。
そのため環境変数 GOARCH の値も、物理マシンの CPU で下記のように変わります。

Intel Mac: GOARCH='amd64' GOOS='linux'
M1 Mac   : GOARCH='arm64' GOOS='linux'

したがって、開発環境の Dockerfile で明記しない限り、物理マシンの CPU によりデフォルトで環境変数 GOARCH が決められることがわかりました。

考察2: Delve インストール時とビルド実行時の GOARCH のズレが原因の可能性

もう少し深ぼって開発環境の Dockerfile を見ていきたいと思います。

修正前のDockerfile
FROM golang:1.18.4-alpine3.16 as deps
~~~省略~~~
# ① ここでは、GOARCH=arm64
RUN go install github.com/go-delve/delve/cmd/dlv@latest
~~~省略~~~
ENV GOARCH=amd64
~~~省略~~~
# ② ここでは、GOARCH=amd64
RUN go build -mod=mod -o /app -gcflags="all=-N -l"

golang:1.18.4-alpine3.16 インストール時の GOARCH がデフォルトで決定されることがわかったため、GOARCH の状態は上記①②のようになっています。

①では、Go 言語用のデバッグツールである Delve をインストールしています。
②では、Go 言語で書かれたプログラムをビルドしています。

したがって、リモートデバッグ時にブレイクポイントで止まらなかった根本原因は、

RUN go install github.com/go-delve/delve/cmd/dlv@latest

を実行時の GOARCH

RUN go build -mod=mod -o /app -gcflags="all=-N -l"

実行時の GOARCH のズレにあった可能性が考えられます。

おわりに

本記事では、特定のプロダクトの開発環境で起こっていた、M1 Mac において Go のリモートデバッグができない問題を解決し原因を考察しました。

入社して間も無く、本記事の問題に先輩エンジニアの方と取り組めて大変勉強になりました。記事を書く中でもわからないことを調べたり、なるべくわかりやすく書くようにしたことで、頭の中が整理できました。
コードを書いていると、たった一行が問題で正常に動かないこともあり、今回改めてコード理解や仮説検証することの大切さを学びました。

HRBrain では一緒に働いてくれる仲間を募集しています。興味がありましたら、ぜひご応募ください。

参考

21
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?