nikkie-ftnextの日記

イベントレポートや読書メモを発信

Dockerfile には shell form と exec form があったのです

はじめに

七尾百合子さん、お誕生日 276日目 おめでとうございます! nikkieです。

Dockerfile についての Today(※最近) I Learned です。

目次

Dockerfile reference 「Shell and exec form」

https://docs.docker.com/reference/dockerfile/#shell-and-exec-form

The RUN, CMD, and ENTRYPOINT instructions all have two possible forms:

Dockerfile のRUNCMDENTRYPOINT命令には2つの形式があります。

1つは exec form
https://docs.docker.com/reference/dockerfile/#exec-form

ENTRYPOINT ["/bin/bash", "-c", "echo hello"]

もう1つは shell form
https://docs.docker.com/reference/dockerfile/#shell-form

RUN source $HOME/.bashrc && echo $HOME

ENTRYPOINT命令やRUN命令は form が違うだけということを認識できていませんでした。

実はどちらの form でも書けたのです

各命令はどちらの form でも書けます。

RUN

https://docs.docker.com/reference/dockerfile/#run

# Shell form:
RUN [OPTIONS] <command> ...
# Exec form:
RUN [OPTIONS] [ "<command>", ... ]

shell form の方ではヒアドキュメントがサポートされています(&&つなぎからの解放!)

CMD

https://docs.docker.com/reference/dockerfile/#cmd

# Shell form:
CMD command param1 param2

# Exec form:
CMD ["executable","param1","param2"]
# Exec form, as default parameters to ENTRYPOINT
CMD ["param1","param2"]

ENTRYPOINT

https://docs.docker.com/reference/dockerfile/#entrypoint

# Shell form:
ENTRYPOINT command param1 param2
# Exec form:
ENTRYPOINT ["executable", "param1", "param2"]

2つの form は何が違うのか?ENTRYPOINTを例に

shell form

https://docs.docker.com/reference/dockerfile/#entrypoint

It(*The shell form of ENTRYPOINT) also starts your ENTRYPOINT as a subcommand of /bin/sh -c, which does not pass signals.

ENTRYPOINT command param1 param2/bin/sh -c "command param1 param2"と実行されるということです。
このとき command にシグナルは渡されません

別の説明としては1
https://docs.docker.com/reference/dockerfile/#shell-form-entrypoint-example

You can specify a plain string for the ENTRYPOINT and it will execute in /bin/sh -c

exec form

https://docs.docker.com/reference/dockerfile/#exec-form-entrypoint-example

executable がコンテナで唯一のプロセスになります。

The following Dockerfile shows using the ENTRYPOINT to run Apache in the foreground (i.e., as PID 1):

違いを分かってなかった私はやらかしていた

2つの form について Dockerfile のリファレンスは読み切れてはいないのですが、過去に書いた Dockerfile を変えたいなと思いました。

書き換えたい箇所

CMD ["python", "main.py"]

こうしたい

ENTRYPOINT ["python", "main.py"]

(リファレンスを読んだ範囲でCMD ["python", "main.py"]で PID 1 になるかが分からなかったので、ENTRYPOINTにしたいです)

また別で以下のようにしていた箇所があり、リファレンスを読んだ今ではこれは完全にあちゃーでした。

CMD ["/bin/sh", "-c", "python main.py"]

これではpythonコマンドにシグナルが渡りません!
ENTRYPOINTでやっていたら実に残念なことになってましたね

終わりに

Dockerfile に exec form と shell form があることを知りました。

  • RUNCMDENTRYPOINT命令はそれぞれ、exec form と shell form の2通りで書ける
  • ENTRYPOINT命令 + exec form で、PID 1 のプロセスとなる
  • ENTRYPOINT命令 + shell form は/bin/sh -cで実行される。そのためシグナルが渡されない

過去に私が書いた際は生成 AI に書かせたのかもしれませんが、Dockerfile の知識がなくて墓穴を掘っていることに気づけていませんでした。
ENTRYPOINT命令 + exec form なのに /bin/sh -c を使用することは実質 shell formであり、exec formのよさを消す大悪手だと思います。
俺みたいになるな!


  1. execコマンドを使った例になっています