はじめに

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は、命令と引数の組み合わせで構成されます。基本的な形式は次のとおりです。

1
2
# コメント
命令 引数

命令は慣例として大文字で記述しますが、実際には大文字・小文字を区別しません。ただし、引数と区別しやすくするため、大文字で統一することが推奨されています。

最もシンプルなDockerfileの例を見てみましょう。

1
2
3
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
CMD ["curl", "--version"]

このDockerfileは以下の処理を定義しています。

  1. Ubuntu 22.04をベースイメージとして使用
  2. curlパッケージをインストール
  3. コンテナ起動時に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の最初に記述する必須の命令です。構築するイメージのベースとなるイメージを指定します。

1
FROM イメージ名[:タグ]

タグを省略するとlatestが使用されますが、再現性を確保するために明示的にタグを指定することが推奨されます。

1
2
3
4
5
# 良い例: タグを明示的に指定
FROM node:20-alpine

# 避けるべき例: タグを省略
FROM node

代表的なベースイメージとその特徴を以下に示します。

ベースイメージ 特徴 サイズ目安
ubuntu フル機能のLinux環境 約77MB
debian 安定性重視のLinux環境 約124MB
alpine 軽量なLinuxディストリビューション 約7MB
scratch 空のイメージ(バイナリ配布向け) 0MB

軽量なイメージを作成したい場合は、alpineベースのイメージ(例:node:20-alpinepython:3.12-alpine)を選択すると良いでしょう。

RUN - ビルド時のコマンド実行

RUN命令は、イメージのビルド時にコマンドを実行します。パッケージのインストールやファイルの作成など、イメージに変更を加える際に使用します。

1
2
3
4
5
# シェル形式
RUN コマンド

# exec形式
RUN ["実行ファイル", "引数1", "引数2"]

シェル形式は/bin/sh -cを介してコマンドを実行し、exec形式は直接実行ファイルを呼び出します。通常はシェル形式で記述することが多いです。

