はじめに
Docker Hubからイメージを取得してコンテナを実行することはできるようになった。しかし、自分のアプリケーションを動かすには、独自のイメージを作成する必要がある。そんなとき活躍するのがDockerfileです。
本記事では、Dockerfileの基本構文からdocker buildコマンドの使い方、ビルドコンテキストや.dockerignoreの活用方法まで、独自イメージ作成に必要な知識を体系的に解説します。この記事を読み終えると、以下のことができるようになります。
- Dockerfileの役割と基本構造を理解できる
- FROM/RUN/COPY/WORKDIR/CMD/ENTRYPOINTなどの主要命令を使い分けられる
- docker buildコマンドでイメージをビルドできる
- ビルドコンテキストと.dockerignoreを適切に設定できる
前提として、Dockerの基本コマンドを習得済みであることを想定しています。まだDockerの基本操作に慣れていない場合は、Docker基本コマンド完全ガイドを参照してください。
Dockerfileとは
Dockerfileは、Dockerイメージを自動的に構築するための設計図です。テキストファイル形式で記述され、イメージに含めるファイルや実行するコマンド、環境設定などを命令(Instruction)として定義します。
なぜDockerfileが必要なのか
手動でコンテナを構築してイメージを作成することも可能ですが、以下の理由からDockerfileを使用することが推奨されます。
| 観点 | 手動構築 | Dockerfile |
|---|---|---|
| 再現性 | 手順を覚えておく必要がある | ファイルとして手順が残る |
| 自動化 | 毎回手動で実行 | CI/CDパイプラインで自動ビルド可能 |
| バージョン管理 | 困難 | Gitで履歴管理可能 |
| チーム共有 | 口頭や文書で伝える必要がある | ファイルを共有するだけ |
Dockerfileを活用することで、誰でも同じ環境を再現でき、チーム開発やCI/CD環境での運用が容易になります。
Dockerfileの基本構造
Dockerfileは、命令と引数の組み合わせで構成されます。基本的な形式は次のとおりです。
|
|
命令は慣例として大文字で記述しますが、実際には大文字・小文字を区別しません。ただし、引数と区別しやすくするため、大文字で統一することが推奨されています。
最もシンプルなDockerfileの例を見てみましょう。
|
|
このDockerfileは以下の処理を定義しています。
- Ubuntu 22.04をベースイメージとして使用
- curlパッケージをインストール
- コンテナ起動時にcurlのバージョンを表示
Dockerfileの基本構文
Dockerfileには多くの命令がありますが、まずは頻繁に使用する主要な命令を押さえておきましょう。
block-beta
columns 1
block:dockerfile["Dockerfile"]
FROM["FROM : ベースイメージの指定"]
WORKDIR["WORKDIR : 作業ディレクトリの設定"]
COPY["COPY / ADD : ファイルのコピー"]
RUN["RUN : ビルド時のコマンド実行"]
ENV["ENV : 環境変数の設定"]
EXPOSE["EXPOSE : 公開ポートの宣言"]
CMD["CMD : コンテナ起動時のデフォルトコマンド"]
ENTRYPOINT["ENTRYPOINT : コンテナ起動時の実行ファイル指定"]
endそれぞれの命令について詳しく見ていきます。
主要命令の解説
FROM - ベースイメージの指定
FROM命令は、Dockerfileの最初に記述する必須の命令です。構築するイメージのベースとなるイメージを指定します。
|
|
タグを省略するとlatestが使用されますが、再現性を確保するために明示的にタグを指定することが推奨されます。
|
|
代表的なベースイメージとその特徴を以下に示します。
| ベースイメージ | 特徴 | サイズ目安 |
|---|---|---|
| ubuntu | フル機能のLinux環境 | 約77MB |
| debian | 安定性重視のLinux環境 | 約124MB |
| alpine | 軽量なLinuxディストリビューション | 約7MB |
| scratch | 空のイメージ(バイナリ配布向け) | 0MB |
軽量なイメージを作成したい場合は、alpineベースのイメージ(例:node:20-alpine、python:3.12-alpine)を選択すると良いでしょう。
RUN - ビルド時のコマンド実行
RUN命令は、イメージのビルド時にコマンドを実行します。パッケージのインストールやファイルの作成など、イメージに変更を加える際に使用します。
|
|
シェル形式は/bin/sh -cを介してコマンドを実行し、exec形式は直接実行ファイルを呼び出します。通常はシェル形式で記述することが多いです。
|
|
RUN命令は1つにつき1つのレイヤーを作成するため、関連するコマンドは&&で連結してまとめることでイメージサイズを削減できます。また、キャッシュファイルを削除することで、さらにイメージを軽量化できます。
COPY - ファイルのコピー
COPY命令は、ビルドコンテキストからイメージ内にファイルやディレクトリをコピーします。
|
|
コピー元にはワイルドカードを使用できます。
|
|
コピー先のパスが存在しない場合は自動的に作成されます。また、コピー先が/で終わる場合はディレクトリとして扱われます。
ADD - ファイルの追加(拡張機能付き)
ADD命令はCOPYと似ていますが、追加機能があります。
|
|
ADDの追加機能は以下のとおりです。
- リモートURLからファイルをダウンロード可能
- tarアーカイブを自動的に展開
|
|
ただし、単純なファイルコピーにはCOPYを使用することが推奨されています。ADDはその追加機能が必要な場合にのみ使用してください。
WORKDIR - 作業ディレクトリの設定
WORKDIR命令は、RUN、CMD、ENTRYPOINT、COPY、ADDなどの命令で使用される作業ディレクトリを設定します。
|
|
指定したディレクトリが存在しない場合は自動的に作成されます。
|
|
上記の例では、/appディレクトリを作成し、そこにファイルをコピーしてnpm installを実行しています。WORKDIRを設定することで、その後の命令で相対パスを使用できるようになります。
複数回WORKDIRを指定すると、相対パスの場合は前のWORKDIRからの相対位置になります。
|
|
ENV - 環境変数の設定
ENV命令は、イメージ内で使用する環境変数を設定します。
|
|
設定した環境変数は、その後の命令やコンテナ実行時に参照できます。
|
|
環境変数は$変数名または${変数名}で参照できます。
EXPOSE - 公開ポートの宣言
EXPOSE命令は、コンテナが実行時にリッスンするポートを文書化するためのものです。
|
|
|
|
EXPOSE命令はドキュメントとしての役割が主であり、実際にポートを公開するにはdocker run -pオプションが必要です。
CMD - デフォルトコマンドの指定
CMD命令は、コンテナ起動時に実行されるデフォルトのコマンドを指定します。
|
|
Dockerfile内にCMDは1つだけ記述できます。複数記述した場合は、最後のCMDのみが有効になります。
|
|
CMDで指定したコマンドは、docker run実行時に引数を渡すと上書きされます。
|
|
ENTRYPOINT - エントリポイントの指定
ENTRYPOINT命令は、コンテナを実行可能ファイルとして実行する際のエントリポイントを設定します。
|
|
ENTRYPOINTとCMDの組み合わせにより、柔軟なコンテナ設計が可能です。
|
|
この例では以下のように動作します。
|
|
CMDとENTRYPOINTの違い
CMDとENTRYPOINTは似た役割を持ちますが、重要な違いがあります。
| 特徴 | CMD | ENTRYPOINT |
|---|---|---|
| docker run引数による上書き | 完全に上書きされる | 引数として追加される |
| 主な用途 | デフォルトコマンドの指定 | 固定の実行コマンド指定 |
| 使い分け | 変更される可能性があるコマンド | 常に実行したいコマンド |
両者の組み合わせパターンを以下に示します。
|
|
パターン3の組み合わせを使用すると、デフォルトではnpm startが実行され、docker run myapp testのように引数を渡すとnpm testが実行されます。
ビルドコンテキストと.dockerignore
ビルドコンテキストとは
docker buildコマンドを実行すると、指定したディレクトリ(ビルドコンテキスト)の内容がDockerデーモンに送信されます。
flowchart LR
subgraph Local["ビルドコンテキスト (ローカルPC)"]
project["project/"]
dockerfile["Dockerfile"]
src["src/"]
node_modules["node_modules/"]
git[".git/"]
end
subgraph DockerDaemon["Dockerデーモン"]
build["イメージ\nビルド"]
end
Local --> DockerDaemonビルドコンテキストが大きいと、デーモンへの転送に時間がかかりビルドが遅くなります。
|
|
ビルドコンテキスト内のファイルは、COPYやADD命令で参照できます。ビルドコンテキスト外のファイルは参照できないため、必要なファイルはすべてビルドコンテキスト内に配置してください。
.dockerignoreの活用
.dockerignoreファイルを使用すると、ビルドコンテキストから特定のファイルやディレクトリを除外できます。.gitignoreと同様の構文で記述します。
# .dockerignore
# バージョン管理
.git
.gitignore
# 依存関係(イメージ内で再インストール)
node_modules
vendor
# ビルド成果物
dist
build
*.log
# 開発用ファイル
.env.local
.env.development
*.md
!README.md
# IDE設定
.vscode
.idea
# Docker関連
Dockerfile*
docker-compose*.yml
.dockerignore
.dockerignoreを適切に設定することで、以下のメリットがあります。
- ビルド時間の短縮: 転送するファイルが減り、ビルドが高速化
- イメージサイズの削減: 不要なファイルがイメージに含まれない
- セキュリティ向上: 機密情報(.envファイルなど)がイメージに含まれない
- キャッシュ効率の向上: 変更のないファイルによるキャッシュ無効化を防止
.dockerignoreの構文
.dockerignoreでは以下のパターンが使用できます。
| パターン | 説明 |
|---|---|
# |
コメント |
* |
任意の文字列にマッチ |
? |
任意の1文字にマッチ |
** |
任意のディレクトリにマッチ |
! |
除外パターンの例外(含める) |
# 例: すべてのMarkdownファイルを除外するが、README.mdは含める
*.md
!README.md
# 例: すべてのサブディレクトリ内の.txtファイルを除外
**/*.txt
docker buildコマンドの使い方
基本的なビルドコマンド
docker buildコマンドでDockerfileからイメージをビルドします。
|
|
最も基本的な使用例は以下のとおりです。
|
|
主要なオプション
docker buildでよく使用するオプションを紹介します。
| オプション | 説明 |
|---|---|
-t, --tag |
イメージ名とタグを指定 |
-f, --file |
使用するDockerfileを指定 |
--no-cache |
キャッシュを使用せずビルド |
--build-arg |
ビルド時の変数を指定 |
--target |
マルチステージビルドのターゲット指定 |
--platform |
ビルド対象のプラットフォーム指定 |
|
|
ビルドプロセスの理解
docker buildを実行すると、Dockerfileの各命令が順番に処理されます。
ビルドプロセスの流れ
1. ビルドコンテキストをDockerデーモンに送信
└─→ "Sending build context to Docker daemon"
2. 各命令を順次実行
├─→ Step 1/5 : FROM node:20-alpine
├─→ Step 2/5 : WORKDIR /app
├─→ Step 3/5 : COPY package*.json ./
├─→ Step 4/5 : RUN npm install
└─→ Step 5/5 : CMD ["npm", "start"]
3. イメージIDまたはタグを出力
└─→ "Successfully built abc123..."
各ステップでは中間イメージ(レイヤー)が作成され、これらがキャッシュとして保存されます。2回目以降のビルドでは、変更のないステップはキャッシュが再利用され、ビルド時間が短縮されます。
レイヤーキャッシュの仕組み
Dockerはビルド時に各命令の結果をレイヤーとしてキャッシュします。
|
|
あるレイヤーに変更があると、それ以降のすべてのレイヤーは再ビルドされます。そのため、変更頻度の低い命令を先に、変更頻度の高い命令を後に配置することで、キャッシュを効率的に活用できます。
|
|
良い例では、ソースコードを変更してもpackage.jsonが変わらなければ、npm installのキャッシュが再利用されます。
サンプルDockerfileで学ぶ
Node.jsアプリケーション
Node.jsアプリケーション用のDockerfileの例です。
|
|
このDockerfileのポイントは以下のとおりです。
- alpineベースで軽量なイメージを作成
- package.jsonを先にコピーして依存関係をインストール(キャッシュ効率化)
npm ciで再現性の高いインストールを実行- 本番用の環境変数を設定
Pythonアプリケーション
Pythonアプリケーション用のDockerfileの例です。
|
|
--no-cache-dirオプションを使用することで、pipのキャッシュファイルを作成せず、イメージサイズを削減しています。
静的Webサイト
Nginxで静的サイトを配信するDockerfileの例です。
|
|
実践演習: シンプルなWebサーバー
実際にDockerfileを作成してビルドしてみましょう。
まず、作業ディレクトリを作成してファイルを準備します。
|
|
index.htmlを作成します。
|
|
Dockerfileを作成します。
|
|
イメージをビルドして実行します。
|
|
ブラウザでhttp://localhost:8080にアクセスすると、作成したWebページが表示されます。
確認が終わったらコンテナを停止・削除します。
|
|
まとめ
本記事では、Dockerfileの基本構文とdocker buildコマンドの使い方について解説しました。重要なポイントを振り返ります。
Dockerfileの基本命令として、以下を学びました。
- FROM: ベースイメージの指定(Dockerfileの先頭に必須)
- RUN: ビルド時のコマンド実行(パッケージインストールなど)
- COPY/ADD: ファイルのコピー(通常はCOPYを推奨)
- WORKDIR: 作業ディレクトリの設定
- ENV: 環境変数の設定
- EXPOSE: 公開ポートの宣言(ドキュメント目的)
- CMD: デフォルトコマンドの指定
- ENTRYPOINT: 固定の実行コマンド指定
ビルドに関しては以下の知識が重要です。
- ビルドコンテキストはDockerデーモンに送信されるファイル群
- .dockerignoreで不要なファイルを除外してビルドを効率化
- レイヤーキャッシュを活用して再ビルドを高速化
- 変更頻度の低い命令を先に記述することでキャッシュ効率が向上
次のステップとして、マルチステージビルドやセキュリティを考慮したDockerfileの書き方を学ぶことで、本番環境でも使える効率的で安全なイメージを作成できるようになります。