DockerfileのCMDとENTRYPOINTの違いとは?docker runの引数による動作を実例で比較


はじめに

Dockerfileには、コンテナ起動時に実行するコマンドを指定するためのCMDとENTRYPOINTという命令があります。

どちらもコンテナ起動時の動作に関係する命令ですが、docker runで引数を指定した場合の動作が異なります。

例えば、次の2つのDockerfileは、一見すると似た動作をします。

CMDを使用する場合

CMD ["/bin/echo", "Hello"]

ENTRYPOINTを使用する場合

ENTRYPOINT ["/bin/echo", "Hello"]

どちらも引数を指定せずにコンテナを起動すると、Helloが表示されます。

しかし、docker runで引数を指定すると、それぞれ異なる動作になります。

本記事では、次の内容を解説します。

  • CMDとENTRYPOINTの役割
  • シェル形式とexec形式の違い
  • CMDだけを指定した場合の動作
  • ENTRYPOINTだけを指定した場合の動作
  • CMDとENTRYPOINTを組み合わせた場合の動作
  • docker runの引数による実行コマンドの変化
  • --entrypointによる実行コマンドの変更

実際のDockerfileとコマンド例を使いながら、両者の違いを確認していきます。

1. CMDとは

CMDは、コンテナ起動時に実行するデフォルトのコマンド、またはENTRYPOINTに渡すデフォルト引数を指定する命令です。

例えば、Apache HTTP Serverをフォアグラウンドで実行する場合は、次のように記述します。

CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

この場合、コンテナ起動時に次のコマンドが実行されます。

/usr/sbin/httpd -DFOREGROUND

ただし、CMDで指定したコマンドは、docker runで別のコマンドを指定すると置き換えられます。

これがCMDの重要な特徴です。

1-1. CMDのシェル形式とexec形式

CMDには、シェル形式とexec形式があります。

exec形式

CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

シェル形式

CMD /usr/sbin/httpd -DFOREGROUND

exec形式は、コマンドと引数をJSON配列形式で指定します。

一方、シェル形式は、コマンドを文字列として指定します。

Linuxでは、シェル形式のコマンドは通常/bin/sh -cを介して実行されます。

このため、シェル形式ではシェルの機能を利用できます。

ただし、シェル形式の場合、シェルがPID 1のプロセスとなり、docker stopによる停止シグナルが実行中のプログラムに適切に伝わらない場合があります。

コンテナのメインプロセスを指定する場合は、exec形式が推奨されます。

2. ENTRYPOINTとは

ENTRYPOINTは、コンテナ起動時に実行するコマンドを指定する命令です。

例えば、次のように記述します。

ENTRYPOINT ["/bin/echo", "Hello"]

この場合、コンテナ起動時に次のコマンドが実行されます。

/bin/echo Hello

CMDと異なり、exec形式のENTRYPOINTでは、docker runで指定した引数がENTRYPOINTの後ろに追加されます。

そのため、実行するコマンドを決めておき、起動時に引数だけを変更したい場合に使用できます。

2-1. ENTRYPOINTのシェル形式とexec形式

ENTRYPOINTも、シェル形式とexec形式で記述できます。

exec形式

ENTRYPOINT ["/bin/echo", "Hello"]

シェル形式

ENTRYPOINT /bin/echo Hello

両者の違いは、コマンドを直接実行するか、シェルを介して実行するかという点です。

また、exec形式ではdocker runで指定した引数が実行コマンドに追加されます。

一方、シェル形式では、通常のdocker run引数やCMDの引数が同じようには渡されません。

本記事では、CMDとENTRYPOINTの引数の違いをわかりやすくするため、以降はexec形式を使用して説明します。

3. CMDだけを指定した場合の動作

まずは、CMDだけを指定した場合の動作を確認します。

次のDockerfileを使用します。

FROM alpine:3.21

CMD ["/bin/echo", "Hello"]

