はじめに
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 Hello | Hello |
docker run cmd-sample /bin/echo World | /bin/echo World | World |
このように、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 Hello | Hello |
docker run entrypoint-sample World | /bin/echo Hello World | Hello 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 Docker | Hello Docker |
docker run combined-sample World | /bin/echo Hello World | Hello 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+CMD | World | /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の動作を次の流れで説明しています。
--entrypointが指定されていれば、ENTRYPOINTの内容を置き換えるdocker runで引数が指定されていれば、CMDの内容を置き換えるENTRYPOINTとCMDを組み合わせる- 最終的なコマンドと引数を実行する
これは、主に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の引数を変更したときの動作を確認してみてください。