🐳

【Docker】コンテナ落とすのになんでこんな時間かかるん?Tiniを使って解決した話

に公開

はじめに 🐳

Dockerを使ってアプリケーションを動かしていると、
「コンテナが docker stop でうまく終了しない…😰」
「なんか終了されるまでに10秒くらいかかってない。。?🤔」
といった現象に遭遇することがあります。

もしかしたら、PID 1 が原因かもしれません。
今回は、

  • DockerコンテナにおけるPID 1の特殊な挙動
  • なぜNode.jsがPID 1だと問題が起こるのか
  • 解決策としてのTini

について、調べたことをまとめていきます!

なんで終了までに時間がかかるのか? ⏳

Dockerコンテナ内で Node.js アプリケーションを動かす とき、node app.js のように CMD ["node", "app.js"] で起動するケースが多いです。
この場合、Node.js のプロセス(node app.js)がコンテナ内でPID 1として実行されます。

しかし、Node.js は本来PID 1 として動作するように設計されていないため、SIGTERM を受け取らず、正常に終了できません

docker stop を実行すると、以下の流れになります。

  1. Dockerはコンテナ内のPID 1に SIGTERM を送信
  2. Node.jsはPID 1だと SIGTERM を無視してしまう
  3. Dockerは 10秒間待機
  4. それでも終了しない場合、強制的に SIGKILL を送信

そのため、コンテナの終了に 10秒かかってしまう という現象が発生します。

そもそもPIDとは? 🔢

PID(Process ID)とは、LinuxやUnix系OSで動作するプロセスを識別するための番号です。

  • PID 1 は、システムの最初のプロセスに割り当てられる特殊なID
  • OS起動時に initsystemd などのプロセスがPID 1として動作し、すべての子プロセスを管理
  • Dockerコンテナでは、最初に起動したプロセスがPID 1になる

Dockerコンテナ内で node app.js のように実行すると、Node.jsがPID 1として動作するため、通常の環境とは異なる挙動になることがあります。

解決策 🏆

Node.jsを使用しているコンテナには、init: true を指定して、Tiniを使うと解決できます。

Docker Composeの場合

compose.yml などのファイルに、下記のようにinit: trueを追加します。

version: '3'
services:
  app:
    image: my-node-app
    init: true

Dockerfileで設定する場合

Docker Composeを使わない場合は、Dockerfileに tini を明示的に追加できます。

FROM node:18-alpine

RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "app.js"]

なぜinit: trueを指定すると解決するのか? 🤔

Tiniが以下の役割を果たすため解決できます。

  • プロセスのシグナルを適切に処理
  • ゾンビプロセスの処理
  • 子プロセス(Node.js)へのシグナルの適切な転送

init: true とは? 🛠️

Docker Compose で init: true を指定すると、自動的に Tini という軽量な init システムが組み込まれ、PID 1として動作するようになります。

なぜ init: true を指定するだけでTiniが動くのか?

Dockerは init: true が指定されると、内部で Tiniを組み込んでコンテナのエントリポイントをTini経由に変更 します。
そのため、特別な設定なしに Tini の機能を利用できます。

version: '3'
services:
  app:
    image: my-node-app
    init: true // ここを追加
    restart: always

この設定を追加すると、

  • TiniがPID 1となり、適切にシグナルを処理する
  • Node.jsはPID 2になり、SIGTERMを正しく受け取る
    という状態になるため、正常に docker stop できるようになります。

ちなみにTiniとは? 🔍

Tiniは Dockerコンテナ内のPID 1の問題を解決するための超軽量なinitプロセス です。

Tiniの特徴

超軽量(バイナリサイズ約 10KB、CPU/メモリほぼ消費なし)
シグナルを適切に処理(SIGTERM / SIGINT を正しく伝播)
ゾンビプロセスを防ぐ(wait() を実行)

まとめ ✍️

  • DockerコンテナにおけるPID 1の特殊な挙動
  • なぜNode.jsがPID 1だと問題が起こるのか
  • 解決策としてのTini

について、調べたことをまとめていきました。
記事を読んでいただきありがとうございました!

参考記事 📚

🔗 DockerのPID 1問題について(Qiita)
🔗 Tiniについての解説(ngzmブログ)

Discussion