実践ガイド
はじめての Dockerfile
Node.js と Python の小さなアプリを題材に、最小構成の Dockerfile を書いてビルド・実行するまでの流れを通します。
Dockerfile は「ビルド手順書」
Dockerfile はイメージをビルドする手順を宣言的に書いたテキストファイルです。
1 行が 1 つの命令になっていて、上から順に実行され、命令ごとに新しいレイヤーが作られます。
最低限覚える命令は次の通りです。
| 命令 | 役割 |
|---|---|
FROM |
ベースイメージを指定。最初に必ず書く |
WORKDIR |
以降の命令のカレントディレクトリ |
COPY |
ホストのファイルをイメージに取り込む |
RUN |
ビルド時に実行するコマンド(apt install など) |
CMD |
コンテナ起動時に実行する既定コマンド |
EXPOSE |
どのポートで待ち受けるかのメタ情報(実際のポート公開は docker run -p) |
ENV |
環境変数の設定 |
Node.js アプリをコンテナ化する
server.js と package.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-appPython アプリをコンテナ化する
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 パターン
docker runで応答がない: アプリが127.0.0.1やlocalhostで待ち受けている。コンテナ内のlocalhostは外から見えないので、0.0.0.0で待ち受けるdocker buildでCOPY failed: コピー元パスがビルドコンテキストの外にある。ビルドコンテキスト(.で指定したディレクトリ)の外のファイルは参照不可- イメージが肥大化:
.dockerignore不備 +apt install後のキャッシュ削除忘れ。RUN apt-get update && apt-get install -y foo && rm -rf /var/lib/apt/lists/*のように同じレイヤーで削除する - 毎回フルビルドで遅い: 依存定義のコピー順が悪く、ソース変更のたびに
npm installが走っている exec形式とshell形式の違い:CMD node server.jsは shell 経由で起動し、シグナルがアプリに届きにくい。CMD ["node", "server.js"]の JSON 配列形式(exec 形式)を推奨
