Quarkus Application on K8s の Health Check
Kubernetes(K8s) 上のコンテナの Health チェック
Kubernetes(以下 K8s) にはコンテナの状態をチェックするための機能が備わっています。K8s のドキュメントにある通り、Liveness Probe、Readieness Probe、Startup Probe の 3 つの Probe がコンテナの状態をモニタリングするための機能として用意されています。
- Liveness Probe
コンテナが実行中かどうかを示します。Liveness Probe が失敗すると、kubelet はコンテナを強制終了し、コンテナは再起動ポリシーの対象となります。コンテナが Liveness Probe を提供しない場合、デフォルトの状態はSuccess
です。 - Readiness Probe
コンテナがリクエストを処理する準備ができているかどうかを示します。Readiness Probe が失敗すると、エンドポイントコントローラーは、ポッドに一致するすべての Service の EndPoint から Pod のIP アドレスを削除します。デフォルト状態はFailure
です。コンテナが Readiness Probe を提供しない場合、デフォルトの状態はSuccess
です。 - Starup Probe
コンテナ内のアプリケーションが開始されているかどうかを示します。他のすべてのプローブは、スタートアッププローブが提供されると、成功するまで無効になります。スタートアッププローブが失敗すると、kubelet はコンテナを強制終了し、コンテナは再起動ポリシーの対象となります。コンテナが起動プローブを提供しない場合、デフォルトの状態はSuccess
です。
Liveness Probe もReadiness Probe もこれらを K8s に明示しない限り Success になる点に注意が必要です。Java のように起動が遅いアプリケーションが起動が完了する前にクライアントからアクセスされると 500 エラーを返却することがよく発生しますが、こうした事態を避けることができます。
またデッドロックが発生して応答を返却できない状態にも関わらず Liveness Probe が Success と判断され Pod として残り続けることを避けるということもできます。
アプリケーション開発者が K8s に Pod のライフサイクルの管理を委ねる場合、上記の Probe を K8s に対して提供することを検討しましょう。
Probe の提供手段
これら Probe の提供手段が 3 つ用意されています。
- ExecAction
指定されたコマンドをコンテナ内で実行します。コマンドがステータスコード 0 で終了した場合、診断は成功したと見なされます。 - TCPSocketAction
指定されたポートでコンテナのIPアドレスに対してTCPチェックを実行します。ポートが開いている場合、診断は成功したと見なされます。 - HTTPGetAction
指定されたポートと Path でコンテナの IP アドレスに対して HTTP Get 要求を実行します。応答のステータスコードが 200 以上 400 未満の場合、診断は成功したと見なされます。
Quarkus でビルドするアプリケーションを開発する場合、quarkus-smallrye-health Extension を使用することで 3 の HTTPGetAction をアノテーションベースで実装することができます。
Liveness Probe
デッドロックアプリケーション
今回、Liveness Probe を検証するためのアプリケーションとしてデッドロックを発生させるアプリケーションを使ってみます。デッドロックが発生するアプリケーションを K8s 上で動作させたところ再起動することはなく Pod は動作し続けました。
その際、Pod にアタッチして Thread Dump を取りましたが Thread Dump 上でもデッドロックが発生していることがわかります。
Found one Java-level deadlock:
=============================
"executor-thread-2":
waiting to lock monitor 0x000055d9f9bb4a28 (object 0x00000000f68beea8, a java.lang.Object), which is held by "executor-thread-1"
"executor-thread-1":
waiting to lock monitor 0x000055d9fa313ac8 (object 0x00000000f68beeb8, a java.lang.Object),
which is held by "executor-thread-2"
Quarkus SmallRye Health Extension
Quarkus の Extension の一つである SmallRye Health Extension を利用するとアプリケーションの Liveness Probe をアノテーションで提供できるようになります。その際、http://(ip):(port)/health/live にアクセスすると、Quarkus アプリケーションの Liveness Probe の結果を表示します。
今回は以下のコードを実装しデッドロックを検出した場合には、DOWN のレスポンスを返却するようにしてあります。
本アプリケーションを実行し、意図的にデッドロックを発生させてみると、http:(ip):(port)/health/live の結果は以下のように “status”: “DOWN” となります。
これで Liveness Probe を K8s に対して提供できるようになりました。実際に K8s 上で動作させ、デッドロックを発生させると再起動されるかどうかを調べてみます。
ちなみに、Quarkus Kubernetes Extension を利用すると K8s 上で Quarkus アプリケーションをデプロイするための YAML ファイルの雛形を生成してくれます。Kubernetes Extension に加え SmallRye Health Extension も使用していると以下のように Liveness Probe や Readieness Probe の設定も反映された YAML ファイルが target ディレクトリ配下に生成されます。
K8s 上で実行
K8s 上でデッドロックするアプリケーションを動作させてかつ Liveness Probe を有効にした状態にすると、以下のようにデッドロックしたアプリケーションは Status が Terminating になって落とされ、新たに新しい Pod が作成されました。
Liveness Probe を開発者が実装しない場合はデッドロックアプリケーションは常に K8s 上で生き続けるのに対し、Liveness Probe を提供したアプリケーションはデッドロック状態を検知して K8s が再起動可能になります。
Readiness Probe
アプリケーションの中には他のアプリケーション、例えばデータベースや、Kafka Broker 等に接続して初めて動作が可能になるものも存在します。こうしたケースでは依存するアプリケーションに接続可能になった段階で利用可能になったと判断できるよう Readiness Probe を実装すると良いでしょう。実際 Quarkus の Readiness Probe の説明ではデータベースを例に取っています。Quarkus SmallRye Health Extension を使用して Liveness Probe と同様に実装することができます。
Kafka Client / Streams
もし、Quarkus で Kafka クライアントを使用している場合は、Readiness Probe の実装すら必要ありません。Quarkus SmallRye Health Extention を依存関係に宣言すれば、Kafka Broker に接続できない状況であれば Readiness Probe が失敗を返却するよう自動設定されます。また Kafka Streams を使用している場合は、Streams で使用する Topic の準備ができていない場合、失敗を返却するよう自動設定してくれます。
今回使用した Source Code
ここに置いておきます。