docker stop が投げるSIGTERMをtrapできないケース?

docker stop のSIGTERMの動作を調べるなかで理解できないケースがあり、相談させていただきたいです。

PID1 の bash プロセスを立ち上げ、

$ docker run --rm -it ubuntu:20.04 /bin/bash
root@552f11e3e2ce:/# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.3  0.0   4116  3336 pts/0    Ss   03:02   0:00 /bin/bash
root           9  0.0  0.0   5900  2892 pts/0    R+   03:02   0:00 ps aux

以下のようにシグナルをトラップするようにした場合は、トラップが動作するのですが、

$ docker run --rm -it ubuntu:20.04 /bin/bash
root@141a8eced1bb:/# trap 'printf "TERM\n";' TERM && trap 'printf "EXIT\n";' EXIT
# 別コンソールで docker stop [conainer name]
root@141a8eced1bb:/# exit
TERM # TERMのトラップが動作
EXIT # EXITのトラップが動作

以下のようにシグナルをトラップするようにした場合は、トラップが動作しません。

$ docker run --rm -it ubuntu:20.04 /bin/bash
root@a8585f8f7b58:/# trap 'printf "TERM\n";' TERM
# 別コンソールで docker stop [conainer name]                                
root@a8585f8f7b58:/# exit

これはどうして動作しないのでしょうか?

「いいね!」 2

@pojiro さん、はじめまして!

docker stop の TERM を使った処理は、通常のシェルで期待する動作にはならないようです。

理由としては、以下のページによりますと、PID 1 のプロセスは、コンテナ内でも Linux カーネルの特別な処理がされるため、TERM が機能しない場合がある、書かれています。

そのため、コンテナ内の bash で trap 'printf "TERM\n";' TERM を実行していても、 docker stop を実行しても何も処理されていないように見えます。

TERM をトラップするのが目的であれば、次のようにされてみてはいかがでしょうか。

docker run -t ubuntu:20.04 bash -c "trap 'printf \"TERM\n\";' TERM; read"

docker stop を実行すると、docker run を実行したコマンドライン上で TERM の文字が見えると思います。

ちなみに、公式ドキュメントにもありますように、

ENTRYPOINT スクリプトが Unix シグナルを受信し、続いて、他の処理を行うようにする必要があります。

停止時に様々な総理をするには、Dockerfile の ENTRYPOINT で何らかの処理をするスクリプトを書く必要がある、とあります。

そこで、もしかすると、処理によっては、以下にあるようなスクリプトを組み合わせると、ご希望通りの動作になるかと思います。

「いいね!」 1

@zembutsu さん、はじめまして、ご回答・dumb-init の紹介ありがとうございます!

回答いただいたキーワードから、docker run の --init オプション、そのオプションで使用されている tini を調べることができました!

ただ、今、私の関心は「 pidが1であっても明示的に trap を実装すればdocker stopの SIGTERM を捕捉できるはず」というところに有ります。

そこで、ご回答いただいたキーワード用いることで以下のページを見つけることができました。

https://raby.sh/sigterm-and-pid-1-why-does-a-container-linger-after-receiving-a-sigterm.html

そして、ページ内の /proc/<pid>/statusを解析するスクリプトを、先に提示したコンテナの起動直後のbash(trapコマンドは未実行)で実行したところ以下を得ました。

root@f602223fbcc2:~# cat > hoge.sh <<"END"
> #!/bin/bash
> # https://stackoverflow.com/a/61365083
>
> pid=${1:?Missing pid}
> cat /proc/$pid/status|egrep '(Sig|Shd)(Pnd|Blk|Ign|Cgt)'|while read name mask;do
>     bin=$(echo "ibase=16; obase=2; ${mask^^*}"|bc)
>     echo -n "$name $mask $bin "
>     i=1
>     while [[ $bin -ne 0 ]];do
>         if [[ ${bin:(-1)} -eq 1 ]];then
>             kill -l $i | tr '\n' ' '
>         fi
>         bin=${bin::-1}
>         set $((i++))
>     done
>     echo
> done
> END

# 実行にはbc が必要だったので、別途 apt install しています
root@f602223fbcc2:~# /bin/bash hoge.sh 1 
SigPnd: 0000000000000000 0
ShdPnd: 0000000000000000 0
SigBlk: 0000000000010000 10000000000000000 CHLD
SigIgn: 0000000000380004 1110000000000000000100 QUIT TSTP TTIN TTOU
SigCgt: 000000004b817efb 1001011100000010111111011111011 HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM CHLD XCPU XFSZ VTALRM WINCH SYS

(trapを未実行であるにも関わらず)SigCgtにすでに TERM が表示されています。現時点では、これが関係するのかなぁと想像しています。

まだまだ分からなそうですが、進展有りましたらここに追記していこうと思います。
とりいそぎ、以上です。

「いいね!」 3

@pojiro さん、情報ありがとうございます!

こちらのページの情報は知らなかったので、スクリプトとあわせて確認してみますね。

是非また何か分かりましたら共有いただけると、他の方のご参考にもなると思います :bowing_man:

「いいね!」 1