DockerfileのCMDとENTRYPOINTを読み解く(2/3) — CMD命令とENTRYPOINT命令の基礎 #docker #dockerfile
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
Dockerイメージを作成するためのDockerfileには多くの命令があります。中でもCMD命令とENTRYPOINT命令は、コンテナ内で実行するPID 1のプロセスを指定するものです。言いかえると「このイメージは一体何をするのか、何のためのイメージなのか」という性質を決定づける命令です。しかしこの両命令にはいろいろな特徴があり、理解するには一筋縄ではいきません。
このCMD命令とENTRYPOINT命令についての理解を深めるため、全3回のブログシリーズとして掲載していきます。なお、Linux版Dockerコンテナの内容であることをご了承ください。
- 第1回: Shell形式とExec形式とは何か
- 第2回: CMD命令とENTRYPOINT命令の基礎 ← 本稿
- 第3回: CMD/ENTRYPOINT/Shell/Exec一覧表
本稿では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一覧表について見ていきましょう。
- 第1回: Shell形式とExec形式とは何か
- 第2回: CMD命令とENTRYPOINT命令の基礎
- 第3回: CMD/ENTRYPOINT/Shell/Exec一覧表 ← 次稿
また、クリエーションラインではMirantis社公認Dockerトレーニングを提供しております。さらにDockerを習得するために本トレーニングの受講を是非ご検討ください。