Docker では、ENTRYPOINT と CMD が Dockerfile内で使用され、両方ともコンテナー内で実行するコマンドを定義します。 コンテナ起動の際に指定したコマンドがコンテナ内で実行されることになります。
どちらも同じ用途で使われてるようですが、それぞれの違いは何だろうかこちら記事で明確にしようと思います。
ENTRYPOINT
ENTRYPOINTは、コンテナが起動されるときに実行されるコマンドを指定します。このコマンドは、docker runで--entrypointフラグを使用しない限り、実行時に上書きすることはできません。
ENTRYPOINTのベストな使い方は、イメージにおけるメインコマンドの設定することです。
# Dockerfile
FROM ubuntu
ENTRYPOINT ["echo", "Hello"]
このイメージからコンテナを実行すると、echo Hello
コマンドが実行されます。
$ docker build . -t test_image
$ docker run test_image
# Output:
Hello
CMD
一方、CMDはコンテナのデフォルトのコマンドまたはパラメータを設定します。ENTRYPOINTとは異なり、CMDが定義するコマンドは引数としてdocker run
に渡すことで上書きすることができます。
# Dockerfile
FROM ubuntu
CMD ["echo", "World"]
こちらで、CMDしか利用されない場合、CMDが指定したコマンドはデフォルトコマンドとして実行されます。逆に、docker runに引数を渡すなら、このコマンドが上書きされる。
$ docker build . -t test_image
$ docker run test_image
# Output:
World
$ docker run test_image echo Universe
# Output:
Universe
ENTRYPOINTとCMDが結合される際に、CMDが指定しているものがENTRYPOINTのコマンドのデフォルト引数となります。
下記の定義はコンテナが起動されると、echo Hello Worldが実行されます。
ENTRYPOINTとCMDの組み合わせ:
# Dockerfile
FROM ubuntu
ENTRYPOINT ["echo", "Hello"]
CMD ["World"]
$ docker build . -t test_image
$ docker run test_image
# Output:
Hello World
こちらの場面で、このイメージからコンテナを実行し(docker run)、Goodbyeのような引数を指定すると、コマンドはecho Hello Goodbyeになります。
$ docker build . -t test_image
$ docker run test_image Goodbye
# Output:
Hello Goodbye
問題になるのは
ENTRYPOINTとCMDが結合される際に、CMDが指定しているコマンドを独自のコマンドとして実行したいときに、どうすれば良いでしょうか。
例えば、こちらのDockerfileのCMDにコマンドを定義したいですが、docker runを実行すると、ENTRYPOINTが指定しているEchoコマンドの引数になってしまいます。
ENTRYPOINTとCMDの組み合わせ:
# Dockerfile
FROM ubuntu
ENTRYPOINT ["echo", "Hello"]
CMD ["echo", "World"]
コンテナを起動するとき、echo Hello echo World
コマンドが実行されてしまいます。
$ docker build . -t test_image
$ docker run test_image
# Output:
Hello echo World
対策
CMDが指定するコマンドがENTRYPOINTのコマンドの引数に追加されることになっている前提で、
もし、ENTRYPOINTが指定するコマンドは引数部分を子コマンドのように実行できれば解決できます。
カスタマイズなbashファイルを作成。ENTRYPOINTのコマンドに入れる。
- entrypoint.sh file
#!/bin/sh
echo "Hello"
exec "$@" # 何かのコマンドをBashシェルスクリプトの最後で実行したいとき、execを利用する。
- Dockerfile
From ubuntu
COPY . .
RUN chmod +x entrypoint.sh # 実行権限を付与
ENTRYPOINT ["./entrypoint.sh"]
CMD ["echo", "World"]
$ docker build . -t test_image
$ docker run test_image
# Output:
Hello
World
なぜか、こちらの結果が出てくるか調べてみます。
ENTRYPOINTとCMDの結合により、上記にDockerfileでイメージをdocker run
を実行すると、./entrypoint.sh echo World
のコマンドが実行されます。
そちらのコマンドの引数はentrypoint.shのスクリプト内で読み込める変数はこちら表となります。
$0 : entrypoint.sh
$1 : echo
$2 : World
$@ : echo World
なので、entrypoint.shのフィアルの最後に実行するexec "$@"
はecho World
コマンドとなります。
echo "Hello"とecho "World"と合わせて、"Hello World"の結果を出力しました。
こちらで、Dockerfile内のENTRYPOINTとCMDの違いについて、ある程度理解できたとおもいます。ご質問やご不明な点がございましたら、お気軽にコメントください。