実践ガイド

はじめての Dockerfile

Node.js と Python の小さなアプリを題材に、最小構成の Dockerfile を書いてビルド・実行するまでの流れを通します。

はじめての Dockerfile diagram

Dockerfile は「ビルド手順書」

Dockerfile はイメージをビルドする手順を宣言的に書いたテキストファイルです。

1 行が 1 つの命令になっていて、上から順に実行され、命令ごとに新しいレイヤーが作られます。

最低限覚える命令は次の通りです。

命令 役割
FROM ベースイメージを指定。最初に必ず書く
WORKDIR 以降の命令のカレントディレクトリ
COPY ホストのファイルをイメージに取り込む
RUN ビルド時に実行するコマンド(apt install など)
CMD コンテナ起動時に実行する既定コマンド
EXPOSE どのポートで待ち受けるかのメタ情報(実際のポート公開は docker run -p
ENV 環境変数の設定

Node.js アプリをコンテナ化する

server.jspackage.json があるシンプルな Express アプリを例にします。

FROM node:20-alpine

WORKDIR /app

# まず依存定義だけコピーして依存をインストール
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# 残りのソースをコピー
COPY . .

EXPOSE 3000
CMD ["node", "server.js"]

ポイントは、依存定義(package.json)をソースより先にコピーしていることです。

これによりソースだけ書き換えたビルドでは、npm ci の重い層がキャッシュから再利用され、ビルドが圧倒的に速くなります。

bash
# ビルドして起動
docker build -t my-node-app .
docker run --rm -p 3000:3000 my-node-app

Python アプリをコンテナ化する

FastAPI を例にすると次のようになります。

FROM python:3.12-slim

WORKDIR /app

# pip のキャッシュを無効化してイメージを軽くする
ENV PIP_NO_CACHE_DIR=1 PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1

# 依存を先に入れる
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

PYTHONUNBUFFERED=1 を入れておくと、print やログが バッファされずに即 stdout に出るので、docker logs で見たときに詰まりません。

--host 0.0.0.0 も忘れないように: 既定の 127.0.0.1 だとコンテナの外からアクセスできません。

.dockerignore を最初から置く

.dockerignore は Git の .gitignore と似た役割で、COPY . . の対象から除外するファイルを書きます

置かないと、node_modules・ローカルの .env.git の履歴丸ごとなど、入れてはいけないものがイメージに混入します。

# .dockerignore
node_modules
.venv
__pycache__
.git
.gitignore
.env
*.log
Dockerfile
.dockerignore

特に .git を入れ忘れるとイメージサイズが数十 MB 〜 GB 単位で膨らみます。

最初の Dockerfile と同時に .dockerignore も書く癖をつけてください。

初心者がよく詰まる 5 パターン

  1. docker run で応答がない: アプリが 127.0.0.1localhost で待ち受けている。コンテナ内の localhost は外から見えないので、0.0.0.0 で待ち受ける
  2. docker buildCOPY failed: コピー元パスがビルドコンテキストの外にある。ビルドコンテキスト(. で指定したディレクトリ)の外のファイルは参照不可
  3. イメージが肥大化: .dockerignore 不備 + apt install 後のキャッシュ削除忘れ。RUN apt-get update && apt-get install -y foo && rm -rf /var/lib/apt/lists/* のように同じレイヤーで削除する
  4. 毎回フルビルドで遅い: 依存定義のコピー順が悪く、ソース変更のたびに npm install が走っている
  5. exec 形式と shell 形式の違い: CMD node server.js は shell 経由で起動し、シグナルがアプリに届きにくい。CMD ["node", "server.js"] の JSON 配列形式(exec 形式)を推奨

関連トピック