これを見ていて思ったネタ(?)記事です。実用性は全然ない話になります。PID 1だとSIGTERMを無視するというLinuxのPID 1問題を解決したいという話。--init
をつけることでENTRYPOINTに指定したコマンドがTiniという軽量 initを間に挟むことでPID 1を回避しつつtiniがシグナルをハンドリングしてくれるというもの。
ここで問題になるのはtini配下に親プロセスが1つあってその下にさらに子プロセスが連なっているみたいなケースでtiniは子プロセス(parent.sh)にSIGTERMを送りparent.shで適切に処理していないとプロセスが終了しそのまま終了処理となります。(1コンテナ複数プロセスで何かしたいみたいなケースって実際どれくらいあるのだろうか)
bash-5.1# ps aux PID USER TIME COMMAND 1 root 0:00 /sbin/docker-init -- parent.sh 8 root 0:00 {parent.sh} /usr/local/bin/bash /usr/local/bin/parent.sh 10 root 0:13 {child.sh} /usr/local/bin/bash /usr/local/bin/child.sh 1 11 root 0:13 {child.sh} /usr/local/bin/bash /usr/local/bin/child.sh 2 12 root 0:13 {child.sh} /usr/local/bin/bash /usr/local/bin/child.sh 3
ラッパー(親プロセス)のイメージはこんなのです。子プロセス5つを生成してwaitしておくだけでchild.shはなんでもよくてとりあえず無限ループでもしてるイメージです。これだとtiniからSIGTERMが送られてきてもchild.shには届かずにparent.shが終了してしまいコンテナが終了してしまいます。
#!/bin/bash for i in $(seq 5); do child.sh ${i} & done wait
じゃあどうすると良いのか。parent.shでSIGTERMをハンドリングして子プロセスへ送信しつつwaitで待つみたいなことをやれればよさそうです。
#!/bin/bash sig_handle_parent() { echo "recv signal: parent" pkill -15 child.sh wait } trap sig_handle_parent 15 for i in $(seq 5); do child.sh ${i} & done wait
または生成した子プロセスのpidを親プロセスで持っておいてwaitの引数に与えておけばシグナルハンドラでkillできる
for i in $n_procs; do ./child.sh[${i}] & pids[${i}]=$! done for pid in ${pids[*]}; do # この辺でkillなりをする # pidは再利用されるので厳密にやると長くなりそう wait $pid done