T-CREATOR

Dockerfile 命令チートシート:FROM/ARG/ENV/COPY/ADD/RUN/ENTRYPOINT の最適書式

Dockerfile 命令チートシート:FROM/ARG/ENV/COPY/ADD/RUN/ENTRYPOINT の最適書式

Dockerfile を書く際、どの命令をどのような順序で、どのように記述すればよいのか迷ったことはありませんか。

本記事では、Dockerfile で最も頻繁に使用される 7 つの基本命令(FROM、ARG、ENV、COPY、ADD、RUN、ENTRYPOINT)について、それぞれの役割、最適な書式、ベストプラクティスを体系的に解説します。初心者の方でもすぐに実践できるよう、具体例を交えながら丁寧にご説明しますので、ぜひ最後までお読みください。

Dockerfile 命令早見表

まず、各命令の特徴を一覧で確認しましょう。

#命令役割記述例主な用途
1FROMベースイメージ指定FROM node:18-alpineイメージのベースを設定
2ARGビルド時変数定義ARG NODE_VERSION=18ビルド時のパラメータ化
3ENV環境変数設定ENV NODE_ENV=productionコンテナ実行時の環境変数
4COPYファイルコピーCOPY package.json .​/​ローカルファイルをコピー
5ADDファイル追加(高機能)ADD archive.tar.gz ​/​app​/​URL 取得や自動展開
6RUNコマンド実行RUN yarn install --frozen-lockfileパッケージインストールなど
7ENTRYPOINT起動コマンド設定ENTRYPOINT ["node", "server.js"]コンテナ起動時の実行内容

この表を参照しながら、各命令の詳細を見ていきましょう。

背景

Docker はコンテナ型仮想化技術の代表格として、現代のアプリケーション開発に欠かせない存在となっています。

開発環境から本番環境まで、一貫した実行環境を提供できる Docker の利便性は、多くの開発チームに支持されていますね。その Docker において、コンテナイメージの設計図となるのが Dockerfile です。Dockerfile は、ベースイメージの選定から、必要なファイルのコピー、パッケージのインストール、起動コマンドの設定まで、すべてをテキストベースで記述できる仕組みです。

しかし、Dockerfile には 30 以上の命令が存在し、すべてを覚えるのは大変でしょう。実際の開発現場では、そのうちの 7 つの基本命令を適切に使いこなせば、ほとんどのユースケースに対応できます。

以下の図は、Dockerfile の基本的な構造と、各命令がどのフェーズで機能するかを示しています。

mermaidflowchart TB
    subgraph build["ビルドフェーズ"]
        from["FROM<br/>ベースイメージ選択"]
        arg["ARG<br/>ビルド時変数定義"]
        run["RUN<br/>コマンド実行"]
        copy["COPY / ADD<br/>ファイル追加"]
    end

    subgraph runtime["実行フェーズ"]
        env["ENV<br/>環境変数設定"]
        entrypoint["ENTRYPOINT<br/>起動コマンド"]
    end

    from --> arg
    arg --> run
    arg --> copy
    run --> env
    copy --> env
    env --> entrypoint

ビルド時に使う命令と実行時に効果を発揮する命令を理解することで、より効率的な Dockerfile が書けるようになります。

課題

Dockerfile を書く際、多くの開発者が以下のような課題に直面しています。

命令の使い分けが不明確

COPY と ADD の違いは何でしょうか。どちらもファイルを追加する命令ですが、適切な使い分けができていないと、予期せぬ動作やセキュリティリスクを招く可能性があります。同様に、ENV と ARG の違いも混乱しやすいポイントです。

レイヤーの肥大化

RUN 命令を複数行に分けて記述すると、それぞれが個別のレイヤーとなり、イメージサイズが肥大化してしまいます。不要なキャッシュやテンポラリファイルが残ったまま次のレイヤーに進むと、最終的なイメージサイズが数 GB にもなることがあるでしょう。

セキュリティとメンテナンス性の欠如

ベースイメージのバージョンを固定せずにlatestタグを使うと、ビルドのたびに異なるイメージが取得され、再現性が失われます。また、環境変数に機密情報を直接書き込むと、イメージ内に平文で残ってしまう危険性があります。

