fbpx

DockerfileのCMDとENTRYPOINTを読み解く(2/3) — CMD命令とENTRYPOINT命令の基礎 #docker #dockerfile

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

New call-to-action

Dockerイメージを作成するためのDockerfileには多くの命令があります。中でもCMD命令とENTRYPOINT命令は、コンテナ内で実行するPID 1のプロセスを指定するものです。言いかえると「このイメージは一体何をするのか、何のためのイメージなのか」という性質を決定づける命令です。しかしこの両命令にはいろいろな特徴があり、理解するには一筋縄ではいきません。

このCMD命令とENTRYPOINT命令についての理解を深めるため、全3回のブログシリーズとして掲載していきます。なお、Linux版Dockerコンテナの内容であることをご了承ください。

本稿ではCMD命令とENTRYPOINT命令の基礎について見ていきます。

はじめに

冒頭で述べた通り、CMD命令とENTRYPOINT命令は、コンテナ内で実行するPID 1のプロセスを指定するものです。言いかえると「このイメージは一体何をするのか、何のためのイメージなのか」という性質を決定づける命令です。

CMD命令とENTRYPOINT命令をそれぞれ単独で利用する場合は、どちらもコンテナ内で実行するPID 1のプロセスを指定することになります。では、それぞれを見ていきましょう。

CMD命令の基礎

次のDockerfileがあるとします。

FROM debian:9
CMD ping

これから生成したイメージを「myimage」とします。これを「docker container run」で実行すると次のようになります。

% docker container run myimage
ping: missing host operand
Try 'ping --help' or 'ping --usage' for more information.

CMD命令で指定した「ping」コマンドを実行しています。pingコマンドに対象とするホストまたはIPアドレスを指定していないため「missing host operand」というエラーとなっています。正確には、対象とするホストまたはIPアドレスを指定【できません】。

直感的に「docker container run myimage 8.8.8.8」としてみましょう。

% docker container run myimage 8.8.8.8
docker: Error response from daemon: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "8.8.8.8": executable file not found in $PATH: unknown.

このように「docker container run [イメージ名] [引数]」とすると、「ping 8.8.8.8」のようにpingコマンドに8.8.8.8という引数をつけて実行するのではなく、「ping」の代わりに「8.8.8.8」という存在しないコマンドを実行しようとしてエラーとなってしまいます。

ということは「docker container run myimage date」とすると、

% docker container run myimage date
Tue Dec 15 07:50:55 UTC 2020

このように「ping」の代わりに「date」を実行しています。

CMD命令のまとめです。

  • コンテナ内のPID 1として実行するプロセスのコマンドを指定できる
  • そのコマンドは「docker container run [イメージ名] [引数]」の引数で上書きできる

ENTRYPOINT命令の基礎

次のDockerfileがあるとします。

FROM debian:9
ENTRYPOINT ["ping"]

これから生成したイメージを「myimage」とします(※Exec形式で記述している理由は後述します)。これを「docker container run」で実行すると次のようになります。

% docker container run myimage
ping: missing host operand
Try 'ping --help' or 'ping --usage' for more information.

CMD命令と同じく、ENTRYPOINT命令で指定した「ping」コマンドを実行しています。pingコマンドに対象とするホストまたはIPアドレスを指定していないため「missing host operand」というエラーとなっています。

では、対象とするホストまたはIPアドレスを指定するには? 直感的に「docker container run myimage 8.8.8.8」としてみましょう。

% docker container run myimage 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=117 time=10.388 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=9.255 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=9.620 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=117 time=9.950 ms
^C--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 9.255/9.803/10.388/0.418 ms

このように「docker container run [イメージ名] [引数]」とすると、「ping 8.8.8.8」のようにpingコマンドに8.8.8.8という引数をつけて実行しています。これはCMD命令との大きな違いです。

ただし、引数を渡すことができるのはENTRYPOINT命令をExec形式で記述しているときのみです。Shell形式の場合は渡すことができません。

FROM debian:9
ENTRYPOINT ping

ENTRYPOINT命令をShell形式に変えたDockerfileから生成したイメージで引数を渡してみましょう。

% docker container run myimage 8.8.8.8
ping: missing host operand
Try 'ping --help' or 'ping --usage' for more information.

このように対象のIPアドレス「8.8.8.8」を引数として渡しているのに、「ping」コマンドは対象とするホストまたはIPアドレスを指定していない「missing host operand」というエラーとなっています。すなわち、引数を渡せていないことを意味します。これがENTRYPOINT命令をExec形式で記述している理由です。

さて、ENTRYPOINT命令で指定したコマンドを上書きするにはどうしたらよいでしょうか。「docker container run」の「--entrypoint」オプションで上書きすることができます。例えば次のDockerfileから作成したイメージ「myimage」の「ENTRYPOINT ["ping"]」を上書きしてみましょう。

FROM debian:9
ENTRYPOINT ["ping"]
% docker container run --entrypoint date myimage
Tue Dec 15 08:02:07 UTC 2020

このように、CMD命令と同じ「docker container run [イメージ名] [引数]」では「ping」が実行されてしまいますので、「--entrypoint」オプションを用いて「ping」の代わりに「date」を実行しています。

ENTRYPOINT命令のまとめです。

  • コンテナ内のPID 1として実行するプロセスのコマンドを指定できる
  • Exec形式で記述した場合のみ、そのコマンドに「docker container run [イメージ名] [引数]」の形式で引数を渡せる
    • Shell形式で記述した場合、「docker container run [イメージ名] [引数]」の引数を無視する
  • コマンドを上書きする場合は「--entrypoint」オプションを用いる

CMD命令とENTRYPOINT命令の組み合わせの基礎

