Skip to content

🐳 Dockerfile: CMD と ENTRYPOINT の違い

Dockerコンテナの起動時に実行されるコマンドを定義する CMDENTRYPOINT。 これらは似ていますが、役割と組み合わせた時の挙動に明確な違いがあります。

1. 根本的な違い

指示句役割上書きのしやすさ
ENTRYPOINT「コンテナが実行する主役のプロセス (実行ファイル)」 を定義。
コンテナを「実行ファイル化」するために使います。
docker run --entrypoint オプションが必要。
(簡単には変動させたくない固定処理)
CMD「ENTRYPOINTに渡すデフォルトの引数」 を定義。
または、ENTRYPOINTがない場合のデフォルトコマンド。
docker run <image> <command> で簡単に上書き可能。
(可変な引数やオプション)

2. 組み合わせた時の挙動

ENTRYPOINTCMD を両方書くと、「ENTRYPOINT + CMD」 という形で連結されて実行されます。

例: ls -la したい場合

dockerfile
ENTRYPOINT ["ls"]
CMD ["-la"]
  • そのまま実行: ls -la が実行される。
  • 引数指定で実行 (docker run my-image -l): ls -l が実行される(-la-l に上書きされる)。

これにより、「常に ls は実行させつつ、オプションだけユーザーが自由に変えられる」 コンテナが作れます。

3. 記述形式(Exec形式 vs Shell形式)

Dockerfileでは2つの書き方がありますが、推奨は Exec形式(JSON配列形式) です。

形式記述例特徴
Exec形式
(推奨)
ENTRYPOINT ["/bin/ping", "localhost"]シェルを介さず直接実行されるため、PID 1 となりシグナル(SIGTERM等)を正しく受け取れる。
Shell形式ENTRYPOINT /bin/ping localhost/bin/sh -c 経由で実行される。シグナルが伝播しないなどの問題が起きやすい。

注意

Shell形式を使うと、CMDrun コマンドの引数が ENTRYPOINT に渡されなくなります(無視されます)。 Docker Infrastructureでは基本的に Exec形式 を採用しています。

4. Docker Infrastructureでの実装例

本プロジェクトでは、「初期化スクリプト (ENTRYPOINT) + メインサーバープロセス (CMD)」 というパターンを多用しています。

WordPress の例

初期設定 (setup.sh) を必ず通してから、PHP-FPM (php-fpm8.2) を起動したい場合。

dockerfile
# Dockerfile
COPY tools/setup.sh /usr/local/bin/setup.sh

# 1. 必ず実行される初期化スクリプト
ENTRYPOINT ["/usr/local/bin/setup.sh"]

# 2. その後に実行されるデフォルトのコマンド
CMD ["/usr/sbin/php-fpm8.2", "-F"]

setup.sh の最後には以下の記述が必要です:

bash
# setup.sh
# ... 初期設定 ...

# 引数として渡されたコマンド(CMD)を実行する
exec "$@"

これにより、コンテナ起動時に setup.sh /usr/sbin/php-fpm8.2 -F が実行される形になり、setup.sh 内の exec "$@" で最終的に PHP-FPM にプロセスが置き換わります。

Website (Node.js) の例

dockerfile
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["npm", "start"]

通常起動時は entrypoint.sh 経由で npm start が実行されます。

5. 具体的な上書き方法 (make docs-dev)

make docs-dev コマンドでは、この仕組みを利用して CMDだけを上書き しています。

Makefile の定義

makefile
docs-dev:
	docker compose -f ./srcs/docker-compose.yml run --rm \
		-p 5173:5173 \
		-v $(PWD)/srcs/requirements/bonus/website/docs:/app/docs \
		website npm run docs:dev

何が起きているか?

  1. website サービスを指定して起動します。
  2. 後ろに続く npm run docs:dev が、Dockerfileの CMD ["npm", "start"] を上書きします。
  3. ENTRYPOINT は上書きされていないので、entrypoint.sh はそのまま実行されます。

実行フロー:

  1. entrypoint.sh が起動。
  2. entrypoint.sh が引数として npm run docs:dev を受け取る。
  3. entrypoint.sh 内の exec "$@" が実行され、プロセスが npm run docs:dev (VitePressの開発サーバー) に置き換わる。

この仕組みのおかげで、「共通の初期化処理(秘密鍵の読み込みなど)」は維持したまま、「本番サーバー起動」と「ドキュメント開発サーバー起動」を柔軟に切り替えることができています。

Released under the MIT License.