🚢

立ち上げたDockerが終了するのが遅い

2024/03/04に公開

gccのイメージを使ってdockerコンテナをデタッチで立ち上げたが、終了するのが遅いのでそれを解消した。

結論

  • コマンドはbashではなく、catを使うとよさそう。
  • initプログラムをPID1で実行しよう。

課題

次のようなdocker-compose.ymlファイルを作成して、docker compose up -dを実行して、コンテナ(?)を立ち上げた。

services:
 gcc:
   image: gcc
   container_name: hoge
   tty: true
   command: "bash"

そのあと、docker compose exec -it gcc bashでコンテナにアクセスして、作docker compose downでコンテナを終了しようとしたら、10秒ほどかかる。なぜに!?

10秒もかかっていると、CIや自動化でイライラしそうなので、今のうちに解決したい。

docker コンテナ 終了しないdocker 終了 遅い コンテナなどで調べると、次のようなことが原因らしい。

原因1 - bash

dockerは、docker compose downdocker <container名> stopを行ったときに対象のコンテナにSIGTERMを送信する。しかし、bashSIGTERMが送信されても新しいプロンプトを返すだけで、終了はしないSITERMがタイムアウトになり、dockerSIGKILLを送信して強制終了する。そのため、時間がかかっていたようである。

ttyは、疑似端末を割り当てるために必要、、、らしい。勉強不足だが、これを付けるとフォアグラウンドではなくデタッチドでもコンテナが終了しない。

bashSIGTERMを送信されても、終了しないことはkill -s SIGTERM $$(現在のシェルのプロセスにSIGTERMを送信する)などで確認可能。あとは、公式リファレンスとか探すと書いてあるかも。知らんけど。

原因2 - init & PID1

PID1のプロセスだけは明示的に、SIGTERMを処理しないと終了処理が走らないらしい。killコマンドが制限しているのか、一体何が制限しているのかは不明。
また、Linux系は最初にinitというプログラムが実行される。dockerコンテナだとそれをすっ飛ばして、コマンドを実行する。それによって、本来initプログラムが正常にSIGTERMを処理していたところ、それがなくてタイムアウトまで終了しないようである。

原因まとめ

PID1でinit以外のプログラムやSIGTERMを処理しないプログラムがあると、終了しない。initのサブプロセスとしてbashを実行していたとしても、SIGTERMを無視するため終了しない。

解決策

dockerdocker-initというプログラムがデフォルトでインストールされているそう。docker runでは--init, docker-compose.ymlではinitを追加すれば、普通のOSのような処理を色々してくれる。ref.docker-run#init: https://docs.docker.jp/engine/reference/run.html#init

bashSIGTERMを無視してしまうので、catコマンドを使う。
catコマンドの採用理由は、入力を待機してくれること、大抵のLinuxに入っていること、処理負荷が少なそうなこと、メモリ消費量が比較的少ないこと、があげられる。
メモリ消費量に関しては、手元の環境では796kbほどだった。(思ってたより多かった)

sh: 936kb, bash: 3484kb (topコマンドのRES値を参照した)

結果

問題なく終了するdocker-compose.yml

services:
  gcc:
    image: gcc
    container_name: hoge
    tty: true
    init: true
    command: "cat"

終了処理

まとめ

強制終了のため10.3sかかっていた終了処理が、0.3sにまで短くなった。
このやり方が色々問題が発生しないかは疑問が残るが、目的は達成できてよかった。

おまけ - docker run

bash

起動
docker run -itd --rm --init --name hoge gcc bash
終了(計測込み)

$ time docker stop hoge
hoge

real    0m10.694s
user    0m0.000s
sys     0m0.000s

cat

起動
docker run -itd --rm --init --name hoge gcc cat
終了(計測込み)

$ time docker stop hoge
hoge

real    0m0.602s
user    0m0.000s
sys     0m0.046s

Discussion