【Docker】DockerfileのCMDとENTRYPOINTを理解する

Docker Docker
Docker

はじめに

こんにちは。

最近、DockerやらAWS Fargateやら、コンテナ関連の構築をすることがあり、Dockerfileを記述することがあったので、改めてDockerfileの”CMD”と”ENTRYPOINT”の使い分けについて調べることがあったので備忘録として残します。

Dockerfile

Dockerfileとは、Dockerイメージを作制するための命令を記述するファイルになります。

そのDockerfileに記述する命令の中でも、最も重要な命令がCMD命令とENTRYPOINT命令になるかと思います。

これら2つの命令は「コンテナとしてどう振る舞うか」、コンテナの役割そのものを決める重要な命令になります。

具体的にはコンテナで稼働させるサービスを起動させる命令などになります。

CMD命令とENTRYPOINT命令の役割と2つの違い

早速ですが結論です。

CMD命令の役割

CMD命令は、対象のコンテナ実行時にデフォルトで実行したい命令を記述するために用います。
この役割はCMD命令が持つ、docker runコマンドの引数による上書き特性からきています。

また、docker docsでは下記のように説明されています

The main purpose of a CMD is to provide defaults for an executing container. These defaults can include an executable, or they can omit the executable, in which case you must specify an ENTRYPOINT instruction as well.

CMD命令の主たる目的は実行対象のコンテナにデフォルトの命令を与えることである。それらのデフォルト命令は実行処理も含み、また一方でENTRYPOINT命令も共に記述すること実行処理を省略することも可能になる。

docker docsから抜粋

CMD命令はCMD命令自身でコンテナへの任意の実行命令を送ることもでき、またデフォルトの命令であるためコンテナ起動時の引数として命令を上書きすることもできます。

例えばGunicornサーバを用いてFlaskなどのアプリケーションサーバをコンテナで起動する場面を考えてみよう。

前提として、Gunicornはそのコマンドオプションとしてバインド設定、スレッド数やログ出力設定などを設定できます。

この時、デフォルトのイメージとしてはGunicornを何も設定しないイメージをDockerfileで準備し、コンテナ起動時に上書きでコマンドオプションを付け加えるような書き方を考えているとしよう。

ENTRYPOINT命令は使わず、CMD命令のみで記述し、コマンド全体を上書きさせるDockerfileの記載例は下記のようになる。

FROM python:3.9

WORKDIR /app
~~省略~~
CMD ["gunicorn", "app:app", "-bind=0.0.0.0:5000"]

上記のDockerfileに対してDocker起動時に上書きをせずに実行すると上記の設定でGunicornが起動するが、下記のようにDocker実行時に上書きをすると上書き後の命令が実行される。

$ docker run -it -d <GUNICORN_IMAGE> gunicorn app:app --bind=0.0.0.0:5000 --threads=2 

このように1docker runコマンドの後に引数として渡してあげることでCMD命令を上書きできる。
(この例はあまり良くないので、、CMDの単純な上書きの例としてのみとらえてください)

 

ENTRYPOINT命令の役割

ENTRYPOINT命令はDocker構築時に”必ず”実行する命令を記述するときに用います。

ENTRYPOINT命令もdocker runコマンドから上書きが可能であるが、CMD命令のように引数として指定する形式ではなく、”–entrypoint”オプションを付けることにより上書きが可能になる。

CMD命令、ENTRYPOINT命令ともに上書きは可能であるが、上書きの容易性が変わってくる。

そのため、僕自身はCMD命令とENTRYPOINT命令の役割分担として、「デフォルトで実行する処理」と「必ず実行する処理」、このように役割分担している。

docker docsでは下記のように説明されています。

An ENTRYPOINT allows you to configure a container that will run as an executable.

For example, the following starts nginx with its default content, listening on port 80:

$ docker run -i -t --rm -p 80:80 nginx

Command line arguments to docker run <image> will be appended after all elements in an exec form ENTRYPOINT, and will override all elements specified using CMD. This allows arguments to be passed to the entry point, i.e., docker run <image> -d will pass the -d argument to the entry point. You can override the ENTRYPOINT instruction using the docker run --entrypoint flag.

ENTRYPOINT命令は実行可能命令を走らせるコンテナを構築することを許可する。

例えば、この後のデフォルトの処理内容としてNGINXを80番ポートで開始するように。