以下の図は、よくある問題とその影響を示しています。

mermaidflowchart LR
    subgraph problems["よくある問題"]
        p1["命令の<br/>使い分け不足"]
        p2["レイヤー<br/>肥大化"]
        p3["セキュリティ<br/>リスク"]
    end

    subgraph impacts["影響"]
        i1["予期せぬ<br/>動作"]
        i2["イメージサイズ<br/>増大"]
        i3["機密情報<br/>漏洩"]
        i4["再現性<br/>欠如"]
    end

    p1 --> i1
    p2 --> i2
    p3 --> i3
    p3 --> i4

これらの課題を解決するには、各命令の役割と最適な書式を正しく理解することが重要です。

解決策

上記の課題を解決するために、各命令の役割を明確に理解し、最適な書式を採用しましょう。

命令の役割を正しく理解する

それぞれの命令には明確な役割があります。FROM はベースイメージの指定、ARG はビルド時の変数定義、ENV はコンテナ実行時の環境変数設定といった具合です。これらの違いを理解することで、適切な命令を適切なタイミングで使えるようになります。

レイヤー最適化のテクニックを活用する

RUN 命令は複数のコマンドを&&でつなぎ、1 つのレイヤーにまとめることができます。さらに、不要なファイルは同一レイヤー内で削除することで、最終イメージサイズを削減できるでしょう。

セキュリティとメンテナンス性を確保する

ベースイメージは具体的なバージョンタグを指定し、環境変数には機密情報を含めず、ビルド引数(ARG)を活用して柔軟性を持たせます。また、マルチステージビルドを活用すれば、ビルドツールを最終イメージに含めずに済みます。

以下の図は、最適化された Dockerfile の構造を示しています。

mermaidflowchart TB
    subgraph optimized["最適化されたDockerfile"]
        direction TB
        step1["FROM: バージョン固定"]
        step2["ARG: ビルド時変数"]
        step3["ENV: 環境変数"]
        step4["COPY: 依存定義ファイル"]
        step5["RUN: 複数コマンド統合"]
        step6["COPY: アプリコード"]
        step7["ENTRYPOINT: 起動設定"]
    end

    step1 --> step2
    step2 --> step3
    step3 --> step4
    step4 --> step5
    step5 --> step6
    step6 --> step7

    style step1 fill:#e1f5ff
    style step5 fill:#fff4e1
    style step7 fill:#e8f5e9

依存関係ファイルを先にコピーし、その後でアプリケーションコードをコピーする順序により、キャッシュを効率的に活用できます。

具体例

ここからは、各命令の具体的な書式とベストプラクティスを見ていきましょう。

FROM:ベースイメージの指定

FROM 命令は、Dockerfile の最初に記述する必須の命令です。どのイメージをベースにするかを指定します。

基本書式

typescriptFROM <image>:<tag>

この書式により、特定のバージョンのベースイメージを指定できます。

ベストプラクティス

dockerfile# 推奨:具体的なバージョンタグを指定
FROM node:18.19.0-alpine3.19

# 非推奨:latestタグは再現性が失われる
# FROM node:latest

バージョンを明確に指定することで、ビルドの再現性が確保されます。alpine 版を使用すると、イメージサイズを大幅に削減できるでしょう。

マルチステージビルドでの活用

dockerfile# ビルドステージ
FROM node:18.19.0-alpine3.19 AS builder

# 実行ステージ
FROM node:18.19.0-alpine3.19 AS runtime

AS キーワードでステージに名前を付けることで、後続の命令で参照できます。

ARG:ビルド時変数の定義

ARG 命令は、ビルド時にのみ有効な変数を定義します。docker build コマンドの --build-arg オプションで値を上書きできる点が特徴です。

基本書式

dockerfileARG <変数名>=<デフォルト値>

デフォルト値を設定しておくと、ビルド時に指定がなくても動作します。

実践例

dockerfile# Node.jsのバージョンをパラメータ化
ARG NODE_VERSION=18.19.0
ARG ALPINE_VERSION=3.19

FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION}

このように変数化することで、異なるバージョンでのビルドが容易になります。

FROM より前の ARG

dockerfile# FROM の前に記述したARGはグローバルスコープ
ARG BASE_IMAGE=node:18.19.0-alpine3.19

FROM ${BASE_IMAGE}

# FROM の後では再度ARGを宣言する必要がある
ARG NODE_VERSION

FROM 命令より前の ARG は特殊なスコープを持つため、注意が必要です。

ビルドコマンド例

bash# ARG変数を上書きしてビルド
docker build --build-arg NODE_VERSION=20.10.0 -t myapp:latest .

ビルド時に柔軟にパラメータを変更できる点が ARG の強みですね。

ENV:環境変数の設定

ENV 命令は、コンテナ実行時に有効な環境変数を設定します。ARG とは異なり、コンテナ起動後もアクセス可能です。

基本書式

dockerfileENV <変数名>=<値>

シンプルで分かりやすい書式です。

複数の環境変数を設定

dockerfile# 推奨:各変数を個別に記述(可読性が高い)
ENV NODE_ENV=production
ENV APP_PORT=3000
ENV LOG_LEVEL=info

各変数を 1 行ずつ記述すると、変更時の差分が分かりやすくなります。

ARG と ENV の連携

dockerfile# ビルド時変数を環境変数に変換
ARG BUILD_ENV=production
ENV NODE_ENV=${BUILD_ENV}

ARG APP_VERSION=1.0.0
ENV VERSION=${APP_VERSION}

この手法により、ビルド時のパラメータをコンテナ実行時にも引き継げます。

注意点

dockerfile# 機密情報は ENV に直接書かない
# ENV DATABASE_PASSWORD=secret123  ← 危険

# 実行時に環境変数を渡す方が安全
# docker run -e DATABASE_PASSWORD=$DB_PASS myapp

ENV に設定した値はイメージ内に記録されるため、機密情報は docker run 時に渡すべきです。

COPY:ファイルのコピー

COPY 命令は、ホストマシンからコンテナ内へファイルやディレクトリをコピーします。シンプルで予測可能な動作が特徴です。

基本書式

dockerfileCOPY <コピー元> <コピー先>

コピー元はビルドコンテキスト(通常は Dockerfile があるディレクトリ)からの相対パスです。

依存関係ファイルを先にコピー

dockerfile# package.jsonとyarn.lockを先にコピー
COPY package.json yarn.lock ./

# 依存関係をインストール
RUN yarn install --frozen-lockfile

この順序により、ソースコードが変更されても依存関係のレイヤーがキャッシュされます。

アプリケーションコードのコピー

dockerfile# アプリケーション全体をコピー
COPY . .

依存関係のインストール後にアプリコードをコピーすることで、キャッシュ効率が向上します。

.dockerignore の活用

dockerfile# .dockerignoreファイルでnode_modulesなどを除外
# COPY . . を実行しても除外対象はコピーされない
COPY . .

.dockerignore ファイルに不要なファイルを記述しておくと、COPY から自動的に除外されます。

オーナーとパーミッションの指定

dockerfile# 特定のユーザー権限でコピー
COPY --chown=node:node package.json yarn.lock ./
COPY --chown=node:node . .

セキュリティ向上のため、root ユーザー以外でファイルを所有させることができますね。

ADD:ファイルの追加(高機能版)

ADD 命令は、COPY の機能に加えて、URL からのダウンロードや tar 自動展開などの高度な機能を持ちます。

基本書式

dockerfileADD <コピー元> <コピー先>

書式は COPY と同じですが、動作が異なります。

tar 自動展開

dockerfile# tarアーカイブを展開してコピー
ADD application.tar.gz /app/

tar.gz、tar.bz2、tar.xz などのアーカイブは、自動的に展開されてコピーされます。

URL からのダウンロード

dockerfile# リモートファイルをダウンロード
ADD https://example.com/config.json /app/config/

URL を指定すると、そのファイルをダウンロードしてコピーできます。

COPY と ADD の使い分け

dockerfile# 通常のファイルコピーはCOPYを使用
COPY package.json ./

