みなさん、Goでビルドするときには、どんなオプションを指定していますか?
Goではgo build
だけでも何事もなくビルドできるので、特に何も指定していないという人もいるかと思います。
しかし、オプションを何も指定せずにビルドした実行ファイルには、デバッグ情報などのリリース時には不要な様々な情報が含まれています。
本稿ではリリースビルドを作成するときに最低限付けておきたいオプションについて、簡単に紹介したいと思います。
※ビルドオプションを網羅的に紹介する記事ではありません。
リリースビルド時に最低限付けたいオプション
Goでリリースビルドを作成する時に最低限付けておきたいオプションは次の2つです。
-
-ldflags="-s -w"
- デバッグ情報(シンボルテーブル等)を含めないオプション
- ファイルサイズが小さくなります
-
-trimpath
- ビルド環境のローカルパス情報を取り除くオプション
- (ユーザー名などが含まれた)ローカルPC上のパス情報の漏洩を防げます
実際にはこのような感じで使います。
go build -ldflags="-s -w" -trimpath
巷の記事やOSSプロジェクトを見ても、だいたいこのような指定をしているものが多い印象です(-trimpath
は無いことも多いですが1)。
以下の項でこの2つのオプションについて簡単に解説します。
-ldflags="-s -w"
オプション
-ldflags
は、ビルド時に内部で呼ばれるgo tool link
の引数を指定するオプションとなっていて、今回の指定ではgo tool link -s -w
のような感じで実行されることになります。
go tool link
に渡せるオプションについてはlinkコマンドのドキュメントかgo tool link --help
で確認できます。
-s
と-w
についてヘルプを確認すると、次のようなことが書かれています。
-s
Omit the symbol table and debug information.
-w
Omit the DWARF symbol table.
いずれもシンボルテーブルやデバッグに関する情報を取り除くオプションです。
これらのオプションを指定することで、実行ファイルからデバッグ用の情報が取り除かれ、ファイルサイズがそこそこ小さくなります。
ちなみにシンボルテーブルというのは、関数名や変数名、ソースコード上の位置などの情報を格納したテーブルです。
ただ、Goのスタックトレースに出てくる情報はまた別に持っているようで、これらを指定してもスタックトレースではファイル名や位置が出力されます。
ファイルサイズについて
簡単なhello-worldプログラムで比較してみると、だいたいこのぐらいの違いが出てきます(Go 1.18 on macOSでの一例です)。
オプション | ファイルサイズ |
---|---|
なし | 1854560 (1.8M) |
-ldflags="-s -w" |
1346656 (1.3M) |
-ldflags="-s -w" -trimpath |
1342560 (1.3M) |
-trimpath
オプション
-trimpath
はgo build
に対するオプションです。Go 1.13で追加されました。
go build
に渡せるオプションについてはbuildコマンドのドキュメントかgo help build
で確認できます。
-trimpath
についてヘルプを確認すると、次のようなことが書かれています。
-trimpath
remove all file system paths from the resulting executable.
Instead of absolute file system paths, the recorded file names
will begin either a module path@version (when using modules),
or a plain import path (when using the standard library, or GOPATH).
通常はビルド環境の絶対パスが実行ファイルに埋め込まれるのですが、これをモジュールやGOPATH
からの相対パスとして記録するようになります。
埋め込まれるパス情報について
例えば次のようなディレクトリで開発をしているとします。
/Users/ynakamura/mycompany/myproject
ここで-trimpath
を付けずにビルドした場合、プログラムがスタックトレースを出力すると、このような感じになります。2
$ ./myproject
panic: error!!
goroutine 1 [running]:
main.main()
/Users/ynakamura/mycompany/myproject/main.go:4 +0x27
ユーザー名や会社名等の含まれた、開発環境のフルパスが見えてしまっていますね。
一方で、-trimpath
を付けてビルドするとこうなります。
$ ./myproject
panic: error!!
goroutine 1 [running]:
main.main()
myproject/main.go:4 +0x27
プロジェクトディレクトリからの相対パスだけが記録されるようになりました。
おわりに
これら2つのオプションを指定すると、ファイルサイズは小さくなるし、余計な情報が残らないので、リリース時にはぜひとも付けておきたいですね。
シングルバイナリという特性上、ちょっと大きめなGoのバイナリが小さくなるのは嬉しいかと思います。
特にコンテナ環境が多くなりつつある昨今では、コンテナイメージを少しでも小さくするために色々工夫をすることも多いので、そういった面でも効果が期待できます。
パス情報についても、漏れたからといって直ちに情報漏えい等の問題に繋がるわけではありませんが、ビルド環境のパス情報が見えてしまうのはあまり気持ちの良いものではないですし、サーバーのログに残ったりもするので、できれば消しておきたいですね。