“docker run <image>”のコマンドライン引数は、exec型のENTRYPOINT命令の全ての要素が後ろに追加される。またCMDを使って記述された全ての要素を上書きする。これにより引数がENTRYPOINT命令に渡るようになる。”<docker run <image> -d”コマンドは-dオプションの引数をENTRYPOINT命令に渡す。あなたは”<docker run —entrypoint>フラグを使ってENTRYPOINT命令を上書きすることができる。

docker docsから抜粋

少しわかりにくいですが、 要はENTRYPOINT命令はCMD命令よりも優先され、ENTRYPOINT命令が記述されていればdocker runコマンドの後ろに優先的に追加されて実行される、と言うことです。

つまり確実に実行させたい処理命令がある場合はENTRYPOINT命令に記述しましょう、と言うことになります。

CMD命令とENTRYPOINT命令の併用

CMD命令とENTRYPOINT命令の書き方

まず、併用の話に移る前にCMD命令とENTRYPOINT命令のそれぞれについて書き方を見ていきましょう。(後の話で必要になります)

CMD命令の書き方

●exec型

CMD ["executable", "param1", "param2"]

exec型はJSON形式で実行コマンドやパラメータを書いていく記載方法です。

default params型(特殊でENTRYPOINTと併用)

CMD ["param1", "param2"]

CMD命令とENTRYPOINT命令の併用を考える上で、記述方法を先に説明した理由がここにあります。

CMD命令はENTRYPOINT命令のデフォルトパラメータとして利用できるのです。

一つ注意点としてはJSON型であると言うこと。

●shell型

CMD command param1 param2

shell型はshellで実行するコマンドをそのまま書くイメージで記載します。

要するに、コマンドの頭に自動で”/bin/sh -c”コマンドがつくイメージです。

ENTRYPOINT命令の書き方

●exec型

ENTRYPOINT ["executable", "param1", "param2"]

exec型はCMDと同じく、JSON形式で実行コマンドやパラメータを書いていく記載方法です。

●shell型

ENTRYPOINT command param1 param2

shell型はCMDと同じく、shellで実行するコマンドをそのまま書くイメージで記載します。

CMD命令とENTRYPOINT命令を併用する例

それではこの章の本題として、CMD命令とENTRYPOINT命令の併用を考えていきましょう。

先ほどのCMD命令の記述で2番目の記述方法を使います。

合わせるとこんな感じ。

# コンテナ実行時に必ず実行される処理
ENTRYPOINT ["executable", "param1"]
# コンテナ実行時にデフォルトで実行される処理
CMD ["param2", "param3"]

この場合、”executable param1 param2 param2″と言う処理が実行される。

これを具体的な例で示すと、以前例にあげたGunicornで試してみます。

# コンテナ実行時に必ず実行される処理
ENTRYPOINT ["gunicorn", "app:app"]
# コンテナ実行時にデフォルトで実行される処理
CMD ["--bind=0.0.0.0:5000", "--threads=2"]

これをコンテナ実行時に引数なしで実行すると下記コマンドが実行されることになります。

$ gunicorn app:app --bind=0.0.0.0:5000 --threads=2

これを上書きするとどうなるでしょうか。

例えば下記のように、コンテナ実行コマンドに引数を渡して実行するとします。

$ docker run -it -d <image_name> --bind=0.0.0.0:8080

この場合、”–bind=0.0.0.0:5000 –threads=2″が上書きされ,

$ gunicorn app:app --bind=0.0.0.0:8080

上記のコマンドがコンテナ実行時に注入されることになります。

CMD命令とENTRYPOINT命令の併用の参考

下記の表はdocker docsで公式が公表しているわかりやすい併用時の例になるので参考にしてください。

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

まとめ

DockerのCMD命令をENTRYPOINT命令についてご紹介しました。

実はCMDとENTRYPOINTは同じような動きに見えて似ても似つかない処理をする面白い特性があることがわかりました。

もしかするとこの2つの命令を比べることすら間違いを引き起こしやすい原因になるのでは、とも思いますが、今後CMDとENTRYPOINTの使い分け、併用を自分のものにし、素晴らしいDockerライフを送りたいものです。

何かありましたらお問合せページお問い合わせからご連絡いただけますと幸いです。

それでは良いDockerライフを!

  • CMDもENTRYPOINTもコンテナ実行(docker run)時に実行される命令
  • どちらもコンテナ実行(docker run)時に上書きができるが、ENTRYPOINT命令は直接上書きができず、–entrypointオプションが必要。
  • ENTRYPOINTの方が優先的に実行される
  • CMDとENTRYPOINTは併用でき、組み合わせることでコンテナ実行時の命令注入が便利になる
タイトルとURLをコピーしました