# tar自動展開が必要な場合のみADDを使用
ADD vendor-libs.tar.gz /usr/local/lib/

Docker の公式ベストプラクティスでは、特別な理由がない限り COPY の使用が推奨されています。ADD の自動展開機能は便利ですが、予期せぬ動作を招く可能性があるためです。

RUN:コマンドの実行

RUN 命令は、イメージビルド時にコマンドを実行し、その結果を新しいレイヤーとして保存します。

基本書式(シェル形式)

dockerfileRUN <コマンド>

シェル経由で実行されるため、環境変数の展開やパイプなどが使用できます。

基本書式(exec 形式)

dockerfileRUN ["実行ファイル", "パラメータ1", "パラメータ2"]

exec 形式は、シェルを介さず直接実行されます。

複数コマンドの統合(レイヤー最適化)

dockerfile# 推奨:複数コマンドを1つのRUNにまとめる
RUN apk add --no-cache \
    python3 \
    py3-pip \
    git \
    && pip3 install --no-cache-dir requests \
    && apk del py3-pip

バックスラッシュ(\)で改行し、&&でコマンドを連結することで、1 つのレイヤーにまとめられます。

キャッシュクリアを同一レイヤーで実行

dockerfile# 推奨:インストールとクリーンアップを同一レイヤーで
RUN yarn install --frozen-lockfile \
    && yarn cache clean

キャッシュクリアを別の RUN で実行すると、前のレイヤーにキャッシュが残ったままになってしまいます。

apt や yum の最適パターン

