「Dockerイメージが2GBもあって,ビルドもpullも遅い」── これは個人開発でありがちな状態だ.だが適切に最適化すれば,同じアプリのイメージが数十分の一のサイズになることも珍しくない.

イメージが小さく軽いことは,ビルドの速さ・デプロイの速さ・ストレージ節約・攻撃面の縮小とすべてに効く.そして最適化の多くは,Dockerfileの書き方を変えるだけで実現できる.

この記事は,VPSにDockerでデプロイする個人開発者,イメージの肥大化とビルドの遅さに悩むエンジニアに向けている.マルチステージビルド・レイヤーキャッシュ・ベースイメージ選びを実践的に解説する.

読み終えたとき,あなたのイメージは小さく,ビルドが速く,デプロイが軽快で,より安全になっている.

なぜイメージ最適化が効くのか

イメージが大きいと,ビルドが遅く,レジストリへのpush/pullが遅く,ストレージを食う.デプロイのたびにこの遅さが積み重なり,開発のテンポを削ぐ.

さらに大きなイメージは不要なものを多く含む──ビルドツール・開発依存・キャッシュなど.これらは本番では不要なだけでなく,攻撃面(脆弱性を含みうるパッケージ)を広げる

最適化の核心は,「本番に必要なものだけを含む,小さなイメージ」を作ることだ.これによりビルドもデプロイも速くなり,安全性も上がる.

そしてその手段── マルチステージビルドとレイヤーキャッシュ── は,Dockerfileの書き方を変えるだけで導入できる.コストはほぼゼロで効果は大きい.

イメージが肥大化する原因を知る

最適化の前に,なぜ大きくなるかを理解しよう.主な原因は(1)重いベースイメージ (2)ビルドツールや開発依存を本番イメージに含めている (3)レイヤーにキャッシュやゴミが残っている (4)不要なファイルをコピーしているだ.

特に大きいのが「ビルドに必要だが実行には不要なもの」を含めてしまうことだ.コンパイラ,ビルドツール,開発用パッケージ── これらは本番イメージには不要なのに,素朴に作ると全部入ってしまう.

マルチステージビルド ― ビルド環境と本番を分離する

最強の最適化がマルチステージビルドだ.「ビルド用のステージ」と「本番用のステージ」を分け,本番には成果物だけをコピーする.ビルドツールは本番イメージに一切残らない.

マルチステージビルド(Node例)

# --- ビルドステージ ---
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build       # 成果物を dist/ に生成

# --- 本番ステージ(軽量) ---
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev    # 本番依存のみ
COPY --from=builder /app/dist ./dist   # 成果物だけ持ってくる
CMD ["node", "dist/server.js"]

ポイントはCOPY --from=builderビルドステージの成果物だけを本番ステージに持ってくることだ.ビルドに使った重いツールやソースは本番イメージに含まれない.コンパイル言語(Go等)なら,本番ステージはscratchやalpineにバイナリ1つ,という極小イメージも可能だ.

コンパイル言語なら劇的に小さくなる

Goのようにシングルバイナリにコンパイルできる言語では,ビルドステージでコンパイルし,本番ステージは最小ベース+バイナリだけにできる.数百MBが数MB〜十数MBになることもある.「ビルド環境と実行環境の分離」の効果が最も劇的に出る例だ.

レイヤーキャッシュ ― 命令の順序で速さが変わる

Dockerは各命令(RUNCOPY等)をレイヤーとしてキャッシュする.変更がなければキャッシュを再利用するため,ビルドが速い.逆に,順序を誤るとキャッシュが効かず毎回フルビルドになる.

キャッシュを効かせる命令順序

# 良い例: 依存ファイルを先にコピーして依存インストール
COPY package*.json ./
RUN npm ci              # package.jsonが変わらなければキャッシュ再利用
COPY . .                # ソースの変更はこのレイヤー以降だけ再ビルド

# 悪い例: 先に全部コピーすると,1行変えただけで
# npm ci からやり直しになる
# COPY . .
# RUN npm ci

鉄則は「変更頻度の低いものを先に,高いものを後に」だ.依存定義(package.json)は変わりにくく,ソースコードは頻繁に変わる.依存インストールを先に置けば,ソースを1行変えただけのビルドでも依存インストールのキャッシュが再利用され,劇的に速くなる.

.dockerignore ― 不要なものをビルドに送らない

COPY . .は,カレントディレクトリの全ファイルをビルドコンテキストに送る.node_modules.git・ログ・.envなどを除外しないと,イメージが膨らみ,秘密情報の混入リスクもある.

.dockerignore の例

