概要
以下自分用のメモです。よく忘れるのでここにメモメモ。。。
以下はLinux上での話です。
Goでは、標準ライブラリを使っているだけのアプリケーションであれば、大抵ビルドすると static linked なバイナリになります。
静的リンクされたバイナリってことですね。自分を動かすのに必要なものが自分自身で完結しているバイナリです。
昨今、コンテナで動作させることが多いので、依存しているものが少なくて、シングルバイナリなアプリケーションが求められるときも多いです。
軽くてセキュリティ的にも強いコンテナを作るために、シェルやlibcが入っていないイメージを使うこともあります。
普通にアプリ作って確認
なので、以下のような
package main import "fmt" func main() { ch := make(chan int) go func(out chan<- int) { defer close(out) for i := 0; i < 10; i++ { out <- i } }(ch) for v := range ch { fmt.Println(v) } }
をビルドして、確認してみると
$ go build $ file ./app | tr , '\n' | grep -F 'linked' statically linked $ ldd ./app not a dynamic executable
と出ます。static linkedなバイナリは自分だけで動けるはずなので、何も無い環境に持って行っても動きます。
簡単に実現できるのは chroot してやることですね。
$ sudo chroot . ./app 0 1 2 3 4 5 6 7 8 9
ちゃんと動きます。
os/user or net パッケージを使うと?
問題はここからです。 os/user パッケージ or net パッケージを使うと結果が変わります。(補足: os/userパッケージに関しては、Windowsの場合Go実装のもの一つしか存在しないので、以下のように動的リンクにはなりません。以下はLinuxの場合。)
package main import ( "fmt" "os/user" ) func main() { u, _ := user.Current() fmt.Println(u.Username) }
確認してみます。
$ go build $ file ./app | tr , '\n' | grep -F 'linked' dynamically linked $ ldd ./app linux-vdso.so.1 (0x00007ffe695fc000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9e7e3c1000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9e7e1cf000) /lib64/ld-linux-x86-64.so.2 (0x00007f9e7e3ee000)
dynamically linked に表示が変わりました。 Go では os/user と net パッケージに関しては cgo を使った実装がデフォルトでは適用されます。
動的リンクしているアプリケーションになっているので、chroot した環境では動きません。
$ ./app gitpod $ sudo chroot . ./app chroot: failed to run command ‘./app’: No such file or directory
./app が存在しないってメッセージですが、実際には依存している ld-linux-x86-64.so.2 が無いからです。
んで、これを static linked なバイナリにするためのやり方が大きく2つあります。
CGO_ENABLED=0 を指定
最も手っ取り早いのが cgo を無効にしちゃうことですね。 アプリが cgo に依存していなければこれで終わりです。
$ CGO_ENABLED=0 go build $ file ./app | tr , '\n' | grep -F 'linked' statically linked $ ./app gitpod $ sudo chroot . ./app root
osusergo, netgo のタグをビルド時に付与
Pure Goな実装を使うようにタグ指定することでも出来ます。
$ go build -tags osusergo,netgo $ file ./app | tr , '\n' | grep -F 'linked' statically linked $ ./app gitpod $ sudo chroot . ./app root
参考情報
- Statically compiling Go programs
- golangで書いたアプリケーションのstatic link化 - okzkメモ
- Goメモ-111 (ビルド時にデバッグ情報とかを消してサイズを小さくする, ldflags, trimpath, upx) - いろいろ備忘録日記
- try-golang/examples/singleapp/static_linked_binary at master · devlights/try-golang · GitHub
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。