コンテンツにスキップ

面接問題: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 を実践経験として挙げられると加点