「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は各命令(RUN,COPY等)をレイヤーとしてキャッシュする.変更がなければキャッシュを再利用するため,ビルドが速い.逆に,順序を誤るとキャッシュが効かず毎回フルビルドになる.
キャッシュを効かせる命令順序
# 良い例: 依存ファイルを先にコピーして依存インストール
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と組み合わせれば,デプロイ体験は驚くほど軽快になる.