Kubernetes で sleep infinity しつつ SIGTERM を受け取ったらすぐ終了させたい

Introducing dumb-init, an init system for Docker containers で説明されているように、PID 1 のプロセスは特殊な扱いになるため、そのプロセスにシグナルハンドラーがなければ、シグナルは無視されます。

例えば Docker の場合、次のように --init オプションなしで実行すると、PID 1 のプロセスは sleep になるため、docker stop を実行してもデフォルトタイムアウトの 10 秒が経過して強制終了させられるまで動き続きます。

$ docker run --rm --name sleep -d ubuntu sleep infinity
0fef58eb514808d77dc26edccb2e56b55268425dae2a299c21b23a1b661b1d84
$ time docker stop sleep
sleep
docker stop sleep  0.01s user 0.02s system 0% cpu 10.381 total

--init オプション付きで実行すると、PID 1 は init プロセスになり、SIGTERM を受け取ると子プロセスである sleep に SIGTERM を送り、sleep はカーネルによって殺されるため、即座に終了します。

$ docker run --init --rm --name sleep -d ubuntu sleep infinity
623807fc3bb9f052c1a001c0ee69db2255356ed6edff42af02dd1fdf14a1c905
$ time docker stop sleep
sleep
docker stop sleep  0.01s user 0.01s system 9% cpu 0.245 total

これと同じようなことを Kubernetes でもやりたかったんですが、Support adding an init process to containers という issue が 6 年前に作成されて今も open であることから、おそらく同様の機能が追加されることはないでしょう。

というわけで、shell script を何行も書かずに手軽に実現する方法がないかと考えたところ、そういえば timeout というコマンドがあったなと思い出したわけです。
こいつを使うと次のようにすぐに pod を終了させることができます。

$ kubectl run sleep --restart=Never --image=ubuntu -- timeout infinity sleep infinity
pod/sleep created
$ time kubectl delete pod sleep
pod "sleep" deleted
kubectl delete pod sleep  0.07s user 0.04s system 5% cpu 2.106 total

timeout なしで実行した場合と雲泥の差ですね。

$ kubectl run sleep --restart=Never --image=ubuntu -- sleep infinity
pod/sleep created
$ time kubectl delete pod sleep
pod "sleep" deleted
kubectl delete pod sleep  0.10s user 0.05s system 0% cpu 32.795 total

なお、sh -c などで複数のコマンドを実行する場合は PID 1 が sh になってしまうので、次のように exec を使う必要があります。

$ kubectl run sleep --restart=Never --image=ubuntu -- sh -c 'echo "do something"; exec timeout infinity sleep infinity'
pod/sleep created
$ time kubectl delete pod sleep
pod "sleep" deleted
kubectl delete pod sleep  0.06s user 0.03s system 5% cpu 1.680 total

以上、ちょっとした小ネタでした。