面接問題:Dockerコンテナは軽量なのになぜ数百MBになるのか
問題¶
Interviewer: If Docker containers are lightweight, why are some containers still hundreds of MBs?
解答¶
「軽量」とはホスト OS のカーネルを共有するという意味であり、コンテナイメージ内に含まれるファイルシステム(ベースイメージ・ライブラリ・アプリケーションコード)のサイズとは別の話。
解説¶
「軽量」の意味¶
VM(仮想マシン):
ゲスト OS カーネル丸ごと含む → 数GB が普通
Docker コンテナ:
ホスト OS のカーネルを共有する → カーネル分は0
ただしユーザースペースのファイルシステムはイメージに含む
コンテナが「軽量」なのは: - プロセス起動が速い(OS ブートが不要) - メモリ消費が少ない(カーネルを共有)
サイズが大きくなる原因は別にある。
コンテナイメージが大きくなる主な原因¶
1. ベースイメージの選択
ubuntu:22.04 → ~77MB
debian:latest → ~124MB
alpine:latest → ~7MB ← 軽量の代表
2. パッケージマネージャのキャッシュ
RUN apt-get update && apt-get install -y curl
→ キャッシュが残る → イメージに含まれる
3. 中間ファイル・ビルドツール
Node.js アプリのビルド:
→ node_modules (~数百MB) がイメージに残る
4. レイヤーの削除不備
RUN apt-get install -y curl # インストール(レイヤー追加)
RUN rm -rf /var/lib/apt/lists # 削除してもレイヤーは残る
イメージを小さくするベストプラクティス¶
# 悪い例: キャッシュが残る
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# 良い例: 1つの RUN にまとめてキャッシュを削除
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
# さらに良い例: Alpine ベースを使う
FROM alpine:3.19
RUN apk add --no-cache curl
Multi-stage build でビルド成果物だけ残す¶
# ステージ1: ビルド(node_modules などを含む)
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ステージ2: 実行(最小限のファイルのみ)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
ビルドツール・ソースコード・テストファイルが含まれず、大幅に軽量化できる。
サイズ比較の目安¶
Go アプリ(静的バイナリ): scratch ベース → ~10MB
Node.js アプリ(最適化): alpine ベース → ~100MB
Python アプリ(未最適化): python:3.12 ベース → ~1GB 超
面接でのポイント¶
- 「軽量 = イメージサイズが小さい」ではなく「軽量 = VM より起動・リソース効率が良い」と正確に答える
- ベースイメージ・キャッシュ・レイヤー構造の話ができると深さが出る
- Multi-stage build を実践経験として挙げられると加点