Dockerfileとは?書き方と主要命令・ベストプラクティスを実例で解説【入門〜実務】
Dockerfileは、Dockerイメージのビルド手順を1つのテキストに記述する「設計図」です。これを用意しておけば、同じ環境を誰のマシンでも・何度でも再現でき、開発から本番までの環境差をなくせます。本記事では、Dockerfileの基本(とは・役割)から書き方の手順、FROM・RUN・CMDなどの主要命令、初心者がつまずきやすいRUN・CMD・ENTRYPOINTの違いやCOPYとADDの違い、そして実務で効くベストプラクティスまでを、実例とともに解説します。
目次
まとめ(先に要点)
細かい内容に入る前に、この記事の結論を先にまとめます。
- Dockerfileとは、Dockerイメージを作るための命令を上から順に書いたテキストファイル。コンテナそのものではなく「イメージの設計図」です。
- 基本の流れは「Dockerfileを書く →
docker buildでイメージ化 →docker runでコンテナ起動」。最小構成はFROM→WORKDIR→COPY→RUN→CMDの数行で書けます。 - つまずきやすい違いは2つ。RUNはビルド時に実行されレイヤを作る/CMD・ENTRYPOINTは起動時のコマンド。COPYは単純コピー/ADDはURL取得やtar展開もできるが通常はCOPY推奨。
- 実務のコツは、変わりにくい命令を上に置いてキャッシュを効かせる・RUNをまとめてレイヤを減らす・軽量ベースとマルチステージでサイズを抑える・非rootユーザーで動かす、の4点が基本です。
以下で、それぞれを順に詳しく見ていきます。
Dockerfileとは何か(定義と役割)
Dockerfileとは、Dockerイメージを作成するための一連の命令を記述したテキストファイルです。ベースとなるイメージを指定し、その上に必要なソフトウェアのインストールやファイルの配置、起動コマンドなどを積み重ねる形で書きます。記述した命令は上から順番に実行され、その結果がイメージとして保存されます。
ここで押さえたいのが、Dockerfile・イメージ・コンテナの関係です。Dockerfileは設計図、イメージはその設計図から作られたテンプレート(不変のスナップショット)、コンテナはイメージから起動した実行中の実体です。Dockerfileをコードとして管理することで、環境構築を自動化し、チーム全体で同じ環境を再現できます。Docker自体の仕組みやコンテナという概念をまず整理したい場合は、Dockerとは何か?仮想環境との違いや特徴をわかりやすく解説やコンテナ技術とは何か:仮想化との違いや基本概念を解説もあわせて参照してください。
Dockerfileの書き方(基本の手順と最小例)
Dockerfileの作成からコンテナ起動までは、次の3ステップです。
- ① 書く:プロジェクトのルートに
Dockerfileという名前(拡張子なし)のファイルを作り、命令を記述します。 - ② ビルドする:
docker buildでDockerfileからイメージを作成します。末尾の.はビルドコンテキスト(参照するファイル群の起点)です。 - ③ 起動する:
docker runでイメージからコンテナを起動します。
Pythonアプリを動かす最小構成のDockerfileと、そのビルド・起動コマンドは次のとおりです。
# ベースイメージを指定(軽量なslim版を使用)
FROM python:3.13-slim
# 作業ディレクトリを設定
WORKDIR /app
# 依存定義を先にコピー(キャッシュを効かせるため本体より前に置く)
COPY requirements.txt .
# パッケージをインストール
RUN pip install --no-cache-dir -r requirements.txt
# アプリ本体をコピー
COPY . .
# コンテナ起動時に実行するデフォルトコマンド
CMD ["python", "app.py"]
# イメージをビルド(-t で名前:タグを付与)
docker build -t myapp:1.0 .
# コンテナを起動
docker run myapp:1.0
ビルドして起動した先の操作(docker run のオプションやネットワーク・Docker Composeでの複数コンテナ起動など)は、Dockerコンテナを起動するための基本的な手順と実践方法で詳しく扱っています。
Dockerfileの主要命令リファレンス
Dockerfileで使う主な命令を一覧にまとめます。まずは FROM / RUN / COPY / CMD / WORKDIR の5つを押さえれば、ほとんどのケースに対応できます。
| 命令 | 役割 | 記述例 |
|---|---|---|
| FROM | ベースイメージ指定(先頭) | FROM python:3.13-slim |
| RUN | ビルド時にコマンド実行 | RUN apt-get update |
| COPY | ファイルをイメージへコピー | COPY . /app |
| ADD | コピー+URL/tar展開 | ADD app.tar.gz /app |
| CMD | 起動時の既定コマンド | CMD ["python","app.py"] |
| ENTRYPOINT | 起動時の固定実行コマンド | ENTRYPOINT ["nginx"] |
| WORKDIR | 作業ディレクトリ指定 | WORKDIR /app |
| ENV | 環境変数の設定 | ENV APP_ENV=production |
| ARG | ビルド時変数の定義 | ARG VERSION=1.0 |
| ONBUILD | 後続ビルドで実行する命令を予約 | ONBUILD COPY . /app |
| EXPOSE | 使用ポートの明示 | EXPOSE 80 |
| VOLUME | 永続化ディレクトリ指定 | VOLUME /data |
| USER | 実行ユーザー指定 | USER appuser |
| LABEL | メタデータ付与 | LABEL version="1.0" |
| HEALTHCHECK | 正常性チェック定義 | HEALTHCHECK CMD curl -f ... |
なお EXPOSE はポートを「使う予定」を記録するだけで、実際の公開は docker run -p で行います。ARG はビルド時のみ有効な変数、ENV はビルド後のコンテナでも使える環境変数、という違いも覚えておくと混乱しません。各命令の細かな仕様はバージョンで追加・変更されることがあるため、最終的には公式リファレンスで最新を確認してください。
混同しやすい命令の違い
初心者が最もつまずくのが、似た役割を持つ命令の使い分けです。検索でもよく調べられる2組を、比較表で整理します。
RUN・CMD・ENTRYPOINTの違い
この3つはどれも「コマンドを書く」点が共通するため混同されがちですが、実行される「タイミング」が決定的に異なります。RUNはビルド時、CMDとENTRYPOINTはコンテナ起動時に作用します。
| 命令 | 実行タイミング | レイヤ | 主な用途 |
|---|---|---|---|
| RUN | ビルド時 | 作る | インストール・構築 |
| CMD | 起動時 | 作らない | 既定コマンド(上書き可) |
| ENTRYPOINT | 起動時 | 作らない | 固定の実行コマンド |
使い分けの要点は次のとおりです。RUN は apt-get install のようにイメージを作る作業に使い、実行のたびに新しいレイヤを作ります。CMD はコンテナ起動時の既定コマンドで、docker run イメージ名 別コマンド のように起動時の引数で簡単に上書きできます(複数書いても有効なのは最後の1つだけ)。ENTRYPOINT はそのコンテナの「主たる実行ファイル」を固定したいときに使い、CMD と組み合わせると「ENTRYPOINTで実行ファイルを固定し、CMDで既定の引数を与える」形になります。どちらも、配列で書くexec形式(例:CMD ["python","app.py"])が推奨です。
COPYとADDの違い
ファイルをイメージへ取り込む命令にはCOPYとADDの2つがありますが、機能差を理解したうえで原則COPYを使うのがベストプラクティスです。
| 観点 | COPY | ADD |
|---|---|---|
| 基本動作 | 単純コピー | コピー+α |
| リモートURL | 不可 | 可 |
| tar自動展開 | しない | する |
| 推奨度 | 通常はこちら | 必要時のみ |
ADDはローカルのtarアーカイブを自動展開したり、リモートURLからファイルを取得したりできますが、この「暗黙の挙動」が予期せぬ結果を生みやすいため、単純にファイルを置きたい場合はCOPYを使います。tarを展開したい・URLから取得したいといった明確な理由があるときだけADDを使う、と覚えておくと安全です。
Dockerfileのベストプラクティス
動くDockerfileを書けるようになったら、次は「速く・軽く・安全に」を意識します。実務で効果の大きいポイントを挙げます。
- キャッシュが効く順序で書く:Dockerは命令ごとに結果をキャッシュします。変更頻度の低い命令(依存パッケージのインストールなど)を上に、変わりやすいアプリ本体のコピーを下に置くと、再ビルドが大幅に速くなります。前述の最小例で
requirements.txtを本体より先にコピーしているのはこのためです。 - レイヤを減らす:
RUNは実行ごとにレイヤを作るため、関連コマンドは&&でまとめ、行が長ければ\で折り返して1つのRUNにします。 - 軽量なベースイメージを選ぶ:
slimやalpine系を使うとサイズを抑えられます。ただしalpineはライブラリ互換で問題が出ることもあるため、用途に応じて選びます。 - マルチステージビルドを使う:ビルド用と実行用のステージを分け、最終イメージには成果物だけを残します(後述)。
- .dockerignoreを置く:
.gitやnode_modulesなどビルドに不要なファイルを除外し、コンテキスト転送とイメージを軽くします。 - 非rootユーザーで動かす:既定のrootのままだとリスクが高いため、
USERで専用ユーザーを指定します。 - タグを固定する:
latestは中身が変わると突然ビルドが壊れることがあるため、python:3.13-slimのようにバージョンを明示します。なお2026年時点ではNode.jsはv24がActive LTS、Pythonは3.13系が安定版です(v22や3.12も現役ですが、採用時は公式の最新サポート状況を確認してください)。 - 機密情報を埋め込まない:パスワードやトークンをDockerfileやENVに直書きせず、ビルド時のシークレット機能や外部のシークレット管理を使います。
- 静的解析で点検する:
hadolintなどのlinterでベストプラクティス違反を自動チェックすると、品質を一定に保てます。
マルチステージビルドは、複数の FROM でステージを分け、最終ステージに必要なものだけを COPY --from= で持ち込む手法です。Node.jsアプリのビルド成果物をNginxで配信する例を示します。
# ① ビルドステージ
FROM node:24 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ② 実行ステージ(成果物だけを軽量イメージへ)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
この構成なら、Node.jsの開発依存やソースは最終イメージに含まれず、配信に必要なファイルとNginxだけの軽量なイメージになります。実際、ビルドツールやnode_modulesを丸ごと含むイメージは数百MB規模にふくらみがちですが、マルチステージで成果物だけに絞ると最終イメージを大きく縮小でき、レジストリへの転送やデプロイの時間短縮にもつながります。VS Codeの開発環境としてDockerfileを使う発展的な構成はdevcontainerとは何か?Visual Studio Codeでの開発環境コンテナ化の仕組みを徹底解説も参考になります。
よくある質問(FAQ)
Dockerfileとは何ですか?
Dockerイメージを作るための命令を上から順に書いたテキストファイルです。コンテナそのものではなく、イメージを生成するための「設計図」にあたります。
Dockerfileには何を書くべきですか?
最低限、FROM(ベースイメージ)と、起動時の動作を決める CMD または ENTRYPOINT を書きます。実用的には、WORKDIR で作業場所を決め、COPY でファイルを入れ、RUN で必要なものをインストールする、という流れが基本形です。
DockerfileとDocker Composeの違いは何ですか?
役割が異なります。Dockerfileは1つのイメージのビルド手順を定義するもので、Docker Composeは複数のコンテナの構成や起動を docker-compose.yml という1ファイルでまとめて管理するものです。実際には「DockerfileでイメージをビルドしつつComposeで複数サービスを連携起動する」というように併用します。
RUNとCMDの違いは何ですか?
実行タイミングが違います。RUN はビルド時に実行されレイヤを作り(インストールなどに使う)、CMD はコンテナ起動時の既定コマンドで、起動時の引数で上書きできます。
Dockerfileが実行されるタイミングはいつですか?
docker build を実行したときに、各命令が上から順に処理されてイメージが作られます。ただし CMD や ENTRYPOINT はビルド時には実行されず、docker run でコンテナを起動したときに初めて動作します。