このDockerfileでは、コンテナ起動時に/bin/echo Helloを実行するように指定しています。

3-1. Dockerイメージをビルドする

Dockerfileを保存したディレクトリで、次のコマンドを実行します。

docker build -t cmd-sample .

これで、cmd-sampleという名前のDockerイメージが作成されます。

3-2. 引数なしでdocker runを実行する

次のコマンドを実行します。

docker run cmd-sample

実行結果:

Hello

この場合、Dockerfileで指定したCMDがそのまま実行されます。

実際に実行されるコマンドは次のとおりです。

/bin/echo Hello

3-3. 引数ありでdocker runを実行する

次に、docker runで別のコマンドを指定します。

docker run cmd-sample /bin/echo World

実行結果:

World

この場合、Dockerfileで指定したCMDの内容が、docker runで指定したコマンドに置き換えられます。

つまり、次のコマンドが実行されます。

/bin/echo World

3-4. CMDだけの場合のまとめ

docker run実行されるコマンド実行結果
docker run cmd-sample/bin/echo HelloHello
docker run cmd-sample /bin/echo World/bin/echo WorldWorld

このように、CMDだけを指定した場合は、docker runで別のコマンドを指定すると、CMD全体が置き換えられます。

4. ENTRYPOINTだけを指定した場合の動作

次に、ENTRYPOINTだけを指定した場合の動作を確認します。

次のDockerfileを使用します。

FROM alpine:3.21

ENTRYPOINT ["/bin/echo", "Hello"]

4-1. Dockerイメージをビルドする

docker build -t entrypoint-sample .

これで、entrypoint-sampleという名前のDockerイメージが作成されます。

4-2. 引数なしでdocker runを実行する

docker run entrypoint-sample

実行結果:

Hello

実際に実行されるコマンドは次のとおりです。

/bin/echo Hello

ここまではCMDだけを指定した場合と同じです。

4-3. 引数ありでdocker runを実行する

続いて、次のコマンドを実行します。

docker run entrypoint-sample World

実行結果:

Hello World

ここがCMDとの大きな違いです。

ENTRYPOINTでは、docker runで指定したWorldが、もともとのコマンドの後ろに追加されます。

そのため、実際に実行されるコマンドは次のようになります。

/bin/echo Hello World

4-4. ENTRYPOINTだけの場合のまとめ

docker run実行されるコマンド実行結果
docker run entrypoint-sample/bin/echo HelloHello
docker run entrypoint-sample World/bin/echo Hello WorldHello World

このように、exec形式のENTRYPOINTでは、docker runで指定した引数がENTRYPOINTの後ろに追加されます。

5. CMDとENTRYPOINTを組み合わせた場合の動作

CMDとENTRYPOINTは、同じDockerfile内で組み合わせて使用できます。

この場合、ENTRYPOINTに実行するコマンドを指定し、CMDにデフォルトの引数を指定できます。

例えば、次のDockerfileを考えます。

FROM alpine:3.21

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["Docker"]

このDockerfileでは、次のように役割を分けています。

  • ENTRYPOINT:/bin/echo Helloを指定する
  • CMD:デフォルトの引数としてDockerを指定する

5-1. Dockerイメージをビルドする

docker build -t combined-sample .

5-2. 引数なしでdocker runを実行する

docker run combined-sample

実行結果:

Hello Docker

この場合、ENTRYPOINTとCMDが組み合わされます。

実際に実行されるコマンドは次のとおりです。

/bin/echo Hello Docker

つまり、ENTRYPOINTで指定したコマンドの後ろに、CMDで指定した引数が追加されます。

5-3. 引数ありでdocker runを実行する

次に、次のコマンドを実行します。

docker run combined-sample World

実行結果:

Hello World

この場合、docker runで指定したWorldが、CMDで指定したDockerを置き換えます。

そのため、実行されるコマンドは次のようになります。

/bin/echo Hello World

