😀

Golang - Dockerfileの最小構成

2024/03/09に公開

Dockerfile

本番環境で動くGoアプリのためのミニマルdockerfileを記す。

  • 実行ファイルが、軽量でセキュアなscratchイメージ上で動く
  • go.modとgo.sumに従って、バージョン込みの依存パッケージをインストールする
  • 依存パッケージに変更がなければ、再ビルド時のインストールにはキャッシュが使われる
dockerfile
FROM golang:1.14.4 as builder

WORKDIR /go/src

COPY go.mod go.sum ./
RUN go mod download

COPY ./main.go  ./

ARG CGO_ENABLED=0
ARG GOOS=linux
ARG GOARCH=amd64
RUN go build \
	-o /go/bin/main \
	-ldflags '-s -w'


FROM scratch as runner

COPY --from=builder /go/bin/main /app/main

ENTRYPOINT ["/app/main"]

補足

レイヤー1
FROM golang:1.14.4 as builder

ベースイメージには、golangのバージョン 1.14.4 を使いbuilderと名付けた。
後でマルチステージビルドを利用して、実行ファイルをより軽量なベースイメージに移す。
本番環境で動くアプリはイメージサイズが小さく、かつ、セキュリティホールが少ないイメージが好まれる。

--

レイヤー2
WORKDIR /go/src

/go/srcで活動することを宣言。

--

レイヤー3
COPY go.mod go.sum ./

go.mod と go.sum をWORKDIRにCOPY。
ベストプラクティスでは、ADDではなくCOPYがを推奨されているのでCOPYを採用した。

--

レイヤー4
RUN go mod download

go mod downloadが go.mod と go.sum に従って正確にパッケージをダウンロードしてくれる。
go.mod と go.sum が以前ビルドしたイメージから変更が無ければ、このレイヤーの処理に対してキャッシュを使って高速に処理される。

--

レイヤー5
COPY ./main.go  ./

main.goを、イメージ内のWORKDIRにCOPY。
今回はmain.goというファイル一つだけをCOPYしているが、依存パッケージがあればそれらもCOPYする。
この辺りは不必要なものは.dockerignoreで指定して、COPY . ./で必要なものを一気にCOPYするなどの工夫で楽にしても良い。

--

レイヤー6
ARG CGO_ENABLED=0

CGO_ENABLEDを0に設定。
Goはコンパイル時に、CGOを使ってC言語のライブラリを使うように設定する事ができる。
しかし、scratchイメージにはこのライブラリは用意されていない。
よって、今回はC言語のライブラリを使って欲しく無いので、CGO_ENABLEDを0にしてこの機能をOFFにした。

--

レイヤー7
ARG GOOS=linux

GOOS=linuxを設定。
Goはクロスコンパイルを採用していて、実行環境を指定できる。
今回はscratchイメージで動かすので、linuxを宣言した。

--

レイヤー8
ARG GOARCH=amd64

GOARCH=amd64を設定
amdの64Bitを宣言した。

--

レイヤー9
RUN go build \
    -o /go/bin/main \
    -ldflags '-s -w'

アプリのビルド。

オプションに関して

  • -o /go/bin/main: 実行ファイルを/go/binの中にmainという名前で作成する
  • -ldflags '-s -w': 実行ファイルにアプリの動作に関係ないものを入れないためのオプションを付与

--

レイヤー10
FROM scratch as runner

本番環境でのベースイメージを用意し、runnerと名付けた。

--

レイヤー11
COPY --from=builder /go/bin/main /app/main

builderイメージの/go/bin/mainを、runnerの/app/mainにCOPYした。

--

レイヤー12
ENTRYPOINT ["/app/main"]

このdockerイメージを起動したときのエントリーポイントを指定。

Discussion