リンドくんたなべ先生、DockerfileでENTRYPOINTとCMDっていう2つの命令があるんですけど、これって何が違うんですか?どっちを使えばいいのか迷ってしまって...
たなべ確かに最初は混乱しやすいポイントなんだ。
実はENTRYPOINTとCMDはどちらもコンテナ起動時に実行されるコマンドを指定するものなんだけど、使い方と役割が少し違うんだよ。今日はその違いをしっかり理解していこう!
Dockerを使ってアプリケーションをコンテナ化する際、Dockerfileの中で必ず目にするのがENTRYPOINTとCMDという命令です。
どちらもコンテナが起動したときに実行されるコマンドを指定するものですが、その挙動と使い分けには明確な違いがあります。
この記事では、Docker初心者の方でも理解できるよう、ENTRYPOINTとCMDの基本的な概念から実践的な使い分けをステップバイステップで解説していきます。
ENTRYPOINTとCMDの基本概念
リンドくんそもそも、なぜ2つも似たような命令があるんですか?1つでいいような気がするんですが...
たなべそれはね、Dockerが柔軟性と確実性の両方を実現するためなんだ。
ENTRYPOINTは「絶対に実行したいコマンド」、CMDは「デフォルトの引数や動作」というイメージで考えるとわかりやすいよ。
ENTRYPOINTとは
ENTRYPOINTは、コンテナが実行するメインのコマンドを固定するための命令です。
一度ENTRYPOINTで指定したコマンドは、コンテナ起動時に上書きすることが基本的にはできません(--entrypointオプションを使えば上書きできますが、通常は推奨されません)。
ENTRYPOINTの主な特徴は以下の通りです。
- コンテナの主要な実行可能ファイルを定義する
- docker runコマンドで指定した引数は、ENTRYPOINTに渡される
- コンテナの目的を明確にする役割を持つ
CMDとは
CMDは、コンテナ起動時のデフォルトの動作や引数を定義するための命令です。
CMDで指定した内容は、docker runコマンドで別のコマンドを指定すると簡単に上書きされます。
CMDの主な特徴は以下の通りです。
- デフォルトの引数やコマンドを提供する
- docker runコマンドで簡単に上書き可能
- ENTRYPOINTと組み合わせて使うことが多い
2つの命令が存在する理由
DockerがENTRYPOINTとCMDの両方を用意している理由は、確実性と柔軟性のバランスを取るためです。
- ENTRYPOINT → 「このコンテナは必ずこのプログラムを実行する」という確実性を保証
- CMD → 「デフォルトではこう動くけど、必要に応じて変更できる」という柔軟性を提供
この2つを組み合わせることで、開発者はコンテナの動作を柔軟にコントロールしながらも、コンテナの目的を明確に保つことができるのです。
ENTRYPOINTの詳細と使い方
リンドくんENTRYPOINTの具体的な使い方を教えてください!
たなべわかった!実際のコード例を見ながら説明していこう。
ENTRYPOINTにはexec形式とshell形式の2つの書き方があるんだ。
ENTRYPOINTの2つの形式
ENTRYPOINTには、記述方法が2種類あります。
exec形式(推奨)
ENTRYPOINT ["実行ファイル", "パラメータ1", "パラメータ2"]exec形式はJSON配列の形式で記述します。これが推奨される書き方です。
具体例:
FROM python:3.14-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]この場合、コンテナは必ずpython app.pyというコマンドを実行します。
shell形式
ENTRYPOINT コマンド パラメータ1 パラメータ2shell形式はシェルを介して実行されます。
具体例:
FROM python:3.14-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT python app.pyexec形式が推奨される理由
exec形式が推奨される理由は、以下のような重要なポイントがあります。
- シグナルの正しい処理 → Ctrl+Cなどのシグナルが直接アプリケーションに届く
- 予測可能な動作 → シェルを介さないため、シェルの解釈による予期しない動作を避けられる
- PID 1の問題を回避 → コンテナ内のメインプロセスが正しくPID 1として動作する
実際のプロジェクトでは、よほどの理由がない限りexec形式を使用することをおすすめします。
ENTRYPOINTの実践例
Webサーバーを起動するコンテナの例を見てみましょう。
FROM nginx:alpine
# 設定ファイルをコピー
COPY nginx.conf /etc/nginx/nginx.conf
COPY html /usr/share/nginx/html
# nginxをフォアグラウンドで実行
ENTRYPOINT ["nginx", "-g", "daemon off;"]このDockerfileでビルドしたコンテナは、常にnginxサーバーとして動作します。
docker runコマンドで別のコマンドを指定しようとしても、nginxが実行されます(--entrypointを使わない限り)。
# コンテナを起動(nginxが実行される)
docker run my-nginx-image
# 追加の引数を渡すこともできる
docker run my-nginx-image -h # nginxに-hオプションが渡されるCMDの詳細と使い方
リンドくんじゃあCMDはどう使うんですか?ENTRYPOINTと組み合わせる場合もあるって聞きましたけど...
たなべそうなんだ!CMDも2つの形式があって、さらにENTRYPOINTと組み合わせることでより柔軟な設定ができるようになるんだよ。
CMDの3つの形式
CMDには実は3つの記述方法があります。
1. exec形式(推奨)
CMD ["実行ファイル", "パラメータ1", "パラメータ2"]具体例:
FROM python:3.14-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]2. shell形式
CMD コマンド パラメータ1 パラメータ2具体例:
FROM ubuntu:22.04
CMD echo "Hello, Docker!"3. ENTRYPOINTのパラメータとして(最も一般的な使い方)
CMD ["パラメータ1", "パラメータ2"]この形式は、ENTRYPOINTと組み合わせて使用します。
具体例:
FROM python:3.14-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]
CMD ["--host", "0.0.0.0", "--port", "8000"]この場合、デフォルトではpython app.py --host 0.0.0.0 --port 8000が実行されますが、docker runコマンドで引数を指定すれば、CMDの部分だけが上書きされます。
CMDの上書き動作
CMDの最大の特徴は、簡単に上書きできることです。
FROM ubuntu:22.04
CMD ["echo", "デフォルトメッセージ"]このDockerfileからビルドしたイメージを使用する場合は以下のような挙動になります。
# デフォルト動作(CMDが実行される)
docker run my-ubuntu-image
# 出力: デフォルトメッセージ
# コマンドを上書き(CMDは無視される)
docker run my-ubuntu-image echo "カスタムメッセージ"
# 出力: カスタムメッセージ
# まったく別のコマンドも実行できる
docker run my-ubuntu-image ls -la
# カレントディレクトリの内容が表示されるCMDの実践例
データベースコンテナの設定例を見てみましょう。
FROM postgres:18-alpine
# 環境変数の設定
ENV POSTGRES_DB=myapp
ENV POSTGRES_USER=appuser
ENV POSTGRES_PASSWORD=secret
# デフォルトではpostgresサーバーとして起動
CMD ["postgres"]この設定により、通常はPostgreSQLサーバーとして起動しますが、必要に応じて以下のように動作を変更できます。
# デフォルト動作(PostgreSQLサーバーとして起動)
docker run my-postgres-image
# データベースの初期化スクリプトを実行
docker run my-postgres-image initdb
# psqlクライアントを起動
docker run -it my-postgres-image psqlこのように、CMDを使うことで同じイメージから異なる用途のコンテナを簡単に作成できます。
ENTRYPOINTとCMDを組み合わせる実践テクニック
リンドくん2つを組み合わせるとどんないいことがあるんですか?
たなべこれがDockerの真骨頂なんだ!
確実性と柔軟性を両立できるようになるんだよ。実際のプロジェクトでよく使われるパターンを紹介するね。
基本的な組み合わせパターン
ENTRYPOINTとCMDを組み合わせると、以下のような動作になります。
ENTRYPOINT ["実行ファイル"]
CMD ["デフォルトパラメータ"]実行時には、実行ファイル デフォルトパラメータというコマンドが実行されます。
そして、docker runでパラメータを指定すると、CMDの部分だけが置き換わります。
パターン① Webアプリケーションサーバー
本番環境とデバッグ環境で動作を切り替えられるDockerfileの例です。
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# アプリケーションは必ずnodeで実行
ENTRYPOINT ["node"]
# デフォルトは本番モードで起動
CMD ["server.js"]使い方は以下の通りです。
# 本番モード(デフォルト)
docker run my-node-app
# 実行されるコマンド: node server.js
# 開発モード
docker run my-node-app dev-server.js
# 実行されるコマンド: node dev-server.js
# テスト実行
docker run my-node-app test.js
# 実行されるコマンド: node test.jsパターン② CLIツールコンテナ
AWSのCLIツールのようなコマンドラインツールをコンテナ化する例です。
FROM python:3.14-slim
RUN pip install awscli
# AWS CLIは必ず実行
ENTRYPOINT ["aws"]
# デフォルトはヘルプを表示
CMD ["--help"]使い方は以下の通りです。
# ヘルプを表示(デフォルト)
docker run my-aws-cli
# S3バケットのリスト表示
docker run my-aws-cli s3 ls
# EC2インスタンスの一覧
docker run my-aws-cli ec2 describe-instancesパターン③ データ処理バッチコンテナ
データ処理スクリプトで、処理モードを柔軟に切り替えられる例です。
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY data_processor.py .
# 必ずPythonスクリプトを実行
ENTRYPOINT ["python", "data_processor.py"]
# デフォルトは全データを処理
CMD ["--mode", "full", "--output", "/data/results"]使い方は以下の通りです。
# 全データ処理(デフォルト)
docker run -v $(pwd)/data:/data my-data-processor
# 増分処理のみ
docker run -v $(pwd)/data:/data my-data-processor --mode incremental
# テストモード
docker run -v $(pwd)/data:/data my-data-processor --mode test --limit 100パターン④ 複数機能を持つアプリケーション
1つのイメージから複数の役割のコンテナを起動できる例です。
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# エントリーポイントスクリプトで分岐
ENTRYPOINT ["python", "entrypoint.py"]
# デフォルトはWebサーバーモード
CMD ["web"]↓entrypoint.pyの内容例↓
import sys
def main():
mode = sys.argv[1] if len(sys.argv) > 1 else "web"
if mode == "web":
# Webサーバーを起動
from app import run_server
run_server()
elif mode == "worker":
# バックグラウンドワーカーを起動
from worker import run_worker
run_worker()
elif mode == "migrate":
# データベースマイグレーション
from migrations import run_migrations
run_migrations()
else:
print(f"Unknown mode: {mode}")
sys.exit(1)
if __name__ == "__main__":
main()使い方は以下の通りです。
# Webサーバーとして起動(デフォルト)
docker run my-multi-app
# バックグラウンドワーカーとして起動
docker run my-multi-app worker
# データベースマイグレーション実行
docker run my-multi-app migrateこのパターンは、マイクロサービスアーキテクチャで同じコードベースから異なる役割のコンテナを起動する際に非常に便利です。
ベストプラクティスと推奨される使い方
リンドくん実際のプロジェクトでは、どういう基準で使い分ければいいんでしょうか?
たなべいい質問だね!チーム開発や本番運用を考えると、いくつか重要なガイドラインがあるんだ。
これを守ることで、保守しやすいDockerfileが作れるよ。
基本的な使い分けの指針
ENTRYPOINTとCMDを使い分ける際の基本的な考え方は以下の通りです。
ENTRYPOINTを使うべきケース
- コンテナの主要な目的が明確な場合
- 実行するプログラムを固定したい場合
- コンテナを実行可能ファイルのように扱いたい場合
CMDを使うべきケース
- デフォルトの動作を提供しつつ、柔軟性を持たせたい場合
- テストや開発時に異なるコマンドを実行したい場合
- ベースイメージを作成する場合
組み合わせを使うべきケース
- 実行プログラムは固定しつつ、引数は柔軟に変更したい場合
- 本番・開発・テスト環境で動作を切り替えたい場合
- 1つのイメージから複数の用途のコンテナを作りたい場合
Dockerfileの記述ルール
チーム開発で混乱を避けるための推奨ルールです。
ルール① exec形式を優先する
# 推奨
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8000"]
# 非推奨(特別な理由がない限り)
ENTRYPOINT python app.py
CMD --port 8000ルール② 1つのDockerfileには1つのENTRYPOINTと1つのCMDのみ
複数記述すると、最後の1つだけが有効になります。意図しない動作を避けるため、明示的に1つずつ記述しましょう。
# 良い例
ENTRYPOINT ["node"]
CMD ["server.js"]
# 悪い例(最後のものだけが有効になる)
ENTRYPOINT ["node"]
ENTRYPOINT ["python"] # これが有効になる
CMD ["app.py"]
CMD ["server.js"] # これが有効になるルール③ コメントで意図を明確にする
# Webサーバーとして必ず起動(変更不可)
ENTRYPOINT ["nginx", "-g", "daemon off;"]
# デフォルト設定ファイルの場所(起動時に変更可能)
CMD ["-c", "/etc/nginx/nginx.conf"]セキュリティのベストプラクティス
本番環境で使用する際の重要なポイントです。
非rootユーザーで実行する
FROM node:20-alpine
# アプリケーション用ユーザーを作成
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
COPY --chown=nodejs:nodejs . .
# 非rootユーザーに切り替え
USER nodejs
ENTRYPOINT ["node"]
CMD ["server.js"]シグナルハンドリングを考慮する
exec形式を使用し、アプリケーションがPID 1として動作するようにします。
# 推奨: シグナルが正しく処理される
ENTRYPOINT ["python", "-u", "app.py"]
# 非推奨: シェルがPID 1になり、シグナルが正しく伝わらない
ENTRYPOINT python -u app.pyチーム開発での推奨パターン
パターン① ドキュメント化されたデフォルト値
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# 環境変数でデフォルト値を明示
ENV APP_PORT=8000
ENV APP_HOST=0.0.0.0
ENV APP_WORKERS=4
ENTRYPOINT ["python", "app.py"]
# CMDは環境変数を参照(実際の値はENVで確認できる)
CMD ["--host", "${APP_HOST}", "--port", "${APP_PORT}", "--workers", "${APP_WORKERS}"]パターン② docker-compose.ymlでの上書き
Dockerfileではシンプルなデフォルト値を設定し、環境ごとの設定はdocker-compose.ymlで行います。
Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENTRYPOINT ["node"]
CMD ["server.js"]version: '3.8'
services:
web:
build: .
command: ["dev-server.js"] # CMDを上書き
volumes:
- .:/app
ports:
- "3000:3000"docker-compose.prod.yml(本番環境):
version: '3.8'
services:
web:
image: my-app:latest
# CMDのデフォルト値を使用
restart: always
ports:
- "80:3000"この方法により、Dockerfileはシンプルに保ちつつ、環境ごとの設定を柔軟に管理できます。
まとめ
リンドくんENTRYPOINTとCMDの違い、やっと理解できました!組み合わせて使うと本当に便利ですね。
たなべそう思ってもらえて嬉しいよ!
最初は混乱するかもしれないけど、実際に手を動かして試してみることが一番の理解への近道だからね。ぜひ自分のプロジェクトで実践してみてほしいな。
この記事では、DockerのENTRYPOINTとCMDの違いと使い分けについて詳しく解説してきました。
重要なポイントをおさらいしましょう。
- ENTRYPOINTは、コンテナの主要な実行コマンドを固定するために使用します
- CMDは、デフォルトの引数や動作を定義し、柔軟に変更できるようにします
- 組み合わせて使うことで、確実性と柔軟性を両立できます
- exec形式を優先的に使用することで、シグナル処理などの問題を回避できます
- チーム開発では、意図を明確にしたコメントと一貫性のある使い方が重要です
ENTRYPOINTとCMDの違いを理解することは、Dockerを使いこなす上での重要なステップです。
特に、これら2つを適切に組み合わせることで、開発から本番環境まで一貫した動作を保ちながら、必要な柔軟性も確保できます。
最初は戸惑うかもしれませんが、実際に様々なパターンを試してみることで、徐々に「このケースではこう書くべき」という感覚が身についてきます。
ぜひ今回紹介したサンプルコードを参考に、自分のプロジェクトでDockerfileを書いてみてください。





