🐳 Dockerfile: CMD と ENTRYPOINT の違い
Dockerコンテナの起動時に実行されるコマンドを定義する CMD と ENTRYPOINT。 これらは似ていますが、役割と組み合わせた時の挙動に明確な違いがあります。
1. 根本的な違い
| 指示句 | 役割 | 上書きのしやすさ |
|---|---|---|
| ENTRYPOINT | 「コンテナが実行する主役のプロセス (実行ファイル)」 を定義。 コンテナを「実行ファイル化」するために使います。 | docker run --entrypoint オプションが必要。(簡単には変動させたくない固定処理) |
| CMD | 「ENTRYPOINTに渡すデフォルトの引数」 を定義。 または、ENTRYPOINTがない場合のデフォルトコマンド。 | docker run <image> <command> で簡単に上書き可能。(可変な引数やオプション) |
2. 組み合わせた時の挙動
ENTRYPOINT と CMD を両方書くと、「ENTRYPOINT + CMD」 という形で連結されて実行されます。
例: ls -la したい場合
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形式を使うと、CMD や run コマンドの引数が ENTRYPOINT に渡されなくなります(無視されます)。 Docker Infrastructureでは基本的に Exec形式 を採用しています。
4. Docker Infrastructureでの実装例
本プロジェクトでは、「初期化スクリプト (ENTRYPOINT) + メインサーバープロセス (CMD)」 というパターンを多用しています。
WordPress の例
初期設定 (setup.sh) を必ず通してから、PHP-FPM (php-fpm8.2) を起動したい場合。
# 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 の最後には以下の記述が必要です:
# setup.sh
# ... 初期設定 ...
# 引数として渡されたコマンド(CMD)を実行する
exec "$@"これにより、コンテナ起動時に setup.sh /usr/sbin/php-fpm8.2 -F が実行される形になり、setup.sh 内の exec "$@" で最終的に PHP-FPM にプロセスが置き換わります。
Website (Node.js) の例
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["npm", "start"]通常起動時は entrypoint.sh 経由で npm start が実行されます。
5. 具体的な上書き方法 (make docs-dev)
make docs-dev コマンドでは、この仕組みを利用して CMDだけを上書き しています。
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何が起きているか?
websiteサービスを指定して起動します。- 後ろに続く
npm run docs:devが、DockerfileのCMD ["npm", "start"]を上書きします。 ENTRYPOINTは上書きされていないので、entrypoint.shはそのまま実行されます。
実行フロー:
entrypoint.shが起動。entrypoint.shが引数としてnpm run docs:devを受け取る。entrypoint.sh内のexec "$@"が実行され、プロセスがnpm run docs:dev(VitePressの開発サーバー) に置き換わる。
この仕組みのおかげで、「共通の初期化処理(秘密鍵の読み込みなど)」は維持したまま、「本番サーバー起動」と「ドキュメント開発サーバー起動」を柔軟に切り替えることができています。