COMPANY SERVICE STAFF BLOG NEWS CONTACT
2022.08.22

Dockerマルチステージビルドのすすめ

テクログdevelopment

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

マルチステージビルドは、一連の手順を経て Docker イメージを作成するプロセスです。

各ステージは、ビルドに必要なツールを読み込んだり、削除したりするという共通なタスクを実行するベースの Docker イメージを経由してそれぞれのステージのイメージが作成されます。

マルチステージビルドのメリット

従来の Docker 環境の作り方ですと、開発環境用に一つ Dockerfile を用意して、アプリケーションの構築に必要なものを入れる。 そこから本番環境用に、 アプリケーションそのものとそれを動かすために必要なもののみを含むスリム化した Dockerfile をもう 1 つ用意するのが一般的でした。

この場合、1 つのプロジェクトに対して 2 つの Dockerfile を保守していく必要がありました。これはプロジェクトが増えていくと保守する量も増えてあまりコストパフォーマンスが高いとは言えない。

マルチステージビルドは、開発環境、テスト環境、本番環境にかかわらず、1 つの Dockerfile で共通部分をベースにして、それぞれの環境のイメージを作成することが可能です。そうすることで可読性も保守性も上がります。

マルチステージビルドの書き方

今回 node アプリの開発と本番環境のビルドを例とします。

まずは共通部分をベースとして書きます。

FROM node:16-buster-slim AS base

RUN set -eux; \
  apt-get update; \
  apt-get install -y --no-install-recommends \
    ca-certificates \
    curl \
    gnupg2 \
    procps \
    tzdata \
    unzip \
    wget

COPY ./entrypoint.sh /usr/bin/entrypoint.sh
RUN chmod +x /usr/bin/entrypoint.sh

FROM で node イメージを docker hub から読み込み、それぞれのサーバーに応じて必要なパッケージをインストールします。

そしてからまず開発環境から

FROM base as development
ENV NODE_ENV=development
WORKDIR /home/app
COPY ./app/src ./src
COPY ./app/package*.json  ./
RUN npm i
RUN npm run build
ENTRYPOINT [ "entrypoint.sh" ]

FROM base as development で名前を付けることで、このステージは development であると指定できます。

ソースと package.json をコピーして npm install することで node_module がしっかり生成されます。

ここで npm run build している理由としては後で本番環境で使うためです。本番環境では src/ 配下のソースは必要なく、ビルドで生成された dist/ 配下のみ必要なので、development の段階ですることによって本番環境で dist のみコピーすればいいことになります。

続いて、開発環境で npm install して生成された node_modules は開発環境のみに使われるパッケージも含まれています。なので本番環境用にさらにスリム化された node_modules を用意します。

FROM base AS pruned
WORKDIR /home/app
COPY ./app/package*.json ./
RUN npm i --production
RUN curl -sf https://gobinaries.com/tj/node-prune | sh
RUN node-prune

npm install –production にすることで本番に必要なパッケージのみインストールして、また node-prune というツールを使って node_module の不必要なファイルを削除します。

これで本番環境のイメージも作るための準備も終わりました。

FROM base AS production
ENV NODE_ENV=production
WORKDIR /home/app
COPY --from=development /home/app/dist ./dist
COPY --from=pruned /home/app/package*.json ./
COPY --from=pruned /home/app/node_modules ./node_modules
ENTRYPOINT [ "entrypoint.sh" ]

–from=development で development ステージでビルドされた dist をコピーし、 –from=pruned で pruned ステージで用意された package.json と node_modules を持ってきます。そうすることで本番環境には必要最低限のものしか読み込まれません。

entrypoint の書き方は自由です。

if [ $NODE_ENV == "development" ]; then
  npm run start:dev
else
  npm run start:prod
fi

以上説明した Dockerfile をつなげて書くと

FROM node:16-buster-slim AS base

RUN set -eux; \
  apt-get update; \
  apt-get install -y --no-install-recommends \
    ca-certificates \
    curl \
    gnupg2 \
    procps \
    tzdata \
    unzip \
    wget

COPY ./entrypoint.sh /usr/bin/entrypoint.sh
RUN chmod +x /usr/bin/entrypoint.sh


FROM base as development
ENV NODE_ENV=development
WORKDIR /home/app
COPY ./app/src ./src
COPY ./app/package*.json  ./
RUN npm i
RUN npm run build
ENTRYPOINT [ "entrypoint.sh" ]


FROM base AS pruned
WORKDIR /home/app
COPY ./app/package*.json ./
RUN npm i --production
RUN curl -sf https://gobinaries.com/tj/node-prune | sh
RUN node-prune


FROM base AS production
ENV NODE_ENV=production
WORKDIR /home/app
COPY --from=development /home/app/dist ./dist
COPY --from=pruned /home/app/package*.json ./
COPY --from=pruned /home/app/node_modules ./node_modules
ENTRYPOINT [ "entrypoint.sh" ]

この記事を書いた人

core-corp

入社年
出身地
業務内容
特技・趣味

テクログに関する記事一覧