そんな悩みを持つ方は多いと思います。Docker自体は使えるようになったけれど、自分のアプリを独自イメージとしてパッケージするとなると、急に手が止まってしまう。
この記事では、Dockerfileの基本構文から実践的な書き方まで、Ubuntu 24.04 LTS / Rocky Linux 9で動作確認した手順を交えながら解説します。
よくあるエラーの対処法やベストプラクティスも網羅していますので、初めてDockerfileを書く方から「なんとなく書いてきたけど最適化したい」という方まで、そのまま実務に使えます。
この記事のポイント
・FROM・RUN・COPY・CMD の4命令を押さえれば最初のDockerfileは書ける
・RUN命令は&&でつなぎ1レイヤーにまとめるとイメージが小さくなる
・.dockerignore でビルドコンテキストを絞るのが速度改善の第一歩
・エラーの9割は「ファイルパスの指定ミス」か「キャッシュの古さ」が原因
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
Dockerfileとは何か
Dockerfileとは、Dockerイメージを自動的に作成するための「設計書」です。通常、Dockerコンテナを起動するには既存のイメージ(ubuntu、nginx、pythonなど)を使います。ただし、自分のアプリケーションを動かすには「必要なパッケージをインストールし、アプリのコードをコピーし、起動コマンドを設定した」状態のイメージが必要です。
Dockerfileにはその手順を1行ずつ命令として記述します。
docker build コマンドを実行すると、Dockerはこのファイルを上から順番に読み込んでイメージを作成します。Dockerfileを使うメリットは次の3点です。
・再現性:誰がどの環境で
docker build しても同じイメージができる・自動化:CI/CDパイプラインに組み込めば、コード変更のたびに自動でイメージを更新できる
・可搬性:Dockerfileと
.dockerignore さえあれば、アプリ環境をどこでも再現できるDockerfileはプロジェクトのルートディレクトリに
Dockerfile(拡張子なし)という名前で保存するのが慣例です。Dockerfileの基本構文と主要命令
Dockerfileの各行は「命令(INSTRUCTION)」と「引数(arguments)」の形式で書きます。# 命令 引数 INSTRUCTION arguments
# から始めます。以下に主要命令を解説します。1. FROM — ベースイメージの指定
すべてのDockerfileはFROM 命令から始まります。どの既存イメージを土台にするかを指定します。FROM ubuntu:24.04 # または FROM python:3.12-slim # または FROM node:20-alpine
:タグ でバージョンを固定するのが鉄則です。:latest は更新のたびに内容が変わるため、本番環境では使いません。2. RUN — コマンドの実行
イメージビルド時にシェルコマンドを実行します。パッケージのインストール、設定ファイルの配置などに使います。# NG:RUNを3行に分けると3つのレイヤーができる RUN apt-get update RUN apt-get install -y python3 RUN apt-get clean # OK:&&でつなぎ1つのRUNにまとめる(レイヤーを削減) RUN apt-get update \ && apt-get install -y python3 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*
RUN 命令1行ごとにレイヤー(層)が作られます。レイヤーが多いほどイメージサイズは大きくなります。後述のレイヤー最適化で詳しく説明します。3. COPY — ファイルのコピー
PCのファイルをイメージ内にコピーします。アプリのソースコードや設定ファイルをイメージに組み込む際に使います。# ローカルの app.py をイメージ内の /app/app.py にコピー COPY app.py /app/app.py # カレントディレクトリ全体を /app にコピー COPY . /app/
ADD という命令も似た用途ですが、URLからのダウンロードやtar展開など余計な挙動があります。ファイルコピー専用には COPY を使うのがベストプラクティスです。4. WORKDIR — 作業ディレクトリの設定
以降の命令(RUN・COPY・CMD等)の作業ディレクトリを指定します。ディレクトリが存在しない場合は自動作成されます。WORKDIR /app # 以降の COPY や RUN は /app を起点に実行される COPY . . RUN pip install -r requirements.txt
5. ENV — 環境変数の設定
コンテナ内で使う環境変数を設定します。ビルド時・実行時の両方で参照できます。ENV APP_ENV=production ENV PORT=8080
6. EXPOSE — ポートの宣言
コンテナが使用するポートを宣言します。実際のポート公開はdocker run -p で行いますが、EXPOSE は「このコンテナはこのポートを使う」という意図をドキュメントとして残す役割があります。EXPOSE 8080
7. CMD / ENTRYPOINT — コンテナ起動時のコマンド
コンテナが起動したときに実行するデフォルトコマンドを指定します。# exec 形式(推奨) CMD ["python3", "app.py"] # shell 形式(シェル展開が必要な場合のみ) CMD python3 app.py
exec形式(配列形式)は、PID 1 として指定したプロセスが直接起動します。シグナル処理が確実に届くため、本番では exec 形式を使います。ENTRYPOINT は常に実行される固定コマンド、CMD はそのデフォルト引数、という使い分けが一般的です。実践:シンプルなWebアプリのDockerfileを書く
実際に Python + Flask で作ったシンプルなWebアプリをDocker化する例で手順を確認します。1. アプリの構成
myapp/ ├── Dockerfile ├── .dockerignore ├── requirements.txt └── app.py
2. app.py(サンプルアプリ)
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello from Docker!' if __name__ == '__main__': app.run(host='0.0.0.0', port=8080)
3. requirements.txt
flask==3.0.3
4. Dockerfile
# ベースイメージ:Python 3.12 スリム版(Debian Bookworm ベース) FROM python:3.12-slim # 作業ディレクトリを /app に設定 WORKDIR /app # 依存ライブラリをインストール(キャッシュ活用のため先にコピー) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # アプリのソースコードをコピー COPY app.py . # コンテナが使用するポートを宣言 EXPOSE 8080 # コンテナ起動時のコマンド CMD ["python3", "app.py"]
5. .dockerignore の作成
ビルドコンテキスト(docker build が読み込むファイル群)から不要ファイルを除外します。__pycache__ *.pyc *.pyo .git .env *.log
6. ビルドと実行
# イメージをビルド(-t でタグ名を付ける、最後の . がビルドコンテキスト) docker build -t myapp:1.0 . # 実行確認(-p でホストのポートとコンテナのポートをマッピング) docker run --rm -p 8080:8080 myapp:1.0
[+] Building 12.3s (9/9) FINISHED => [internal] load build definition from Dockerfile => [internal] load .dockerignore => [internal] load metadata for docker.io/library/python:3.12-slim => [1/4] FROM docker.io/library/python:3.12-slim => [2/4] WORKDIR /app => [3/4] COPY requirements.txt . => [4/4] RUN pip install --no-cache-dir -r requirements.txt => exporting to image => => writing image sha256:a3f7... => => naming to docker.io/library/myapp:1.0
http://localhost:8080/ を開くと「Hello from Docker!」と表示されれば成功です。Dockerfileのベストプラクティス(レイヤー最適化・キャッシュ活用)
1. レイヤーを最小化する
Dockerイメージはレイヤーの積み重ねで構成されます。RUN 命令1行ごとに1レイヤーが作成されます。中間レイヤーに削除したファイルが残るため、インストールとクリーンアップは同一 RUN 命令内で行うのが鉄則です。# NG:クリーンアップしても前のレイヤーにキャッシュが残る RUN apt-get update && apt-get install -y curl RUN apt-get clean && rm -rf /var/lib/apt/lists/* # OK:同一 RUN 命令内でインストール&クリーンアップ RUN apt-get update \ && apt-get install -y curl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*
2. キャッシュを有効活用するための順序
Docker はビルドの各ステップをキャッシュします。変更があったステップ以降はキャッシュが無効になります。「変更頻度が低いもの(依存ライブラリ)を先に、変更頻度が高いもの(ソースコード)を後に」という順序が基本です。# 推奨順序:依存ファイルを先にコピーして pip install COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 依存が変わらない限り上記はキャッシュが使われる # ソースコードは最後にコピー COPY app.py .
requirements.txt が変わらなければ pip install はキャッシュが効くため、ビルドが大幅に速くなります。3. 軽量ベースイメージを選ぶ
| タグ例 | 特徴 | 推奨用途 |
|---|---|---|
python:3.12 |
フルサイズ(約1GB) | 開発・デバッグ |
python:3.12-slim |
Debianベース軽量版(約130MB) | 本番アプリ |
python:3.12-alpine |
Alpine Linuxベース最小版(約50MB) | サイズ優先・musl libc対応アプリ |
slim からはじめるのが無難です。4. 非rootユーザーで実行する
コンテナをroot権限で実行すると、脆弱性を突かれたときのリスクが高まります。# 専用ユーザーを作成してアプリを実行 RUN useradd -m appuser USER appuser CMD ["python3", "app.py"]
5. マルチステージビルドで最終イメージを軽量化する
ビルド時に必要なツール(コンパイラ、テストツール等)を最終イメージから除外するパターンです。Go言語のアプリを例に示します。# ステージ1:ビルド FROM golang:1.22 AS builder WORKDIR /src COPY . . RUN go build -o app . # ステージ2:実行(最小イメージ) FROM gcr.io/distroless/base-debian12 COPY --from=builder /src/app /app CMD ["/app"]
また、Linuxサーバーのポート確認コマンド(ss/lsof)で
EXPOSE したポートが実際にリッスンしているか確認するのも、動作確認時の基本操作です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Dockerfileの書き方からコンテナ運用の実務スキルを短期間で習得できる講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
よくあるエラーと対処法
エラー1:「COPY failed: file not found」
# エラーメッセージ例 COPY failed: file not found in build context or excluded by .dockerignore: stat requirements.txt
requirements.txt のパスが間違っているか、.dockerignore で除外されています。対処:
docker build を実行するディレクトリ(ビルドコンテキスト)と、COPY に指定したパスの起点が一致しているか確認してください。# Dockerfile があるディレクトリで実行する(最後の . = ビルドコンテキスト) cd myapp/ docker build -t myapp:1.0 .
エラー2:「RUN: exit code 100」(apt-get が失敗する)
# エラーメッセージ例 #6 4.321 E: Unable to locate package xxx
対処:
apt-get update と apt-get install を必ず同一 RUN 内に書くこと。また --no-cache オプション付きでリビルドしてください。# キャッシュを無効化してビルド docker build --no-cache -t myapp:1.0 .
エラー3:コンテナは起動するがアプリに接続できない
原因1:-p オプションでポートマッピングをしていない。原因2:アプリが
127.0.0.1 でリッスンしており、コンテナ外からアクセスできない。対処:
# -p ホストポート:コンテナポート を正しく指定する docker run --rm -p 8080:8080 myapp:1.0 # アプリ側も 0.0.0.0 でリッスンさせる(Flask の例) app.run(host='0.0.0.0', port=8080)
docker exec でコンテナに入って確認するのが近道です。# 起動中のコンテナへ入る docker exec -it <コンテナID> /bin/bash # コンテナ内でポートリッスン状態を確認 ss -tlnp
エラー4:「permission denied」でアプリが起動しない
原因:USER 命令で切り替えた非rootユーザーに、ファイルの読み取り権限がない。対処:
COPY 後に権限を設定するか、所有者を変更してください。# COPY と同時に所有者を指定する(Docker 17.09+) COPY --chown=appuser:appuser . /app/
本記事のまとめ
Dockerfileの主要命令と実践的な書き方を解説しました。| やりたいこと | 命令・コマンド |
|---|---|
| ベースイメージを指定する | FROM python:3.12-slim |
| パッケージをインストールする | RUN apt-get update && apt-get install -y xxx && apt-get clean && rm -rf /var/lib/apt/lists/* |
| ファイルをイメージに追加する | COPY ローカルパス コンテナパス |
| 作業ディレクトリを設定する | WORKDIR /app |
| ポートを宣言する | EXPOSE 8080 |
| 起動コマンドを設定する | CMD ["python3", "app.py"] |
| イメージをビルドする | docker build -t myapp:1.0 . |
| キャッシュなしでビルドする | docker build --no-cache -t myapp:1.0 . |
| コンテナを起動してポートを開ける | docker run --rm -p 8080:8080 myapp:1.0 |
Linuxサーバーの基礎からDockerの実務活用まで、体系的に学びたい方は以下のリンクもあわせてご参照ください。
・Linux ポート確認の全コマンド
・mount コマンドの使い方
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、DockerfileからDockerの実践活用まで体系的に学べる講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:DockerfileのマルチステージビルドとCompose設計|本番イメージの軽量化・セキュリティ・環境分離の実践手順
- 前のページへ:Docker Compose入門|複数コンテナでWordPress環境を構築するハンズオン
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます