はじめに
自分のメモ用に殴り書き。この記事では、docker-compose を使って、マルチステージビルドで中間生成される Docker イメージにタグ付けするためのベストプラクティスを紹介します。
TL;DR
- Dockerfile の各ステージには名前を付けましょう。
- 各ステージの中間イメージビルド用の
docker-compose.build.yml
を作りましょう。 - 以上
- なお、docker-compose は、バージョン 3.4 以上である必要があります。
- V3.4 でサポートされた
build
のtarget
という機能を利用します。
- V3.4 でサポートされた
Dockerfile マルチステージビルドのムカつくところ
- Dockerfile のマルチステージビルドは中間イメージにタグを付けてくれません。
- 結果として、
<none>:<none>
という無名の Docker イメージが残存し続けます。 - たまに掃除して上げないと、
docker images
に大量の無名イメージがリストされます。 - 不要なイメージでサーバーストレージも無駄に消費します。
# image:latest は Dockerfile で生成された最終成果物となる Docker イメージ
REPOSITORY TAG IMAGE ID CREATED SIZE
image latest 439521aeed0d 1 hours ago 1.48GB
<none> <none> f683a5456003 1 hours ago 1.33GB
<none> <none> a92a033bf4e7 1 hours ago 1.03GB
<none> <none> 7fa2a4097f16 2 hours ago 1.33GB
<none> <none> 20acbe3cae6c 2 hours ago 1.03GB
<none> <none> dee5b549543c 3 hours ago 1.33GB
<none> <none> 8679ced16d20 3 hours ago 1.03GB
<none> <none> f683a5456003 4 hours ago 1.33GB
<none> <none> a92a033bf4e7 4 hours ago 1.03GB
<none> <none> 7fa2a4097f16 5 hours ago 1.33GB
<none> <none> 20acbe3cae6c 5 hours ago 1.03GB
<none> <none> dee5b549543c 6 hours ago 1.33GB
<none> <none> 8679ced16d20 6 hours ago 1.03GB
...延々と続く
マルチステージビルドって何?
- 公式ドキュメント(英語)
- Docker イメージのビルド工程を複数ステージに分割して最終イメージを生成する、Dockerfile の機能です。
- 複雑になりがちだった Docker イメージ生成プロセスを効率よくするために導入された機能です。
- 例えば、Webアプリケーションサーバーの Docker イメージの場合、フロントエンドのパッケージ生成は、
node:latest
イメージを使い、バックエンドのパッケージ生成は、python:latest
イメージを用いて、最終的にフロントエンドとバックエンドの生成物をalpine:latest
イメージにデプロイするといった感じになるでしょう。(図とか欲しいけど、そんな時間ない、殴り書き)
擬似サンプル: シンプルなマルチステージビルド
- この記事では次のような
Dockerfile
とdocker-compose.yml
を利用して、手順を説明します。
Dockerfile
# 第1ステージビルド: node:latest を用いて、フロントエンドパッケージをビルドする
FROM node:latest
RUN build_frontend_package
# 第2ステージビルド: python:latest を用いて、バックエンドパッケージをビルドする
FROM python:latest
RUN build_backend_package
# 最終ステージビルド: alpine:latest をベースイメージとし、フロントエンドとバックエンドのパッケージを組み込んだ Web アプリケーションサーバを構築する
FROM alpine:latest
COPY --from 0 frontend_package
COPY --from 1 backend_package
RUN build_production frontend_package backend_package
docker-compose.yml
version: "3.4"
services:
web:
build: .
image: image:latest
ポイント1: 各ステージには名前をつけましょう
-
FROM イメージ名 AS ステージ名
を使って、それぞれのステージに名前をつけてください。 - そうすると、Dockerfile 内や、docker-compose から、その名前でステージを参照できます。
- docker-compose でステージ名を指定してビルドするために、ステージに名前を付けるんです。
Dockerfile
# 第1ステージビルド: node:latest を用いて、フロントエンドパッケージをビルドする
FROM node:latest AS frontend_stage
RUN build_frontend_package
# 第2ステージビルド: python:latest を用いて、バックエンドパッケージをビルドする
FROM python:latest AS backend_stage
RUN build_backend_package
# 最終ステージビルド: alpine:latest をベースイメージとし、フロントエンドとバックエンドのパッケージを組み込んだ Web アプリケーションサーバを構築する
FROM alpine:latest AS final_stage
COPY --from frontend_stage frontend_package
COPY --from backend_stage backend_package
RUN build_production frontend_package backend_package
ポイント2: docker-compose.build.yml
というファイルでビルドしましょう
- 執筆段階では、docker-compose を使うなら、
docker-compose.yml
のみで実現するのは無理だと思います。 -
docker-compose.build.yml
では、build
でcontext
とtarget
という設定を入れます。 -
context
には、カレントディレクトリとか Docker のビルドコンテキストのパスを入れておきます。 -
target
には、(先ほど名前をつけた) マルチステージビルドのステージ名を入れて上げます。 - ファイル名は任意です。
docker-compose.build.yml
である必要はありません。
docker-compose.build.yml
version: "3.4"
services:
frontend:
build:
context: .
target: frontend_stage
image: frontend:latest
backend:
build:
context: .
target: backend_stage
image: backend:latest
final:
build:
context: .
target: final_stage
image: image:latest
-
docker-compose.yml
からは、build
設定を抜いておきましょう。(残しておいてもOK。使わないだけ。)
docker-compose.yml
version: "3.4"
services:
web:
image: image:latest
ビルドしてサービスを起動する方法
-
docker-compose build
に、-f
オプションでdocker-compose.build.yml
を指定してビルドします。
$ docker-compose -f docker-compose.build.yml build
- ビルドが完了したら、
docker-compose.yml
を用いて、サービスを起動して上げます。- 注意:
docker-compose.build.yml
ではありません
- 注意:
$ docker-compose up -d
- Docker イメージの一覧を表示すると、中間イメージがタグ付けされています。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
image latest 439521aeed0d 1 hours ago 1.48GB
frontend latest f683a5456003 1 hours ago 1.33GB
backend latest a92a033bf4e7 1 hours ago 1.03GB
まとめ
- Dockerfile の各ステージには名前を付けましょう。
- 各ステージの中間イメージビルド用の
docker-compose.build.yml
を作りましょう。 - 細かな修正とかして、何度も docker-compose ビルドし直したりしても、タグ付けされます。(当たり前)
- 知らず知らずのうちに、手元ビルドで大量に残存していた無名イメージがなくなります。
- マルチステージビルドの中間イメージにタグ付けする方法は他にもありますが、私の試行錯誤したかぎりでは、これがベストプラクティスだと思います。