ぷるぷるした直方体

元職業エンジニアの雑記です

Docker Multistage Buildのユースケース

Multistage Build機能により、Dockerfileを非常に簡潔に書くことができるようになりました。 感動したのでユースケースごとに嬉しい点をまとめておきます。

なお、この内容のメモを書いてからかなり時間が経っているので、すごく今更な記事になってしまいました。Multistage Buildは2017/05にリリースされたバージョンから使えるようですので、1年近く経ってますね……。 また、GCPの公式ブログでMultistage Buildについての記事も上がっています。計測などもあり興味深い記事となっていますので、ぜひ先にご参照ください。本記事のユースケース1を詳細に説明している内容です。

cloudplatform.googleblog.com

ユースケース1: イメージサイズを減らす

これはMultistage Buildを語る上で欠かせないメリットですね。成果物(と必要なライブラリ)をコピーする事により、ビルドだけに必要なプログラムや余計なレイヤーをイメージから除外することができます。

上記は多くのコンテナで共通のメリットですが、特にGo言語のプログラムでは威力が大きいです。なぜなら、Goのプログラムは1バイナリをコピーするだけで動くため、極めて軽量なイメージにできるからです。 それまでは実現できなかった、数MB程度のイメージにすることができます。

before

FROM alpine:latest as builder
...
// build as command

CMD command

数百MB〜

after

FROM ... as builder
...
// build as command

FROM ...
COPY --from=build command command
CMD command

数MB〜

ユースケース2: 開発環境だけで使うプログラム

ホットリロードツールやデバッガなど、開発環境では重宝しますがリリース時には使いたくないプログラムがあります。 Multistage Buildが無い場合、2つのDockerfileを用意する必要がありました(Dockerfile.devDockerfile.prodなど)。ビルドのための準備はそれぞれのファイルで同じなため、両方変更する必要がありメンテナンス性を下げる原因となっていました。

Multistage Buildを使うことにより、ビルドをするコードはそのまま、開発環境だけで使うプログラムをイメージから除外することが単一のDockerfileで可能になります。docker-comopseから使う場合の修正も軽微です。

個人的には、Multistage Buildの最も便利な使い方だと思います。

before

Dockerfile.dev

FROM ...
// ビルド用の準備
// ビルド
// ホットリロードツールのインストール
CMD hot-reload command

Dockerfile.prod

FROM ...
// ビルド用の準備
// ビルド
CMD command

after

FROM ... as builder
// ビルド用の準備
// ビルド
// ホットリロードツールのインストール
CMD hot-reload command

FROM ...
COPY --from=build command command
CMD command

ユースケース3: リファクタリング

上記の2つを実行した副産物ですが、各ステップの依存関係がわかりやすくなります。 例えば2つのプログラムをビルドし、その成果物を使ってプログラムを動かす場合を考えます(プラグインのビルド、ローカル証明書の作成などがそれぞれのステージ)。従来ではイメージサイズを減らすため、Dockerfileの1レイヤーでひたすらビルドをし、最後に実行としていました。 Multistage Buildにより、依存関係をそれぞれのステージに切り出すことができ、よりわかりやすいDockerfileになります。

before

FROM ...
RUN apk pre-cmd1 pre-cmd2 cmd3 ... && \
    build cmd1 ... && \
    cd ... && \
    build cmd2 ... && \
    ...

CMD cmd1 cmd2

after

FROM ... as cmd1
RUN apk pre-cmd1 ... && \
    build cmd1 ...

FROM ... as cmd2
RUN apk pre-cmd2 ... && \
    build cmd2 ...

FROM ...
COPY --from=build cmd1 cmd1
COPY --from=build cmd2 cmd2

CMD cmd1 cmd2

まとめ

Multistage Buildは複雑なDockerfileをシンプルにすることができるとても便利な機能です。軽量で分かりやすいDockerfileを目指して有効活用してゆきましょう。