ENTRYPOINTの/bin/echo Helloはそのままで、CMD部分だけが置き換えられていることがわかります。

5-4. CMDとENTRYPOINTを組み合わせた場合のまとめ

docker run実行されるコマンド実行結果
docker run combined-sample/bin/echo Hello DockerHello Docker
docker run combined-sample World/bin/echo Hello WorldHello World

このように、ENTRYPOINTとCMDを組み合わせると、実行コマンドを維持しながら、デフォルト引数を変更できます。

6. CMDとENTRYPOINTの違いを比較

ここまでの3つのパターンを比較してみましょう。

6-1. Dockerfileの指定方法

パターン①:CMDだけ

CMD ["/bin/echo", "Hello"]

パターン②:ENTRYPOINTだけ

ENTRYPOINT ["/bin/echo", "Hello"]

パターン③:ENTRYPOINTとCMDの組み合わせ

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["Docker"]

6-2. 引数を指定しなかった場合

パターン実行されるコマンド
CMDだけ/bin/echo Hello
ENTRYPOINTだけ/bin/echo Hello
ENTRYPOINT+CMD/bin/echo Hello Docker

6-3. 引数を指定した場合

パターンdocker runの指定実行されるコマンド
CMDだけ/bin/echo World/bin/echo World
ENTRYPOINTだけWorld/bin/echo Hello World
ENTRYPOINT+CMDWorld/bin/echo Hello World

この比較から、次の違いがわかります。

CMDだけの場合

docker runの引数によって、デフォルトのコマンド全体が置き換えられます。

ENTRYPOINTだけの場合

docker runの引数が、ENTRYPOINTで指定したコマンドの後ろに追加されます。

ENTRYPOINTとCMDを組み合わせた場合

ENTRYPOINTが実行コマンドとなり、CMDがデフォルト引数となります。docker runで引数を指定すると、CMDが置き換えられます。

7. ENTRYPOINTをdocker runで変更する方法

ここまで、ENTRYPOINTは通常のdocker run引数によって置き換えられないことを説明しました。

しかし、ENTRYPOINTは絶対に変更できないわけではありません。

docker runの--entrypointオプションを使用すると、Dockerfileで指定した実行コマンドを変更できます。

7-1. 通常のENTRYPOINTの動作

次のDockerfileを使用します。

FROM alpine:3.21

ENTRYPOINT ["/bin/echo", "Hello"]

Dockerイメージをビルドします。

docker build -t entrypoint-sample .

通常どおりコンテナを起動します。

docker run entrypoint-sample World

実行結果:

Hello World

この場合、ENTRYPOINTとdocker runの引数が組み合わされます。

7-2. –entrypointで実行コマンドを変更する

次に、--entrypointオプションを使用します。

docker run --entrypoint /bin/echo entrypoint-sample World

実行結果:

World

この場合、Dockerfileで指定した次のENTRYPOINTが、

ENTRYPOINT ["/bin/echo", "Hello"]

--entrypointによって、/bin/echoに置き換えられます。

そのため、実際に実行されるコマンドは次のとおりです。

/bin/echo World

もともとENTRYPOINTに含まれていたHelloは実行されません。

つまり、--entrypointを使用することで、Dockerfileに設定されている実行コマンドを変更できます。

7-3. CMDとENTRYPOINTの優先関係

参考資料では、ENTRYPOINTとCMDの動作を次の流れで説明しています。

  1. --entrypointが指定されていれば、ENTRYPOINTの内容を置き換える
  2. docker runで引数が指定されていれば、CMDの内容を置き換える
  3. ENTRYPOINTとCMDを組み合わせる
  4. 最終的なコマンドと引数を実行する

これは、主にexec形式を使用した場合の動作を理解するための整理です。

なお、--entrypointによる上書きでは、イメージに定義されていたデフォルトのCMDもクリアされます。