1
2
3
4
5
6
# パッケージのインストール
RUN apt-get update && apt-get install -y \
    curl \
    vim \
    git \
    && rm -rf /var/lib/apt/lists/*

RUN命令は1つにつき1つのレイヤーを作成するため、関連するコマンドは&&で連結してまとめることでイメージサイズを削減できます。また、キャッシュファイルを削除することで、さらにイメージを軽量化できます。

COPY - ファイルのコピー

COPY命令は、ビルドコンテキストからイメージ内にファイルやディレクトリをコピーします。

1
COPY [--chown=ユーザー:グループ] コピー元 コピー先

コピー元にはワイルドカードを使用できます。

1
2
3
4
5
6
7
8
# 単一ファイルのコピー
COPY package.json /app/

# 複数ファイルのコピー(ワイルドカード使用)
COPY package*.json /app/

# ディレクトリ全体のコピー
COPY src/ /app/src/

コピー先のパスが存在しない場合は自動的に作成されます。また、コピー先が/で終わる場合はディレクトリとして扱われます。

ADD - ファイルの追加(拡張機能付き)

ADD命令はCOPYと似ていますが、追加機能があります。

1
ADD コピー元 コピー先

ADDの追加機能は以下のとおりです。

  • リモートURLからファイルをダウンロード可能
  • tarアーカイブを自動的に展開
1
2
3
4
5
# tarファイルの自動展開
ADD myarchive.tar.gz /app/

# リモートURLからダウンロード
ADD https://example.com/file.txt /app/

ただし、単純なファイルコピーにはCOPYを使用することが推奨されています。ADDはその追加機能が必要な場合にのみ使用してください。

WORKDIR - 作業ディレクトリの設定

WORKDIR命令は、RUN、CMD、ENTRYPOINT、COPY、ADDなどの命令で使用される作業ディレクトリを設定します。

1
WORKDIR /パス

指定したディレクトリが存在しない場合は自動的に作成されます。

1
2
3
WORKDIR /app
COPY . .
RUN npm install

上記の例では、/appディレクトリを作成し、そこにファイルをコピーしてnpm installを実行しています。WORKDIRを設定することで、その後の命令で相対パスを使用できるようになります。

複数回WORKDIRを指定すると、相対パスの場合は前のWORKDIRからの相対位置になります。

1
2
3
4
WORKDIR /app
WORKDIR src
WORKDIR config
# 最終的な作業ディレクトリは /app/src/config

ENV - 環境変数の設定

ENV命令は、イメージ内で使用する環境変数を設定します。

1
ENV 変数名=[変数名=値 ...]

設定した環境変数は、その後の命令やコンテナ実行時に参照できます。

1
2
3
4
5
ENV NODE_ENV=production
ENV APP_HOME=/app PORT=3000

WORKDIR $APP_HOME
EXPOSE $PORT

環境変数は$変数名または${変数名}で参照できます。

EXPOSE - 公開ポートの宣言

EXPOSE命令は、コンテナが実行時にリッスンするポートを文書化するためのものです。

1
EXPOSE ポート番号[/プロトコル]
1
2
3
4
5
6
7
8
# TCPポート(デフォルト)
EXPOSE 80

# UDPポート
EXPOSE 53/udp

# 複数ポート
EXPOSE 80 443

EXPOSE命令はドキュメントとしての役割が主であり、実際にポートを公開するにはdocker run -pオプションが必要です。

CMD - デフォルトコマンドの指定

CMD命令は、コンテナ起動時に実行されるデフォルトのコマンドを指定します。

1
2
3
4
5
6
7
8
# exec形式(推奨)
CMD ["実行ファイル", "引数1", "引数2"]

# シェル形式
CMD コマンド 引数1 引数2

# ENTRYPOINTへのデフォルト引数
CMD ["引数1", "引数2"]

Dockerfile内にCMDは1つだけ記述できます。複数記述した場合は、最後のCMDのみが有効になります。

1
2
3
4
5
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]

CMDで指定したコマンドは、docker run実行時に引数を渡すと上書きされます。

1
2
3
4
5
# Dockerfileで定義したCMD(npm start)が実行される
docker run myapp

# docker run時の引数でCMDが上書きされる
docker run myapp npm test

ENTRYPOINT - エントリポイントの指定

ENTRYPOINT命令は、コンテナを実行可能ファイルとして実行する際のエントリポイントを設定します。

1
2
3
4
5
# exec形式(推奨)
ENTRYPOINT ["実行ファイル", "引数1", "引数2"]

# シェル形式
ENTRYPOINT コマンド 引数1 引数2

ENTRYPOINTとCMDの組み合わせにより、柔軟なコンテナ設計が可能です。

1
2
3
FROM alpine
ENTRYPOINT ["ping"]
CMD ["localhost"]

この例では以下のように動作します。

1
2
3
4
5
# デフォルト: ping localhost
docker run myping

# 引数を指定: ping google.com
docker run myping google.com

CMDとENTRYPOINTの違い

CMDとENTRYPOINTは似た役割を持ちますが、重要な違いがあります。

特徴 CMD ENTRYPOINT
docker run引数による上書き 完全に上書きされる 引数として追加される
主な用途 デフォルトコマンドの指定 固定の実行コマンド指定
使い分け 変更される可能性があるコマンド 常に実行したいコマンド

両者の組み合わせパターンを以下に示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# パターン1: CMDのみ
# docker run時に完全に上書き可能
CMD ["npm", "start"]

# パターン2: ENTRYPOINTのみ
# docker run時の引数が追加される
ENTRYPOINT ["npm"]

# パターン3: 組み合わせ(推奨パターン)
# ENTRYPOINTは固定、CMDはデフォルト引数
ENTRYPOINT ["npm"]
CMD ["start"]

パターン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

ビルドコンテキストが大きいと、デーモンへの転送に時間がかかりビルドが遅くなります。

1
2
3
4
5
# カレントディレクトリをビルドコンテキストとして指定
docker build -t myapp .

# 別のディレクトリをビルドコンテキストとして指定
docker build -t myapp ./project

ビルドコンテキスト内のファイルは、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を適切に設定することで、以下のメリットがあります。

  1. ビルド時間の短縮: 転送するファイルが減り、ビルドが高速化
  2. イメージサイズの削減: 不要なファイルがイメージに含まれない
  3. セキュリティ向上: 機密情報(.envファイルなど)がイメージに含まれない
  4. キャッシュ効率の向上: 変更のないファイルによるキャッシュ無効化を防止

.dockerignoreの構文

.dockerignoreでは以下のパターンが使用できます。

パターン 説明
# コメント
* 任意の文字列にマッチ
? 任意の1文字にマッチ
** 任意のディレクトリにマッチ
! 除外パターンの例外(含める)
# 例: すべてのMarkdownファイルを除外するが、README.mdは含める
*.md
!README.md

# 例: すべてのサブディレクトリ内の.txtファイルを除外
**/*.txt

docker buildコマンドの使い方

基本的なビルドコマンド

docker buildコマンドでDockerfileからイメージをビルドします。

1
docker build [オプション] ビルドコンテキスト

最も基本的な使用例は以下のとおりです。

1
2
3
4
5
6
7
8
# カレントディレクトリのDockerfileを使用してビルド
docker build .

# イメージ名とタグを指定してビルド
docker build -t myapp:1.0 .

# 別のDockerfileを指定してビルド
docker build -f Dockerfile.dev -t myapp:dev .

主要なオプション

docker buildでよく使用するオプションを紹介します。

オプション 説明
-t, --tag イメージ名とタグを指定
-f, --file 使用するDockerfileを指定
--no-cache キャッシュを使用せずビルド
--build-arg ビルド時の変数を指定
--target マルチステージビルドのターゲット指定
--platform ビルド対象のプラットフォーム指定
1
2
3
4
5
6
7
8
# キャッシュを使用せずにビルド
docker build --no-cache -t myapp .

# ビルド引数を渡す
docker build --build-arg VERSION=1.0.0 -t myapp .

# 複数タグを付与
docker build -t myapp:1.0 -t myapp:latest .

