DockerfileのCMDをShell形式で記述したらコンテナが停止しなくなってしまったので直した

UbuntuPHP-FPMのイメージを作成する際に、CMDにPHP-FPMの起動コマンドをShell形式で書いたらdocker kill, Ctrl-Cでコンテナが終了しなくなってしまったので調べて修正しました。

FROM ubuntu:20.04

RUN apt update -qq && apt -y upgrade -qqy \
    && DEBIAN_FRONTEND=noninteractive apt -qqy install php \
                                                       php-fpm \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

CMD /usr/sbin/php-fpm7.4 -F -y /etc/php/7.4/fpm/php-fpm.conf

こういう感じでDockerfileを書いていたのですが、

docker run --rm ubuntu-php-fpm

のような形で起動したところ、docker killやCtrl-Cでコンテナが終了しなくなってしまいました。 CentOS7のイメージではうまくいったのに...!?

  • 起動コマンドをShell形式で書いていたこと
  • Ubuntuのイメージを使用していたこと

上記に点が今回の事象原因のようでした。

起動コマンドをShell形式で書いていた

CMD 命令にはShell形式とExec形式があります。 ※今回はENTRYPOINTを使用する例についての話は省略します。

https://docs.docker.jp/engine/reference/builder.html#cmd

今回の例だと、以下がShell形式

CMD /usr/sbin/php-fpm7.4 -F -y /etc/php/7.4/fpm/php-fpm.conf

以下がExec形式となります。

CMD ["/usr/sbin/php-fpm7.4","-F","-y","/etc/php/7.4/fpm/php-fpm.conf"]

Shell形式は内部で /bin/sh -c を使用しています。 そのため、実際に起動するコマンドはこうなります。

/bin/sh -c "/usr/sbin/php-fpm7.4 -F -y /etc/php/7.4/fpm/php-fpm.conf"

docker kill, Ctrl-CはPID1のプロセスにシグナルを送ります。 そのため、ここで受け取るプロセスは/bin/shになります。

Ubuntuのイメージを使用していた

Ubuntu/bin/shdash を参照しています。

root@0bc44db82ffa:/# ls -la /bin/sh
lrwxrwxrwx 1 root root 4 Jul 18  2019 /bin/sh -> dash

そのため、docker killやCtrl-Cはdashに送られます。 このことからわかるように、シェルは子プロセスにシグナル伝搬しないので、PHP-FPMまでシグナルが届かず今回のようにコンテナが終了しないということになります。

なぜCentOSなら終了するのか??

CentOS/bin/shbash を参照しています。

[root@af2a4533ad51 /]# ls -la /bin/sh
lrwxrwxrwx 1 root root 4 Nov 13  2020 /bin/sh -> bash

bashは-cで起動したプロセスにPIDが置き換わります。 今回のケースではPID1になるため、シグナルを受け取ることができコンテナを終了することができていたようでした。

まとめ

CMDで指定するコマンドは推奨されているExec形式を使っていこうず https://docs.docker.jp/engine/reference/builder.html#cmd

参考

https://www.creationline.com/lab/39662 https://qiita.com/ukinau/items/410f56b6d777ad1e4e90