1. 導入:なぜPID 1の管理が重要なのか
コンテナエンジニアリングにおいて、コンテナの停止処理が「毎回10秒間待たされる」「ゾンビプロセスがメモリを圧迫する」といった問題に直面したことはありませんか。これは、コンテナのメインプロセス(PID 1)がシグナルのハンドリングや子プロセスの回収(reap)というOS本来の役割を適切に果たせていないことが原因です。本記事では、軽量なinitシステムである「Tini」を用いて、これらの課題をスマートに解決する方法を解説します。
2. 基礎知識:PID 1の役割と陥る罠
Linuxシステムにおいて、PID 1(initプロセス)は非常に重要な役割を担います。
シグナルの伝播:PID 1はOSから送られてくるSIGTERMなどのシグナルを適切に子プロセスへ転送する必要があります。しかし、Node.jsやJavaなどのランタイムを直接起動すると、シグナルを無視してしまうことが多く、Dockerが強制終了(SIGKILL)するまでコンテナが停止しない事態を招きます。
ゾンビプロセスの回収:終了した子プロセスが親プロセスに回収されない状態を「ゾンビプロセス」と呼びます。PID 1は、終了した子プロセスをOSから切り離す「reap」処理を行う義務がありますが、多くのアプリケーションはこの責務を負えません。
3. 実装/解決策:DockerでのTini導入
Tiniは、PID 1として振る舞い、シグナルを転送し、ゾンビプロセスを回収するためだけに設計された最小限のinitプログラムです。現代のDockerには、これを自動的に適用するオプションが組み込まれています。
最も推奨されるのは、docker run時に–initオプションを付与する方法です。また、Dockerfile内で明示的にTiniをインストールしてENTRYPOINTに設定する方法もあります。これにより、アプリケーションはPID 1ではなく、Tiniの子プロセスとして実行されるようになり、安定したライフサイクル管理が可能になります。
4. サンプルプログラム:Tiniを活用したDockerfile例
以下は、Tiniを明示的に導入し、アプリケーションを安全に起動させるためのDockerfileの記述例です。
FROM node:18-alpine
Tiniをインストール(Alpine Linuxの場合)
RUN apk add –no-cache tini
作業ディレクトリの設定
WORKDIR /app
アプリケーション起動時にtiniを経由させる
-gオプションはシグナルをプロセスグループ全体に転送するためのものです
ENTRYPOINT [“/sbin/tini”, “–“]
メインとなるアプリケーションの起動コマンド
CMD [“node”, “server.js”]
1. ENTRYPOINTに[“/sbin/tini”, “–“]を指定することで、
CMDの内容がTiniの子プロセスとして実行されます。
2. これにより、nodeプロセスにSIGTERMが正しく伝わり、
graceful shutdownが機能するようになります。
5. 応用・注意点:現場での運用Tips
docker run –initの活用:Dockerfileを修正できない環境や、一時的なコンテナ実行であれば、docker run –initコマンドを使うのが最も手軽で確実です。開発環境でのトラブルシュートにはまずこのオプションを試してください。
注意点:シグナルの二重受け取り:一部のアプリケーションは既に内部でシグナルハンドリングを実装しています。Tiniを導入しても終了処理が改善されない場合は、アプリケーション側がSIGTERMを受け取った後の終了処理(コネクションのクローズなど)が適切に記述されているか、コードレベルでの見直しも併せて行ってください。
ゾンビプロセスの監視:Tiniを使用していても、アプリケーションが非同期で大量の子プロセスを生成し続ける場合、メモリリークのリスクは残ります。docker statsコマンドなどでコンテナのプロセス数を確認し、異常な増加がないか定期的に監視することを推奨します。

コメント