マルチプロセス・コンテナのデザインパターン

原則として、kubernetesやdockerなどのコンテナは、単一プロセス構成で設計します。 「なるべくシンプルな方が良いから」という曖昧な理由ではなく、対障害の観点でベストプラクティスを検討すると単一プロセスになります。

この記事では、複数のプロセスを必要とするアプリケーションの設計を掘り下げます。
具体的な実例として、RailsとSideKiqの2プロセスのケースを取り上げますが、プロセスは他の何であっても概ね同様でしょう。

なお、あらかじめ結論を提示すると、Kubernetesで1Pod内にプロセスごとのコンテナを起動するパターンが良いでしょう。

マルチプロセスのコンテナ

何の工夫もせず、1コンテナ内で複数のプロセスを起動する例は単純です。
以下のような起動スクリプトを実行するとsidekiqとpumaの2プロセスが起動します。

#!/bin/bash

sidekiq &
exec puma -C config/puma.rb

コンテナ内で複数のプロセスを起動することじたいは特に問題なく可能です。
この例の最大の問題は、SideKiqが起動に失敗した場合にもコンテナは正常動作している挙動をとることです。

実例:sidekiq

実際の例として、SideKiqはバージョン6からプロセス監視の機能が省略されています。 バージョン5以前は、再起動をsidekiq自身が行っていたため、この構成でも問題は起きなかったのですが、バージョン6以降で起動失敗するとsidekiqがdefunct状態になりゾンビ化します。

SideKiqバージョン6のように実行機能のみを持つプログラムは、一般的なサーバー上ではsystemdなどのサービス監視プログラムを利用してデーモン化する想定になっています。

先ほどのスクリプトでは、1回バックグラウンド起動するだけの処理であるため、sidekiqの起動エラー時には子プロセスは単に終了し、親プロセスでも何も処理していません。

プロセス別のコンテナ構成

複数のプロセスが必要なアプリでは、コンテナ内に監視プログラムを導入する手もありますが、コンテナ環境ではkubernetesに監視させる方がストレートでしょう。
この例では、SideKiq専用のコンテナを作成することでsystemd利用と同様の効果を得られます。

kubernetesのcontroller(deploymentなど)には、コンテナのステータスを監視し再起動を行う機能があります。

コンテナのステータスはコンテナ内のPID(ProcessID)1の状態によります。シェルスクリプトから起動した場合には、 execで起動するとPID1になります。
(なお、psコマンドによりPIDを確認できるため、プログラムの状態とコンテナの状態が一致しない場合の確認に使えます)

よって、先ほどの起動スクリプトを以下のように2つに分割し、それぞれのコンテナを起動することで、kubernetesがsidekiqとpumaのプロセス起動状態を監視します。

#!/bin/bash

exec sidekiq
#!/bin/bash

exec puma -C config/puma.rb

kubernetesのPodには複数のコンテナを定義できます。この構成でたとえばsidekiqが起動エラーに遭遇した場合、Podは適切にCrashBackLoop状態になり、Deploymentなどであれば再起動をトライします。

なお、コンテナ間の連携はアプリ依存であり、個別に実装が必要です。この例(RailsのActiveJob)では、Redisを介してJob通信を行っています。

まとめ

コンテナで複数プロセスを起動する構成についてはドキュメントがあまりありませんが、プロセスごとのコンテナ構成が有効です。
各プログラムの解説ではsystemdを推奨している場合でも、コンテナ環境ならkubernetesで代用できる場合があります。求めるHA機能が再起動なのであれば、Deploymentなどの標準機能でカバーできます。

中馬崇尋
Chuma Takahiro