応用読了目安 14 分#Docker#コンテナ#イメージ#デプロイ
コンテナ — 「手元では動く」を構造的に潰す
仮想マシンとの違いはどこか。イメージが層でできていることが、ビルド時間とデプロイの両方を決める。
この記事の進み方
What — 仮想マシンとの違い
ハードウェアを仮想化し、ゲスト OS を丸ごと動かす。
┌─────────┬─────────┐
│ アプリ │ アプリ │
│ ライブラリ│ライブラリ │
│ ゲスト OS │ ゲスト OS │ ← OS ごと入っている
├─────────┴─────────┤
│ ハイパーバイザ │
│ ホスト OS │
│ ハードウェア │
└───────────────────┘- 起動時間
- 数十秒〜数分
- サイズ
- 数 GB
- 分離の強度
- 強い
- 別 OS の実行
- できる
- –OS レベルで分離されるため、セキュリティ境界として信頼できる。
- –異なるカーネル(Linux 上で Windows など)を動かせる。
コンテナは OS カーネルを共有し、ユーザー空間だけを分離する。この違いが、起動時間とサイズの差を生む。
イメージは層でできている
FROM node:20-slim+ OS + Nodeベースイメージほぼ変わらない
COPY package*.json ./+ package.json依存関係の定義だけを先にコピー依存を追加したときだけ変わる
RUN npm ci+ node_modules依存をインストール最も重い工程
COPY . .+ アプリのコードソースコードをコピー毎回変わる
RUN npm run build+ 成果物ビルド毎回実行される
キャッシュが効く範囲
OS + Nodeデータ本体
1/5
FROM node:20-slim — 最も変わりにくい。一度取得すればキャッシュされ続ける。タグではなくダイジェストで固定すると、より再現性が高まる。
変更のあった層より下(後ろ)だけが再ビルドされる。だから、変わりにくいものを先に書くとビルドが速くなる。
この順序が逆だと、ソースを1行変えるたびに npm ci が走ります。 ビルドが5分から30秒になるかどうかは、この数行の順序で決まります。
マルチステージビルド
ビルドに必要なものと、実行に必要なものは違います。
# ① ビルド用ステージ(コンパイラや devDependencies が必要)
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ② 実行用ステージ(成果物だけを持ってくる)
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
USER node # root で動かさない
CMD ["node", "dist/main.js"]
最終イメージには、ビルドツールもソースコードも含まれません。 サイズが小さくなるだけでなく、攻撃対象も減ります。
- イメージ
- コンテナの雛形。読み取り専用の層の集まり。
- コンテナ
- イメージから起動した実行中のインスタンス。書き込み可能な層が1つ上に乗る。
- レイヤー
- Dockerfile の命令ごとに作られる差分。キャッシュの単位でもある。
- ダイジェスト
- イメージの内容から計算されるハッシュ(sha256:...)。タグと違い、絶対に変わらない。
- タグ
- イメージに付ける別名。上書きできるので、同じタグが別のイメージを指すことがある。
- ボリューム
- コンテナの外にデータを永続化する仕組み。コンテナを消してもデータは残る。
確認 — ここまで読めたか
image: myapp:latest を本番で使うと、何が起きますか?
Why — なぜ「手元では動く」が起きなくなるのか
「手元では動く」の原因は、環境の差です。
- OS のバージョンが違う
- ライブラリのバージョンが違う
- 環境変数が違う
- そもそも入っているものが違う
コンテナは、アプリと、それが依存するもの全部を1つのイメージに固めることでこれを解消します。イメージが同じなら、どのホストで動かしても中身は同じです。
ただし、完全に同じにはなりません。
- ホストのカーネルバージョンは共有される
- CPU アーキテクチャ(arm64 / amd64)は違いうる
- 環境変数・シークレット・接続先は外から注入される
- ボリュームでマウントされたデータは環境ごとに違う
**「差分をゼロにする」のではなく、「差分を環境変数とボリュームだけに絞る」**というのが正確な理解です。
コンテナは使い捨て
コンテナの設計思想は「いつ消えてもよい」です。
- ログはファイルではなく標準出力に書く(コンテナが消えても収集済み)
- 状態はコンテナ内に持たない(DB やオブジェクトストレージに置く)
- 起動に時間をかけない(スケールアウトの速さが利点)
- 停止シグナルを受けたら、処理中のリクエストを終えてから終了する(graceful shutdown)
この前提が守られていないと、オートスケールもローリングデプロイも安全に使えません。
確認 — ここまで読めたか
Dockerfile でアプリのコードを先にコピーし、その後に依存をインストールすると何が起きますか?
演習 — まず自分で判断する
解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。
演習 — 設計判断を問う
この Dockerfile の問題点を、影響の大きい順に挙げてください
与えられた条件
- FROM node:latest
- WORKDIR /app
- COPY . .
- RUN npm install
- RUN npm run build
- ENV DB_PASSWORD=prod_secret_123
- EXPOSE 3000
- CMD ["npm", "start"]
- 運用状況: ビルドに毎回 6 分かかる。イメージサイズは 1.4GB。デプロイは image: myapp:latest で指定している
この軸で考える
- · 再現性を壊しているのはどこか
- · ビルド時間とイメージサイズの原因はどこか
- · セキュリティ上、最も重大な問題はどれか
演習 — 説明できるか
「切り戻せなかった」障害の振り返りで、何が原因だったかを説明してください
与えられた条件
- マニフェストは latest を指定していた
- ノードによって動いているバージョンが違った
- 再発防止まで決めたい
読み終わりましたか?
読了にすると、これを前提とする記事がロードマップで開放されます。