はじめに

大変遅まきながら、Dockerのマルチステージビルドについて学んだ内容を記載します。

マルチステージビルドとは?

Dockerfileの可読性、メンテナンス性を維持するための仕組みです。2017年5月のDocker Engine 17.05で追加されました。
Dockerでシステム環境を構築する際、「イメージサイズを小さくしたい」、「本番/ステージング/開発環境でDockerfileを分けたい」と考えることはよくあります。この仕組みはそれらを効率的に実現することが可能です。

マルチステージビルド以前

マルチステージビルド以前も、近いことは実現できました。
しかし、実現するためには下記のような煩雑な取り組みが必要で、保守していくのが面倒でした。

  • ビルド用Dockerfile、実行用Dockerfileを準備する
  • 環境ごとにDockerfileを準備する

マルチステージビルド以後

マルチステージビルド導入後は、一つのDockerfileで実現できます。
下記はDockerのドキュメントにあるサンプルですが、FROMを一つのDockerfileに2つ書き、ビルド用のimageと実行用のimageを使い分けています。

# syntax=docker/dockerfile:1
FROM golang:1.26
WORKDIR /src
COPY <<EOF ./main.go
package main

import "fmt"

func main() {
  fmt.Println("hello, world")
}
EOF
RUN go build -o /bin/hello ./main.go

FROM scratch
# 最初のステージから実行ファイルだけをコピーする
# Goのビルド環境は最終イメージに含めない
COPY --from=0 /bin/hello /bin/hello
CMD ["/bin/hello"]

また、下記のように構築ステージに名前をつけることができるため、リリース環境ごとに使い分けることも可能です。

例えばdocker-composeを使っているのなら、下記のようなDockerfile、docker-composeを作成できます。

Dockerfile

# 開発/本番で共通のimage
FROM php:8.4-fpm AS base

WORKDIR /app

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        git \
        unzip \
        libicu-dev \
        libzip-dev \
    && docker-php-ext-install \
        intl \
        mysqli \
        pdo_mysql \
        zip \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# 開発用
FROM base AS development

RUN pecl install xdebug \
    && docker-php-ext-enable xdebug

CMD ["php-fpm"]


# 本番用
FROM base AS production

COPY . /app

RUN chown -R www-data:www-data /app

USER www-data

CMD ["php-fpm"]

docker-compose.yml(抜粋)

  app:
    build:
      context: ..
      dockerfile: docker/app/Dockerfile
      target: development # 構築ステージの名前を指定

    volumes:
      - ..:/app

※ちなみに、Composeの設定を環境ごとに切り替えたい場合は、共通設定に環境別のoverrideファイルを重ねる方法があります。
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d のように指定します。

終わりに

マルチステージビルドを活用し、より効率的なシステム運用を目指します。
まずは開発で使用しているDockerfileで試していきたいと思います。