ビルドプロセスの理解

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はビルド時に各命令の結果をレイヤーとしてキャッシュします。

1
2
3
4
5
6
FROM node:20-alpine       # レイヤー1(ベースイメージ)
WORKDIR /app              # レイヤー2
COPY package*.json ./     # レイヤー3
RUN npm install           # レイヤー4
COPY . .                  # レイヤー5
CMD ["npm", "start"]      # レイヤー6(メタデータのみ)

あるレイヤーに変更があると、それ以降のすべてのレイヤーは再ビルドされます。そのため、変更頻度の低い命令を先に、変更頻度の高い命令を後に配置することで、キャッシュを効率的に活用できます。

1
2
3
4
5
6
7
8
# 良い例: 依存関係定義ファイルを先にコピー
COPY package*.json ./
RUN npm install
COPY . .

# 悪い例: すべてのファイルを先にコピー
COPY . .
RUN npm install

良い例では、ソースコードを変更してもpackage.jsonが変わらなければ、npm installのキャッシュが再利用されます。

サンプルDockerfileで学ぶ

Node.jsアプリケーション

Node.jsアプリケーション用のDockerfileの例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ベースイメージ
FROM node:20-alpine

# 作業ディレクトリの設定
WORKDIR /app

# 依存関係ファイルをコピー
COPY package*.json ./

# 依存関係のインストール
RUN npm ci --only=production

# ソースコードをコピー
COPY . .

# アプリケーションのポート
EXPOSE 3000

# 環境変数の設定
ENV NODE_ENV=production

# アプリケーションの起動
CMD ["node", "server.js"]

このDockerfileのポイントは以下のとおりです。

  1. alpineベースで軽量なイメージを作成
  2. package.jsonを先にコピーして依存関係をインストール(キャッシュ効率化)
  3. npm ciで再現性の高いインストールを実行
  4. 本番用の環境変数を設定

Pythonアプリケーション

Pythonアプリケーション用のDockerfileの例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# ベースイメージ
FROM python:3.12-slim

# 作業ディレクトリの設定
WORKDIR /app

# 依存関係ファイルをコピー
COPY requirements.txt .

# 依存関係のインストール
RUN pip install --no-cache-dir -r requirements.txt

# ソースコードをコピー
COPY . .

# アプリケーションのポート
EXPOSE 8000

# アプリケーションの起動
CMD ["python", "app.py"]

--no-cache-dirオプションを使用することで、pipのキャッシュファイルを作成せず、イメージサイズを削減しています。

静的Webサイト

Nginxで静的サイトを配信するDockerfileの例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ベースイメージ
FROM nginx:alpine

# 静的ファイルをコピー
COPY ./public /usr/share/nginx/html

# カスタム設定ファイルをコピー(必要に応じて)
# COPY nginx.conf /etc/nginx/nginx.conf

# ポート80を公開
EXPOSE 80

# Nginxはデフォルトでフォアグラウンドで起動するためCMD不要

実践演習: シンプルなWebサーバー

実際にDockerfileを作成してビルドしてみましょう。

まず、作業ディレクトリを作成してファイルを準備します。

1
2
mkdir mywebapp
cd mywebapp

index.htmlを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Dockerで作成したWebサイト</title>
</head>
<body>
    <h1>Hello, Docker!</h1>
    <p>Dockerfileから作成したイメージで動いています。</p>
</body>
</html>

Dockerfileを作成します。

1
2
3
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80

イメージをビルドして実行します。

1
2
3
4
5
# イメージのビルド
docker build -t mywebapp:1.0 .

# コンテナの実行
docker run -d -p 8080:80 --name mywebapp mywebapp:1.0

ブラウザでhttp://localhost:8080にアクセスすると、作成したWebページが表示されます。

確認が終わったらコンテナを停止・削除します。

1
2
docker stop mywebapp
docker rm mywebapp

まとめ

本記事では、Dockerfileの基本構文とdocker buildコマンドの使い方について解説しました。重要なポイントを振り返ります。

Dockerfileの基本命令として、以下を学びました。

  • FROM: ベースイメージの指定(Dockerfileの先頭に必須)
  • RUN: ビルド時のコマンド実行(パッケージインストールなど)
  • COPY/ADD: ファイルのコピー(通常はCOPYを推奨)
  • WORKDIR: 作業ディレクトリの設定
  • ENV: 環境変数の設定
  • EXPOSE: 公開ポートの宣言(ドキュメント目的)
  • CMD: デフォルトコマンドの指定
  • ENTRYPOINT: 固定の実行コマンド指定

ビルドに関しては以下の知識が重要です。

  • ビルドコンテキストはDockerデーモンに送信されるファイル群
  • .dockerignoreで不要なファイルを除外してビルドを効率化
  • レイヤーキャッシュを活用して再ビルドを高速化
  • 変更頻度の低い命令を先に記述することでキャッシュ効率が向上

次のステップとして、マルチステージビルドやセキュリティを考慮したDockerfileの書き方を学ぶことで、本番環境でも使える効率的で安全なイメージを作成できるようになります。

参考リンク