ここからは、CMD命令とENTRYPOINT命令を組み合わせてみましょう。これら2つの命令を同時に指定すると、コンテナ内で実行するPID 1のプロセスはENTRYPOINT命令で指定したコマンドとなり、CMD命令はそのコマンドのデフォルトの引数となります。これが理解を少々難しくしていますので、例を追って見ていきましょう。

次のDockerfileからイメージを生成し「myimage」とします。

FROM debian:9
CMD ["8.8.8.8"]
ENTRYPOINT ["ping"]

これを「docker container run」でそのまま実行します。

% docker container run myimage
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=117 time=10.545 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=9.344 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=9.917 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=117 time=9.104 ms
^C--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 9.104/9.727/10.545/0.557 ms

このように、ENTRYPOINT命令の「ping」コマンドにCMD命令の「8.8.8.8」を引数とした形で実行されました。「コンテナ内で実行するPID 1のプロセスはENTRYPOINT命令で指定したコマンドとなり、CMD命令はそのコマンドのデフォルトの引数」とは、このことを意味します。

さらに「CMD命令はそのコマンドの【デフォルトの】引数」となっていることに注目しましょう。「デフォルト」ということは別の物に差し替えできるはずです。ここで、CMD命令は「docker container run [イメージ名] [引数]」の形式で上書きできたことを思い出しましょう。この形式でCMD命令を上書きしてみます。

% docker container run myimage 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: icmp_seq=0 ttl=55 time=13.624 ms
64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=9.993 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=55 time=9.648 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=9.562 ms
^C--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 9.562/10.707/13.624/1.692 ms

このように、CMD命令のデフォルトの「8.8.8.8」を「docker container run [イメージ名] [引数]」の形式で「1.1.1.1」に上書きできました。

さらに「--entrypoint」オプションでENTRYPOINT命令を上書きしてみましょう。

% docker container run --entrypoint date myimage
Wed Dec 16 08:54:50 UTC 2020

「--entrypoint」オプションでENTRYPOINT命令を上書きした場合、CMD命令は無視されていることに注目しましょう。CMD命令のデフォルトの「8.8.8.8」を明示的に引数として指定すると次のようにエラーとなることからも無視されていることがわかります。

% docker container run --entrypoint date myimage 8.8.8.8
date: invalid date '8.8.8.8'

なお、ENTRYPOINT命令をExec形式で記述した場合は、CMD命令もExec形式で記述しなければいけないことに注意してください。次のように交ぜ書きすると、

FROM debian:9
CMD 8.8.8.8
ENTRYPOINT ["ping"]
% docker container run myimage
ping: invalid value (`8.8.8.8' near `.8.8.8')

おかしなエラーとなります。

なお、ENTRYPOINT命令の基礎で見た通り、ENTRYPOINT命令をShell形式で記述した場合はCMD命令はShell形式・Exec形式のどちらで書かれていても存在自体無視します。

FROMM debian:9
CMD ["8.8.8.8"]
ENTRYPOINT ping
% docker container run myimage
ping: missing host operand
Try 'ping --help' or 'ping --usage' for more information.
FROMM debian:9
CMD ["8.8.8.8"]
ENTRYPOINT ping
% docker container run myimage
ping: missing host operand
Try 'ping --help' or 'ping --usage' for more information.

CMD命令とENTRYPOINT命令の組み合わせのまとめです。

  • ENTRYPOINT命令は、コンテナ内で実行するPID 1のプロセスとなるコマンドを指定できる
  • CMD命令は、そのコマンドのデフォルトの引数を指定できる
  • CMD命令は、「docker container run [イメージ名] [引数]」の形式で上書きできる
  • ENTRYPOINT命令は、「--entrypoint」オプションで上書きでき、その際CMD命令は無視される
  • ENTRYPOINT命令をExec形式で記述した場合、CMD命令もExec形式で記述しなければならない
  • ENTRYPOINT命令をShell形式で記述した場合、CMD命令も「docker container run [イメージ名] [引数]」の引数も無視する

CMD命令とENTRYPOINT命令の基礎のまとめ

再びCMD命令とENTRYPOINT命令の基礎のまとめです。

  • CMD命令
    • コンテナ内のPID 1として実行するプロセスのコマンドを指定できる
    • そのコマンドは「docker container run [イメージ名] [引数]」の引数で上書きできる
  • ENTRYPOINT命令
    • コンテナ内のPID 1として実行するプロセスのコマンドを指定できる
    • Exec形式で記述した場合のみ、そのコマンドに「docker container run [イメージ名] [引数]」の形式で引数を渡せる
      • Shell形式で記述した場合、「docker container run [イメージ名] [引数]」の引数を無視する
    • コマンドを上書きする場合は「--entrypoint」オプションを用いる
  • CMD命令とENTRYPOINT命令の組み合わせ
    • ENTRYPOINT命令は、コンテナ内で実行するPID 1のプロセスとなるコマンドを指定できる
    • CMD命令は、そのコマンドのデフォルトの引数を指定できる
    • CMD命令は、「docker container run [イメージ名] [引数]」の形式で上書きできる
    • ENTRYPOINT命令は、「--entrypoint」オプションで上書きでき、その際CMD命令は無視される
    • ENTRYPOINT命令をExec形式で記述した場合、CMD命令もExec形式で記述しなければならない
    • ENTRYPOINT命令をShell形式で記述した場合、CMD命令も「docker container run [イメージ名] [引数]」の引数も無視する

CMD命令とENTRYPOINT命令の基礎を見た次は、すべてをまとめたCMD/ENTRYPOINT/Shell/Exec一覧表について見ていきましょう。

また、クリエーションラインではMirantis社公認Dockerトレーニングを提供しております。さらにDockerを習得するために本トレーニングの受講を是非ご検討ください。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA