「ENTRYPOINT と CMD の違いを調べても同じことしか書いていなくて、結局どちらを使えばいいかわからない」
Dockerfile を書き始めた段階で多くの人がつまずくのが、ENTRYPOINT と CMD の使い分けです。
この記事では、2つの命令の明確な違い、「シェル形式」と「exec形式」の落とし穴、そして実務で役立つ設計パターンを実際のコマンド実行例とともに解説します。
動作確認は Rocky Linux 9 / Ubuntu 24.04 LTS + Docker 26.x で行っています。
この記事のポイント
・ENTRYPOINT は「コンテナが必ず実行するプログラム」を固定する命令
・CMD は「ENTRYPOINT へのデフォルト引数」または単独のデフォルトコマンド
・exec形式(JSON配列)を使わないとシグナルが届かず docker stop が10秒かかる
・両方を組み合わせると「固定コマンド+上書き可能な引数」の柔軟な設計が実現できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
ENTRYPOINTとCMDの違いを整理する
Dockerfile には、コンテナ起動時に実行するコマンドを指定する命令が2つあります。・ENTRYPOINT:コンテナが「必ず実行するプログラム」を定義します。docker run で上書きするには --entrypoint オプションが必要です。
・CMD:ENTRYPOINT が定義されている場合はその「デフォルト引数」として機能します。ENTRYPOINT がない場合は、単独のデフォルト起動コマンドになります。docker run の末尾に引数を渡すだけで上書きできます。
| 命令 | 役割 | docker run で上書きする方法 |
|---|---|---|
| ENTRYPOINT | コンテナが実行するプログラム(固定) | --entrypoint オプションが必要 |
| CMD | デフォルト引数またはデフォルトコマンド | docker run の末尾に引数を渡すだけ |
どちらか1つだけ書く場合と、両方書く場合で動作が変わります。以下で確認しましょう。
1. ENTRYPOINTのみの場合
docker run の末尾に渡したものがすべて ENTRYPOINT の引数になります。# Dockerfile(ENTRYPOINT のみ指定) FROM ubuntu:22.04 ENTRYPOINT ["echo"]
# 動作確認 $ docker build -t entrypoint-only . $ docker run entrypoint-only hello world hello world # 引数を渡さないと echo コマンドが引数なしで実行される(空行が出力される) $ docker run entrypoint-only
2. CMDのみの場合
docker run の末尾に引数を渡すと、CMD 全体が上書きされます。# Dockerfile(CMD のみ指定) FROM ubuntu:22.04 CMD ["echo", "Hello from CMD"]
# CMD がそのまま実行される $ docker build -t cmd-only . $ docker run cmd-only Hello from CMD # docker run に引数を渡すと CMD 全体が上書きされる(echo は実行されない) $ docker run cmd-only /bin/bash -c "whoami" root
シェル形式とexec形式の違い
ENTRYPOINT と CMD にはどちらも「シェル形式」と「exec形式」の2通りの記法があります。この違いを正しく理解しておかないと、本番環境で `docker stop` に10秒以上かかるという問題が起きます。
1. シェル形式(問題あり)
シェル形式はコマンドをそのまま文字列で記述します。# シェル形式の書き方(スペース区切りの文字列) ENTRYPOINT nginx -g "daemon off;" CMD echo "Hello"
そのため、コンテナ内の PID 1(プロセスID 1番)が `/bin/sh` になり、実際のアプリは子プロセスになります。
問題は、`docker stop` が送る `SIGTERM` シグナルが PID 1(sh)に届いても、sh がシグナルを子プロセスへ転送しないことです。
Dockerは10秒後にタイムアウトして `SIGKILL` を送り、コンテナを強制終了します。
# シェル形式の場合、PID 1 が sh になっている $ docker exec mycontainer ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 2888 968 ? Ss 10:05 0:00 /bin/sh -c nginx -g daemon off; root 7 0.0 0.1 54264 4256 ? S 10:05 0:00 nginx: master process nginx -g daemon off; # docker stop がタイムアウトまでかかる(10秒待ってから SIGKILL) $ time docker stop mycontainer mycontainer real 0m10.126s
2. exec形式(推奨)
exec形式はコマンドを JSON 配列で記述します。# exec形式の書き方(JSON配列) ENTRYPOINT ["nginx", "-g", "daemon off;"] CMD ["echo", "Hello"]
`docker stop` の `SIGTERM` がアプリに直接届き、グレースフルシャットダウンが正しく機能します。
# exec形式の場合、PID 1 が nginx になっている $ docker exec mycontainer ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 54264 4256 ? Ss 10:05 0:00 nginx: master process nginx -g daemon off; # docker stop が即座に完了する(SIGTERM が nginx に届く) $ time docker stop mycontainer mycontainer real 0m0.305s
ENTRYPOINT がシェル形式の場合、CMD は完全に無視されます。
ENTRYPOINTとCMDを組み合わせる設計パターン
ENTRYPOINT と CMD を組み合わせると、「実行するプログラムを固定しつつ、デフォルト引数だけ上書き可能」にする設計が実現できます。典型的な使用例は、CLI ツールのコンテナ化です。
# curl ツールをコンテナ化した Dockerfile FROM ubuntu:22.04 RUN apt-get update && apt-get install -y curl --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT ["curl"] CMD ["--help"]
$ docker build -t my-curl . # 引数なし → CMD の --help が使われる $ docker run my-curl Usage: curl [options...]
-d, --data HTTP POST data ... # URL を指定 → CMD が上書きされて指定の URL にリクエストする $ docker run my-curl https://example.com Example Domain ... # JSON のレスポンスを整形して取得する $ docker run my-curl -s https://api.example.com/status | python3 -m json.tool { "status": "ok", "version": "1.2.3" }
・ENTRYPOINT で「何をするコンテナか」を固定する:curl コンテナに /bin/bash を起動させるといったことが docker run の末尾引数だけではできなくなります
・CMD で「デフォルト動作」を定義する:引数なしで実行したときのフォールバックとして機能します
・docker run の末尾引数で CMD を柔軟に上書きできる:--entrypoint オプションを使う必要がなく、シンプルに使えます
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、DockerfileのベストプラクティスやCompose設計まで含めた実践的なDocker講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
docker runでENTRYPOINT・CMDを上書きする方法
1. CMDを上書きする(末尾に引数を渡す)
docker run の末尾に渡した引数がそのまま CMD を上書きします。ENTRYPOINT は変わらず、引数だけ差し替えられます。
# CMD を上書きして別のオプションを渡す $ docker run my-curl -I https://example.com HTTP/2 200 content-type: text/html; charset=UTF-8 ...
2. ENTRYPOINTを上書きする(--entrypoint)
ENTRYPOINT 自体を変更するには `--entrypoint` オプションを使います。デバッグ時にコンテナ内のシェルに入りたい場合によく使います。
# ENTRYPOINT を bash で上書きしてコンテナ内に入る $ docker run --entrypoint /bin/bash -it my-curl root@a1b2c3d4e5f6:/# # コンテナ内から curl が使えるか確認する root@a1b2c3d4e5f6:/# curl --version curl 7.81.0 (x86_64-pc-linux-gnu) libcurl/7.81.0 ...
3. docker-compose.yml での上書き
Compose を使っている場合は、`entrypoint:` と `command:` キーで上書きできます。・entrypoint:ENTRYPOINT に相当します。記述すると Dockerfile の ENTRYPOINT を上書きします
・command:CMD に相当します。記述すると Dockerfile の CMD を上書きします
# docker-compose.yml での上書き例 services: curl: image: my-curl # コンテナを起動したまま待機させてデバッグに使う entrypoint: ["/bin/bash", "-c"] command: ["while true; do sleep 60; done"]
実践:エントリーポイントスクリプトを使うパターン
本番環境に近い設計として、シェルスクリプトを ENTRYPOINT にするパターンを紹介します。この手法は公式の Docker イメージ(PostgreSQL, MySQL, Redis など)でも広く使われています。
1. スクリプトを使う理由
直接アプリを ENTRYPOINT に指定するだけでは、コンテナ起動前の初期化処理が行えません。エントリーポイントスクリプトを挟むことで、次の処理をコンテナ起動時に安全に実行できます。
・環境変数の検証:必須の環境変数が未設定ならエラーで停止する
・設定ファイルの動的生成:envsubst などで設定ファイルのプレースホルダーを置換する
・起動順序の制御:データベースが起動するまでリトライするなど
2. エントリーポイントスクリプト
#!/bin/bash # docker-entrypoint.sh set -e # 必須の環境変数を検証する if [ -z "$APP_ENV" ]; then echo "ERROR: APP_ENV が未設定です。起動を中止します。" >&2 exit 1 fi echo "INFO: APP_ENV=${APP_ENV} でサーバーを起動します" # envsubst で設定テンプレートの変数を置換する envsubst '${APP_ENV} ${SERVER_NAME}' \ < /etc/nginx/nginx.conf.template \ > /etc/nginx/nginx.conf # exec で CMD(ENTRYPOINT の後続引数)を実行する # exec を使うことで nginx が PID 1 を引き継いでシグナルを受け取れる exec "$@"
`exec` を書くことで、このスクリプトが CMD(ここでは `nginx -g "daemon off;"`)に置き換えられ、nginx が PID 1 を引き継ぎます。
`exec` を書き忘れると、シェルが PID 1 のまま残り、nginx へのシグナルが届かなくなります。
3. Dockerfile
FROM nginx:1.27-alpine # envsubst のために gettext をインストールする RUN apk add --no-cache gettext # エントリーポイントスクリプトをコピーして実行権限を付与する COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh # 設定ファイルのテンプレートをコピーする COPY nginx.conf.template /etc/nginx/nginx.conf.template # スクリプトを ENTRYPOINT に設定する(exec形式必須) ENTRYPOINT ["docker-entrypoint.sh"] # デフォルトでは nginx をフォアグラウンド実行する CMD ["nginx", "-g", "daemon off;"]
4. 動作確認
$ docker build -t my-nginx . # 正常起動(APP_ENV を設定) $ docker run -d \ -e APP_ENV=production \ -e SERVER_NAME=www.example.com \ -p 8080:80 \ --name web \ my-nginx INFO: APP_ENV=production でサーバーを起動します ... # 環境変数なし → エラーで起動を停止する $ docker run my-nginx ERROR: APP_ENV が未設定です。起動を中止します。 # graceful stop が機能しているか確認する(即座に停止する) $ time docker stop web web real 0m0.318s
よくあるミスとトラブルシュート
【問題1】docker stop に10秒以上かかる
原因:ENTRYPOINT がシェル形式で記述されており、PID 1 が sh になっている。対処:ENTRYPOINT をexec形式(JSON配列)に変更する。シェルスクリプトを ENTRYPOINT にしている場合は末尾に `exec "$@"` があるか確認する。
# NG: シェル形式(PID 1 が sh になる) ENTRYPOINT nginx -g "daemon off;" # OK: exec形式(PID 1 が nginx になる) ENTRYPOINT ["nginx", "-g", "daemon off;"]
【問題2】ENTRYPOINT と CMD を書いたのに CMD が無視される
原因:ENTRYPOINT がシェル形式で記述されている。シェル形式の ENTRYPOINT は CMD を完全に無視します。対処:ENTRYPOINT をexec形式に変更する。
# NG: ENTRYPOINT がシェル形式 → CMD は無視される ENTRYPOINT echo CMD ["Hello"] # 無視される(echo が引数なしで実行される) # OK: 両方 exec形式 ENTRYPOINT ["echo"] CMD ["Hello"] # "Hello" が echo に渡されて "Hello" と出力される
【問題3】docker run に引数を渡しても反映されない
原因:docker-compose.yml に `command:` が明示的に記述されている。`command:` キーは Dockerfile の CMD を上書きするため、docker run の末尾引数よりも Compose の設定が優先されます。対処:Compose ファイルの `command:` 行を確認し、不要であれば削除する。
【問題4】エントリーポイントスクリプトが「Permission denied」で起動しない
原因:スクリプトに実行権限がない、またはスクリプトの先頭行(shebang)が Windows 改行コード(CRLF)になっている。対処:Dockerfile で `chmod +x` を実行しているか確認する。改行コードは LF に統一する。
# Dockerfile で実行権限を付与する(必須) COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh # Windows で作成したスクリプトの改行コードを LF に変換する $ sed -i 's/\r//' docker-entrypoint.sh # スクリプトの先頭が正しい shebang になっているか確認する $ head -1 docker-entrypoint.sh #!/bin/bash
本記事のまとめ
ENTRYPOINT と CMD の使い分けポイントをまとめます。| やりたいこと | Dockerfile の書き方 |
|---|---|
| コマンドを固定して引数だけ上書き可能にする | ENTRYPOINT ["cmd"] + CMD ["default-arg"] |
| シグナルを正しく受け取れるようにする | exec形式(JSON配列)で記述する |
| コンテナ起動前に初期化処理を実行する | シェルスクリプトを ENTRYPOINT にして末尾で exec "$@" |
| docker run でプログラム自体を変更する | docker run --entrypoint /bin/bash イメージ名 |
| docker run でデフォルト引数だけ変更する | docker run イメージ名 新しい引数 |
ENTRYPOINT と CMD を正しく使い分けると、「このコンテナが何をするプログラムか」が Dockerfile だけで明確になります。
シェル形式とexec形式の違いを意識するだけで、本番環境でのシグナル問題も回避できます。
チームで複数のコンテナを管理する場面では、この設計の差が運用の安定性に直結します。
次に読む記事:
・Dockerfileの書き方入門|自分のアプリをイメージ化する基礎と注意点
・docker execコマンドでコンテナ内を調査・デバッグする方法|docker logs・docker inspectの実践例も
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、DockerfileのENTRYPOINT設計から本番運用まで含めた実践的なDocker講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:docker execコマンドでコンテナ内を調査・デバッグする方法|docker logs・docker inspectの実践例も
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます