はじめに

この記事は Go7 Advent Calendar 2019 の 22 日目の記事です。 最近GoをDockerに乗せて運用することが多いのですが、Goをクロスコンパイルする方法をググってると、結構みんな違うこと言っててどれが正しいのか疑問に思ってたのでちゃんと調べてみました。

結論

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build "-ldflags=-s -w -buildid=" -trimpath -o server

CGOを使ってない場合のみ可

※ GOOS/GOARCHは適宜設定。対応表がこちらにまとまってます

※ “ldflags …” と -trimpath はなくても全く問題ありません

フラグの説明

-ldflags

内部的に使うgo tool link へのフラグ(一覧

  • ”-ldflags=-s -w”: バイナリサイズ小さくなる
$ go build main.go
$ wc -c <main
1109624
$ go build "-ldflags=-s -w" main.go
$ wc -c <main
851576

-trimpath

バイナリから全てのファイルシステムパスを削除する(v1.13以降)

$ go build main.go
$ ./main
goroutine 1 [running]:
main.main()
	/Users/tomokatsuiguchi/Desktop/main.go:12 +0xaf
# ↑どこでビルドしたのか丸見え

$ go build -trimpath "-ldflags=-buildid=" main.go
$ ./main
goroutine 1 [running]:
main.main()
	command-line-arguments/main.go:12 +0xb8
# ↑ファイルパスが無くなった!

”-ldflags=-buildid=” を足すと完全に再現性のあるバイナリが生成できる https://github.com/golang/go/issues/34186#issuecomment-529390306

Dockerに乗せる

スタティックバイナリ生成する理由として、DockerImageのサイズを小さくしたいからというのが多いと思います。

FROM scratch
COPY --from=golang:1.13 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=golang:1.13 /usr/share/zoneinfo /usr/share/zoneinfo
COPY server /app/
CMD ["/app/server"]

ファイルサイズを小さくするためにscratchで作ってます。

あと最低限必要な、ssl通信するときに必要なルートCA証明書とtimezone情報を別imageから持ってきてます。

まとめ

ldflagsに渡せるフラグはここに紹介したもの以外にも、ビルド時に変数に値をセットできる -ldflags=-X とかあって便利そうなので今度使ってみたい。

あと、CGO使うときのstaticバイナリ生成時のフラグは結構ややこしそうなのでこの万能フラグ追加されてほしい…

余談

  $ go build "-ldflags=-s -w" main.go
  $ wc -c < main
  1429360
  $ upx main
  $ wc -c < main
  524304

gophers by Renee French CC BY 3.0