JSONArgsRecommended

出力

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

(訳: ENTRYPOINT/CMD に対する JSON 引数は OS シグナルを起こすような意図しない動作は避けるべきです)

内容説明

ENTRYPOINT および CMD は、引数に関して 2 つの文法をサポートしています。

  • シェル形式: CMD my-cmd start
  • exec 形式: CMD ["my-cmd", "start"]

シェル形式を用いた場合、実行モジュールはシェルに対する子プロセスとして実行されます。 これはシグナルを送信しません。 これが何を意味するかと言えば、コンテナー内において実行されるプログラムが SIGTERMSIGKILL といった OS シグナルを検出できないため、それらに適切に対処できないということです。

❌ 不可: ENTRYPOINT コマンドは OS シグナルを受け取りません。

FROM alpine
ENTRYPOINT my-program start
# entrypoint は /bin/sh -c my-program start となります

実行ファイルが OS シグナルを受け取れるようにするには、CMDENTRYPOINT では exec 形式を使ってください。 これはコンテナー内においてメインプロセス (PID 1) として、シェルの親プロセスをうまく避けながら実行ファイルを実行します。

✅ 可: ENTRYPOINT は OS シグナルを受け取ります。

FROM alpine
ENTRYPOINT ["my-program", "start"]
# entrypoint は my-program start となります

PID 1 としてプログラム起動を行うということは、Linux 上の PID 1 に求められるそれ相応の責任と動作が求められます。 たとえば子プロセスの終了を検出することなどです。

回避策

シェルの元でコンテナーを実行したいというケースがあるかもしれません。 exec 形式を用いた場合、シェル機能として変数展開、パイプ (|)、コマンド連結 (&&, ||, ;) は利用できません。 そういった機能を利用したいならシェル形式を用いなければなりません。

以下に示すのはそれを実現する方法です。 これは、シェルの子プロセスとして実行ファイルを実行することに変わりはありません。

ラッパースクリプトの生成

エントリーポイント用のスクリプトを用意して、起動コマンドをラップするようにします。 そしてそのスクリプトを実行しつつ ENTRYPOINT コマンドは JSON 形式とします。

✅ 可: ENTRYPOINT は JSON 形式を使っています。

FROM alpine
RUN apk add bash
COPY --chmod=755 <<EOT /entrypoint.sh
#!/usr/bin/env bash
set -e
my-background-process &
my-program start
EOT
ENTRYPOINT ["/entrypoint.sh"]

シェルの明示的な指定

Dockerfile の命令として SHELL があります。 利用したいシェルを明示的に指定するものです。 SHELL 命令を利用するということは、シェル形式を意図的に利用することを表しているため、警告メッセージは出力されなくなります。

✅ 可: シェルが明示的に定義されています。

FROM alpine
RUN apk add bash
SHELL ["/bin/bash", "-c"]
ENTRYPOINT echo "hello world"