はじめに
Docker のイメージはマルチステージでビルドしてイメージサイズを小さくするメモ。
ソースコードを Docker のなかでビルドしようとするとビルド時だけ必要なライブラリとかも Docker のイメージに含める必要があり、どうしても最終的な Docker イメージのサイズが大きくなってしまう。 あと Docker イメージの中間レイヤーが増えると最終的な Docker イメージのサイズが大きくなってしまうのでできるだけレイヤーの数を増やさない工夫をする必要があるのだけど、Docker Engine 17.05 で導入されたマルチステージビルドはこの2つの課題を一気に解決することができる。
マルチステージビルドのやり方
マルチステージビルドは FROM
を Dockerfile の中に複数記述するとマルチステージビルドでき、Dockerfile のなかで最後に記述する FROM
のステージが最終的な Docker イメージになる。
# ソースコードをビルドするステージ FROM golang:1.16 AS BUILD # ソースコードを実行して生成したバイナリを実行するステージ FROM busybox
シングルステージビルドとマルチステージビルドでの Docker イメージのサイズを比較
Go のソースコードをシングルステージビルドとマルチステージビルドでそれぞれ Docker のなかでビルドして最終的な Docker イメージのサイズを比較してみる。
Go のソースコードはこんな感じで Hello World!
を出力するだけのもの。
// app.go package main import "fmt" func main() { fmt.Println("Hello World!") }
// go.mod module github.com/ebc-2in2crc/app go 1.16
まずシングルステージビルドしていく。Dockerfile はこんな感じ。
FROM golang:1.16 WORKDIR /go/src/app COPY . . RUN go install -v ./... CMD ["app"]
$ docker image build -t my-golang-app:single-stage-build . $ docker container run --rm my-golang-app:single-stage-build Hello World!
次にマルチステージビルドしていく。Dockerfile はこんな感じ。
FROM golang:1.16 AS BUILD WORKDIR /go/src/app COPY app.go . COPY go.mod . RUN GOOS=linux go build FROM busybox COPY --from=BUILD /go/src/app/app /usr/local/bin/ CMD ["app"]
マルチステージビルドは FROM
のところで AS <ステージの名前>
みたいにしてステージに名前をつけておくと、後続のステージは COPY --from=<ステージの名前> 〜
みたいにして先行するステージのイメージからファイルとかにアクセスできる。
$ docker image build -t my-golang-app:multi-stage-build . $ docker container run --rm my-golang-app:multi-stage-build Hello World!
当たり前だけどシングルステージビルドでもマルチステージビルドでも Docker コンテナを実行すると同じ結果になる。
シングルステージビルドで作った Docker イメージとマルチステージビルドで作った Docker イメージのサイズはこんな感じ。
$ docker image ls | grep my-golang-app my-golang-app single-stage-build 46d618cbdd49 32 minutes ago 864MB my-golang-app multi-stage-build 8dbd2f2d2e87 36 minutes ago 3.17MB
Docker のイメージはシングルステージビルドしたものが 864MB でマルチステージビルドしたものが 3.17MB とマルチステージビルドしたイメージのほうが圧倒的に小さくなってる。 マルチステージビルドのほうは busybox をベースイメージにしてるので非常に小さくなってるけど実際は distroless をベースイメージにすることのほうが多いと思う。 distroless は busybox に比べたらサイズは大きいけど手元でマルチステージビルドしてみたら Docker イメージは 21.1MB で golang:1.16 でシングルステージビルドしたものに比べたら40倍以上は小さくできている。
自分が作ってる Docker イメージはまだシングルステージビルドしているものがあるけどあえてシングルステージビルドする必要はないと思うし、いまは新しく作る Docker イメージは基本的にマルチステージビルドするようにしてる。