dockerfile# Debian/Ubuntu系
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        curl \
        ca-certificates \
    && rm -rf /var/lib/apt/lists/*

apt-get update と install を同一 RUN で実行し、最後にキャッシュを削除することがベストプラクティスです。

Alpine Linux の最適パターン

dockerfile# Alpine Linux
RUN apk add --no-cache \
    nodejs \
    yarn

Alpine の apk コマンドでは、--no-cache オプションでキャッシュを保存しない設定ができます。

ENTRYPOINT:コンテナ起動コマンドの設定

ENTRYPOINT 命令は、コンテナ起動時に必ず実行されるコマンドを定義します。CMD と組み合わせて使うことで、柔軟な起動設定が可能です。

基本書式(exec 形式・推奨)

dockerfileENTRYPOINT ["実行ファイル", "パラメータ1", "パラメータ2"]

exec 形式は、シグナルが正しく伝わるため推奨されます。

基本書式(シェル形式)

dockerfileENTRYPOINT コマンド パラメータ

シェル形式は、シグナル処理に問題が生じる可能性があるため非推奨です。

Node.js アプリケーションの例

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

このように設定すると、docker run で追加引数を渡しても node server.js が必ず実行されます。

ENTRYPOINT と CMD の組み合わせ

dockerfile# ENTRYPOINTで実行ファイルを固定
ENTRYPOINT ["node"]

# CMDでデフォルト引数を設定(上書き可能)
CMD ["server.js"]

この構成により、docker run myapp index.js を実行すると、node index.js が起動します。

シェルスクリプトをエントリーポイントにする

dockerfile# エントリーポイントスクリプトをコピー
COPY docker-entrypoint.sh /usr/local/bin/

# 実行権限を付与
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# エントリーポイントに設定
ENTRYPOINT ["docker-entrypoint.sh"]

初期化処理が必要な場合は、シェルスクリプトをエントリーポイントにする手法が有効です。

エントリーポイントスクリプトの例

bash#!/bin/sh
set -e

# 環境変数の検証
if [ -z "$DATABASE_URL" ]; then
  echo "ERROR: DATABASE_URL is not set"
  exit 1
fi

# マイグレーション実行
yarn migrate:latest

# メインアプリケーション起動
exec "$@"

exec "$@" により、スクリプトの引数として渡されたコマンドが PID 1 で実行されます。

全体を統合した実践例

これまで学んだ命令を組み合わせた、実践的な Dockerfile の例をご紹介します。

マルチステージビルドの完全な例

dockerfile# ビルド引数の定義(グローバルスコープ)
ARG NODE_VERSION=18.19.0
ARG ALPINE_VERSION=3.19

# ========================================
# ビルドステージ
# ========================================
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS builder

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

最初のステージでは、ビルドに必要な環境を整えます。

dockerfile# ビルド時変数の再定義(ステージスコープ)
ARG BUILD_ENV=production

# 環境変数の設定
ENV NODE_ENV=${BUILD_ENV}

# 依存関係定義ファイルのコピー
COPY package.json yarn.lock ./

依存関係ファイルを先にコピーすることで、キャッシュ効率を最大化できます。

dockerfile# 依存関係のインストール
RUN yarn install --frozen-lockfile \
    && yarn cache clean

# アプリケーションコードのコピー
COPY . .

# ビルド実行
RUN yarn build

ビルドステージでは、開発依存関係を含むすべてのパッケージをインストールします。

dockerfile# ========================================
# 実行ステージ
# ========================================
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS runtime

# 本番環境用のパッケージをインストール
RUN apk add --no-cache \
    tini \
    && addgroup -g 1001 -S nodejs \
    && adduser -S nodejs -u 1001

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

実行ステージでは、最小限のパッケージのみをインストールします。tini はシグナル処理を適切に行うための軽量な init プロセスです。

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

# ビルドステージから成果物をコピー
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./

--from=builder により、ビルドステージの成果物のみを実行ステージにコピーできます。

dockerfile# 非rootユーザーに切り替え
USER nodejs

# ヘルスチェック用ポートの公開
EXPOSE 3000

# エントリーポイントの設定
ENTRYPOINT ["/sbin/tini", "--"]

# デフォルトコマンド
CMD ["node", "dist/server.js"]

tini を経由することで、シグナルが適切に Node.js プロセスに伝わります。

この構成により、ビルドツールやソースコードを含まない、最小限のサイズの実行イメージが作成できるでしょう。

以下の図は、マルチステージビルドの流れを示しています。

mermaidflowchart LR
    subgraph builder["ビルドステージ"]
        b1["FROM node:18"]
        b2["依存関係<br/>インストール"]
        b3["ソースコード<br/>コピー"]
        b4["ビルド実行"]
        b1 --> b2 --> b3 --> b4
    end

    subgraph runtime["実行ステージ"]
        r1["FROM node:18"]
        r2["最小限の<br/>パッケージ"]
        r3["成果物のみ<br/>コピー"]
        r4["ENTRYPOINT<br/>設定"]
        r1 --> r2 --> r3 --> r4
    end

    b4 -.->|"COPY --from=builder"| r3

    style builder fill:#fff4e1
    style runtime fill:#e8f5e9

ビルドステージの成果物のみを実行ステージに引き継ぐことで、イメージサイズを大幅に削減できます。

まとめ

本記事では、Dockerfile で最も重要な 7 つの命令(FROM、ARG、ENV、COPY、ADD、RUN、ENTRYPOINT)について、それぞれの役割と最適な書式を解説しました。

FROM 命令では具体的なバージョンタグを指定し、再現性を確保することが重要です。ARG と ENV の使い分けでは、ビルド時のパラメータ化と実行時の環境変数を適切に区別しましょう。COPY と ADD では、通常のファイルコピーには COPY を使用し、tar 自動展開などの特殊な用途にのみ ADD を使うことが推奨されます。

RUN 命令では、複数のコマンドを&&で連結し、キャッシュのクリーンアップまでを同一レイヤーで実行することで、イメージサイズを最小化できます。ENTRYPOINT では、exec 形式を使用してシグナル処理を適切に行い、CMD と組み合わせることで柔軟性を確保できるでしょう。

これらの命令を正しく理解し、適切な順序で記述することで、軽量で安全、かつメンテナンスしやすい Dockerfile を作成できます。マルチステージビルドを活用すれば、ビルドツールや開発依存関係を最終イメージから除外し、本番環境に最適化されたコンテナイメージを構築できますね。

ぜひ本記事のチートシートと具体例を参考に、実際のプロジェクトで Dockerfile を最適化してみてください。

関連リンク