[Docker] マルチステージビルドって?
バックエンドエンジニアは、Dockerに触れる機会が多いと思います。Dockerを使用するにあたり必ずと言っていいほど必要になってくるのが、Dockerfileですね。 なんとなくDockerfileを使用していると気づかないうちにDockerイメージが大きくなってしまい、イメージの容量でPCがいっぱいいっぱいになってしまいます。 せっかくなのでこの記事を読んでDockerイメージを軽くしましょう!
目次
Dockerイメージの容量を減らすことによるメリット
- PCなどのディスク容量を圧迫しない
- GCPやAWSなどのストレージ系料金を抑えられる
- Deploy時間を短縮できる
Dockerイメージ小さくしたくなりましたか?したくなりましたね。
Dockerイメージを削減する方法で、COPY
やRUN
などの記載を少なくし、レイヤーを削減する方法がありますが、
今回は一番効果の大きいマルチステージビルドを利用した方法を説明します。
まずは、公式のドキュメントです。
公式: マルチステージビルドの利用
んーわたし的にわかりやすく説明しようと思います。
マルチステージビルドの文法は Docker Engine 17.05 から導入されています。
例を記載しながら説明します。
今回の例はnpm, typescript, expressで記載されたバックエンドサーバーのプロジェクトとします。package.json
の例(他に多くのパッケージがありますが、省略しています)
{
"name": "example",
"scripts": {
"build": "rm -rf dist && tsc",
"start": "node dist/index.js",
},
"dependencies": {
"express": "^4.18.1",
...
},
"devDependencies": {
"typescript": "^4.6.4",
...
}
}
まず、マルチステージビルドを使用しないでDockerfileを記載してみます。
single stage build
FROM node:16-slim
ENV APP_ROOT /app/
WORKDIR $APP_ROOT
COPY . $APP_ROOT
RUN npm ci && npm run build
CMD npm start
よく、Qiitaの例であるやつですね。
次にマルチステージビルドを使用した例です。
バックエンドサービスとして動く動作はもちろん同じです。
multi stage build 1
# ------------------------
# First stage: builder
# ------------------------
FROM node:16-slim AS builder
ENV HOME=/build
WORKDIR $HOME
COPY . $HOME
RUN npm ci && npm run build
# ------------------------
# Second stage: release
# ------------------------
FROM node:16-slim AS release
ENV HOME=/app
WORKDIR $HOME
COPY package* $HOME/
COPY --from=builder /build/dist $HOME/dist
RUN npm ci
CMD npm start
First stage
で全体をコピーし、ビルド(tsc)を実施しています。
次にSecond stage
で必要なファイルのみFirst stageからコピーしています。
実際にサーバーが動くために必要なファイルは、コマンド実行とnode_modules作成のためのpackage.json(package.lock.json)
とビルドした結果のdist
だけですよね?
まだ無駄なところがあります。npm ci
をするときにdevDependenciesに記載されているパッケージもインストールされていますね。しかし実際にサーバーとして動かすためにdevDependenciesに記載されているものは必要ないので、無駄に容量が大きくなってしまいます。
必要なパッケージだけインストールするようにしましょう npm ci --production
multi stage build 2
# ------------------------
# First stage: builder
# ------------------------
FROM node:16-slim AS builder
ENV HOME=/build
WORKDIR $HOME
COPY . $HOME
RUN npm ci && npm run build
# ------------------------
# Second stage: release
# ------------------------
FROM node:16-slim AS release
ENV HOME=/app
WORKDIR $HOME
COPY package* $HOME/
COPY --from=builder /build/dist $HOME/dist
RUN npm ci --production
CMD npm start
いい感じになりましたね。 実際にできたイメージを比較してみましょう
single-stage-buildとmulti-stage-build2で比べると、約1/6になりました。ここまで変わるとは…
この結果を見るにやらない理由がない!
フロントエンドなら動作にnode_modulesが必要ないのでさらにイメージが小さくなりそうですね。
以上で検証を終わりにしたいと思います。
PCへの負担もクラウドの料金もビルド時間も少ない方がいいですよね