node_modules
.git
.env
*.log
dist
tmp
.DS_Store
README.md

.dockerignore.gitignoreのDocker版だ.ビルドに不要なものを除外することで,イメージが小さくなり,ビルドコンテキストの転送も速くなる.特に.env.gitを除外することは,秘密情報の漏洩防止の観点でも重要だ.

ベースイメージの選び方 ― slim・alpineの活用

ベースイメージの選択でサイズは大きく変わる.フルイメージ → slim → alpineの順に小さくなる.用途に応じて選ぶ.

ベース特徴向き
full (例: node:20)何でも入っている/大きいビルドステージ向き
slim (node:20-slim)最小限のOS/中庸本番の標準的な選択
alpine (node:20-alpine)極小/musl libcサイズ最優先(互換性に注意)

本番ステージはslimを標準にし,さらにサイズを攻めたいときにalpineを検討する.ただしalpineはmusl libcを使うため,一部のネイティブ依存で互換性問題が出ることがある.「小ささ」と「互換性の安心」のトレードオフを理解して選ぶ.

最適化の効果を測る ― サイズと脆弱性を確認

最適化したら効果を確認する.イメージサイズと,含まれる脆弱性を見るのが基本だ.

サイズと中身の確認

docker images                      # イメージサイズ一覧
docker history myimage:latest      # レイヤーごとのサイズ
docker scout cves myimage:latest   # 脆弱性スキャン(または trivy)

docker historyどのレイヤーが大きいかが分かり,最適化の的を絞れる.加えてdocker scoutやtrivyのような脆弱性スキャンで,イメージに含まれる既知の脆弱性を確認する.小さいイメージは含むパッケージが少ないぶん,脆弱性も少なくなる傾向がある.

補論:軽いイメージは「速いVPS」でデプロイ体験を最大化する

イメージ最適化の効果── ビルドとpull/pushの速さ── は,VPSのディスクとネットワークの速さと掛け算で効く.遅い基盤では,せっかく小さくしたイメージのpullも遅い.

高速NVMe・50種類以上のOSテンプレートに対応した国内VPS─シン・VPS─ はNVMe SSDでイメージのビルド・展開が高速で,最適化した軽量イメージのデプロイがさらに軽快になる.スナップショットを取ってからイメージを入れ替えれば,新イメージに問題があっても即座に戻せるので,最適化の試行錯誤を安心して進められる.

軽量イメージで素早くデプロイできるサービスを,独自ドメインで公開しよう.取り扱い400種類以上のドメイン取得サービス─ムームードメイン─ でドメインを取得すれば,「小さなイメージ→速いデプロイ→HTTPS公開」の軽快なサイクルが回る.

よくある質問(FAQ)

Q1.一番効く最適化は?

マルチステージビルドだ.ビルドツールや開発依存を本番イメージから排除でき,サイズが劇的に下がる.特にコンパイル言語では数百MBが数MBになることもある.

Q2.ビルドが毎回遅い

レイヤーキャッシュの順序を見直す.依存定義(package.json等)を先にコピーして依存インストールし,ソースのコピーを後に置く.これでソース変更時も依存インストールがキャッシュされる.

Q3.alpineは使うべき?

サイズ最優先なら有効だが,musl libcによる互換性問題に注意.ネイティブ依存があるアプリでは動かないことがある.まずslimを標準にし,問題なければalpineを検討する.

Q4..dockerignoreは必須?

実質必須だ.node_modules.git.envを除外しないと,イメージが膨らみ秘密情報混入のリスクもある..gitignoreのDocker版として必ず用意する.

Q5.イメージサイズはどこで確認する?

docker imagesでサイズ一覧,docker historyでレイヤーごとの内訳を確認する.大きいレイヤーを特定して最適化の的を絞る.

Q6.小さいと安全性も上がる?

傾向としてそうだ.含むパッケージが少ないほど,既知の脆弱性も少なくなりやすい.攻撃面が縮む.docker scoutやtrivyで脆弱性をスキャンして確認するとよい.

まとめ ― 小さなイメージは「速さ」と「安全」を同時に得る

Dockerイメージ最適化の核心は,マルチステージビルドで本番に必要なものだけを含め,レイヤーキャッシュを効かせ,不要なものを.dockerignoreで除外することだ.これでビルドもデプロイも速く,より安全になる.

今日やるべきことは,自分のDockerfileをマルチステージ化し,依存インストールをソースコピーより前に置くこと.イメージサイズが大きく下がるのを確認できる.

小さなイメージは,速さと安全を同時にもたらす.NVMe SSDのVPSと組み合わせれば,デプロイ体験は驚くほど軽快になる.