8. CMDとENTRYPOINTはどう使い分ける?

ここまでの内容を踏まえると、次のように使い分けることができます。

CMDを使う場合

コンテナ起動時のデフォルトコマンドを指定し、必要に応じて別のコマンドに変更したい場合に使用します。

例えば、Apacheを起動する場合です。

CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

CMDで指定したコマンドは、docker runの引数で変更できます。

ENTRYPOINTを使う場合

実行するコマンドを決めておき、コンテナ起動時に引数を追加したい場合に使用します。

ENTRYPOINT ["/bin/echo", "Hello"]

この場合、docker runで指定した引数が追加されます。

docker run entrypoint-sample World

実行結果:

Hello World

ENTRYPOINTとCMDを組み合わせる場合

実行するコマンドを決めておき、デフォルト引数を設定したい場合に使用します。

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["Docker"]

引数を指定しなければ、次の結果になります。

Hello Docker

引数を指定した場合は、CMDが置き換えられます。

docker run combined-sample World

実行結果:

Hello World

9. CMDとENTRYPOINTで注意すること

9-1. シェル形式とexec形式を混同しない

本記事で説明したdocker runによる引数の追加や置き換えは、主にexec形式を前提としています。

例えば、次の形式です。

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["Docker"]

一方、シェル形式のENTRYPOINTでは、通常のdocker run引数やCMDが同じようには追加されません。

また、シェル形式はシェルを介してコマンドを実行するため、停止シグナルの処理にも違いがあります。

そのため、ENTRYPOINTとCMDを組み合わせて使用する場合は、exec形式を基本として考えるとよいでしょう。

9-2. CMDとENTRYPOINTの役割を区別する

CMDとENTRYPOINTは、どちらもコンテナ起動時の動作を指定できますが、役割は異なります。

CMDはデフォルトのコマンドや引数を指定します。

ENTRYPOINTは、主にコンテナが実行するコマンドを指定します。

両者を組み合わせる場合は、どの部分を固定し、どの部分を起動時に変更できるようにするかを意識しましょう。

9-3. ENTRYPOINTも変更できる

ENTRYPOINTは通常のdocker run引数では置き換えられませんが、--entrypointオプションによって変更できます。

docker run --entrypoint /bin/echo entrypoint-sample World

ENTRYPOINTは絶対に上書きできないというわけではないため、この点も覚えておきましょう。

10. まとめ

今回は、DockerfileのCMDとENTRYPOINTの違いについて解説しました。

両者はどちらもコンテナ起動時の動作を指定する命令ですが、docker runで引数を指定した場合の動作が異なります。

重要なポイントは次のとおりです。

  • CMDはコンテナ起動時のデフォルトコマンドや引数を指定する
  • ENTRYPOINTはコンテナ起動時に実行するコマンドを指定する
  • CMDだけの場合、docker runの引数でデフォルトコマンドが置き換えられる
  • exec形式のENTRYPOINTだけの場合、docker runの引数が後ろに追加される
  • ENTRYPOINTとCMDを組み合わせると、実行コマンドとデフォルト引数を分けて指定できる
  • --entrypointを使用すると、Dockerfileで指定したENTRYPOINTを変更できる
  • シェル形式とexec形式では、引数や停止シグナルの扱いが異なる

最後に、3つのパターンをまとめます。

指定方法docker runで引数なしdocker runで引数あり
CMDのみCMDを実行CMD全体を置き換える
ENTRYPOINTのみ(exec形式)ENTRYPOINTを実行ENTRYPOINTに引数を追加
ENTRYPOINT+CMD(exec形式)ENTRYPOINT+CMDを実行CMDを指定引数に置き換える

Dockerfileでコンテナの起動処理を記述する際には、CMDとENTRYPOINTの違いを理解して使い分けることが重要です。

まずは本記事のDockerfileを実際にビルドし、docker runの引数を変更したときの動作を確認してみてください。


コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です