背景
以前Docker multi stage buildなどビルド環境と実行環境が異なる(クロスコンパイル)時に、単純にGOOS
やGOARCH
をセットするだけではなく
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -a -installsuffix cgo -o main main.go
のように
CGO_ENABLED=0
-a
-installsuffix cgo
といったおまじないが必要ですよ、という記事を書きました。
ScratchイメージでGolangアプリの超軽量イメージをビルド - Carpe Diem
が、go 1.10からそれらが不要になったようです。
CGO_ENABLED=0 and -installsuffix cgo are no longer required since Go 1.10 | by Адам | Medium
環境
- golang/go 1.13.5
検証コード
go
以下のmain.go
ファイルを用意します。
package main import ( "fmt" "io/ioutil" "net/http" "os" ) func main() { resp, err := http.Get("http://google.com") if err != nil { fmt.Println(err) os.Exit(1) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println(len(body)) }
Dockerfile
CGO_ENABLED=0 and -installsuffix cgo are no longer required since Go 1.10 | by Адам | Medium
の通りに
CGO_ENABLED=0
-a
-installsuffix cgo
を削ります。
alpine -> scratchのmulti stage buildです。
FROM golang:alpine AS build-env ADD . /workspace WORKDIR /workspace RUN GOOS=linux GOARCH=amd64 go build -o main main.go FROM scratch COPY --from=build-env /workspace/main /main CMD ["/main"]
検証
docker imageをビルド
$ docker build --tag hoge .
実行してみます。
$ docker run --rm --name piyo hoge standard_init_linux.go:211: exec user process caused "no such file or directory"
あれ?コケましたね。
なぜコケたのか?
ドキュメントにはCGO_ENABLED
はデフォルトは1
、クロスコンパイル時は0
とあります。
The cgo tool is enabled by default for native builds on systems where it is expected to work. It is disabled by default when cross-compiling.
ref: cgo command - cmd/cgo - pkg.go.dev
そこでこんなDockerfileを用意してみます。
FROM golang:alpine ADD . /workspace WORKDIR /workspace RUN go env RUN GOARCH=amd64 go env RUN GOARCH=ppc64le go env
実行してみましょう。
デフォルト(GOARCH未指定)
Step 4/6 : RUN go env ---> Running in 12010cc1f9ba GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" # 1になってる GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build069180331=/tmp/go-build -gno-record-gcc-switches" Removing intermediate container 12010cc1f9ba ---> 1e52bae6675d
ドキュメント通りCGO_ENABLED=1
です。
GOARCH=amd64指定
Step 5/6 : RUN GOARCH=amd64 go env ---> Running in d108c8647275 GO111MODULE="" GOARCH="amd64" # 未指定と同じ GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" # 1になってる GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build158549923=/tmp/go-build -gno-record-gcc-switches" Removing intermediate container d108c8647275 ---> a3db739ae176
指定したGOARCH=amd64
はデフォルトのGOARCHと同じなのでCGO_ENABLED=1
になっています。
GOARCH=ppc64le指定
Step 6/6 : RUN GOARCH=ppc64le go env ---> Running in dfc4db342a73 GO111MODULE="" GOARCH="ppc64le" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GCCGO="gccgo" GOPPC64="power8" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="0" # 0になってる GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build362580251=/tmp/go-build -gno-record-gcc-switches"
ドキュメント通りCGO_ENABLED=0
です。
つまり
GOARCHがビルド環境以外の値を指定すればデフォルトでCGO_ENABLED=0
になるので問題は起きないですが、知らずに被ってCGO_ENABLED=1
になる場合もあるので明示的にCGO_ENABLED=0
を指定した方が良いです。
修正コード
ビルドする箇所にCGO_ENABLED=0
を追記します。
FROM golang:alpine AS build-env ADD . /workspace WORKDIR /workspace RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go FROM scratch COPY --from=build-env /workspace/main /main CMD ["/main"]
実行してみます。
$ docker run --rm --name piyo hoge 13277
今度は実行できました。
結論
Go 1.10以降でcgoを使わないクロスコンパイル時は
CGO_ENABLED=0
は付けた方がいい-a
や-installsuffix cgo
は付けなくていい
とすれば良いです。