WebアプリケーションのヘルスチェックエンドポイントとKubernetesのProbe


本エントリは GMOペパボ エンジニア Advent Calendar 2024 (2) 12/2 の記事です。

本記事の対象読者

  • /healthz のようなヘルスチェックエンドポイントで何を確認するべきか悩んでいる
  • Kubernetes で運用しているアプリケーションの readinessProbe と livenessProbe に設定している内容の妥当性を説明できない。

はじめに

Webアプリケーションの健全性を観測するために /healthz のようなヘルスチェックエンドポイントを作成することがあります。

サービスの信頼性を向上するために、ヘルスチェックエンドポイントを用いて問題のあるサーバの検出、サービスアウト、再起動など様々な対策を講じていることと思います。

Kubernetes を使っていると readinessProbe と livenessProbe を用いて自動化しているはずです。

Kubernetes の Probe とは、以下のようなものです。

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: example
  name: example
spec:
  containers:
  - name: example
    image: example:latest
    # ref https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
    # 
    # kubeletは、Readiness Probeを使用して、コンテナがトラフィックを受け入れられる状態であるかを認識します。 
    # Podが準備ができていると見なされるのは、Pod内の全てのコンテナの準備が整ったときです。 
    # 一例として、このシグナルはServiceのバックエンドとして使用されるPodを制御するときに使用されます。 Podの準備ができていない場合、そのPodはServiceのロードバランシングから切り離されます。
    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
    # kubeletは、Liveness Probeを使用して、コンテナをいつ再起動するかを認識します。 
    # 例えば、アプリケーション自体は起動しているが、処理を継続することができないデッドロック状態を検知することができます。 
    # このような状態のコンテナを再起動することで、バグがある場合でもアプリケーションの可用性を高めることができます。
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080

Kubernetes の readinessProbe, livenessProbe それぞれ設定する際に何を考えるべきでしょうか。その上でヘルスチェックエンドポイントにどのような種類が必要でしょうか。

前提として一般的なWebアプリケーションを実行する場合で考えます。

readinessProbe は必要か?

Service経由で外部からリクエストを受付けるWebアプリケーションにおいては必要です。コンテナの起動が完了してからリクエストを割り振ることでリクエストの取りこぼしを防ぐためです。

readinessProbe で何を確認するか?

普段サービスの監視で利用しているヘルスチェックエンドポイントをとりあえず流用しているときは注意が必要です。

基本的には、単体の生存確認のみするべきです。

# AppPod
    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
AppService
AppPod
DB
API

この場合のAppPodの /healthz の内部実装はどうあるべきか考えます。単にHTTP Status 200を返すだけなのか、DBやAPIのような外部依存関係のチェックも行うべきかなどです。

外部依存関係のチェックを含めるべきか慎重に考える必要があります。例えば、DBの負荷やアクセス経路の問題等で一時的に律速になった場合を考えます。

外部依存関係のチェックを行ってしまうと、障害が発生したら全てのPodがサービスアウトするため、依存関係がない機能も含めて提供できなくなります。依存関係の問題はアプリケーションでハンドリングできる可能性があります。

FrontendService
FrontendPod
BackendService
BackendPod
DB
# FrontendPod
    readinessProbe:
      httpGet:
        path: / # <- とりあえずトップページをチェックしている例
        port: 8080

例えばこのようなサーバサイドレンダリングなアプリケーションがあったときに、FrontendPodのProbeで考えなしに / をチェックすると、実はBackendPodやDBに依存している可能性があります。また、ヘルスチェックエンドポイントを作るにしてもフレームワークによってはDBの接続まで確認してしまうこともあるので注意が必要です。

livenessProbe は必要か?

livenessProbe の特徴はコンテナを再起動することです。

少なくとも前述した、外部依存関係のチェックを行わないようにするべきです。

外部依存関係に問題があった場合に再起動しても解決しないにも関わらず再起動するため、外部依存が復旧していてもコンテナの起動時間中はサービスが提供できなくなってしまいます。 再起動を続けてしまい無駄なリソースを浪費してしまい2次被害が発生する可能性もあります。

アプリケーションが復旧不可能な問題を検知した場合は適切にGraceful Shutdownをすればコンテナは再起動します。 livenessProbe はデッドロックしたような場合を救済する手段なので基本的に発生するべきではないです。

それらを踏まえて再起動で解決したい問題を救済するために利用しましょう。また、 livenessProbe 違反が多数発生している場合はしっかり調査しましょう。

ヘルスチェックエンドポイントの種類

ここまでの内容で、KubernetesのProbeに設定するべきヘルスチェック内容として単体の生存確認を行うことの重要性の話をしました。

一方でサービスを提供している人間としては、単体の確認だけでなく結合チェックを行いたいと思います。例えば、頻繁に外形監視を行う際に指定するエンドポイントです。

この2種類は分けて実装しておくことがおすすめであると言えます。

最後に

今回は、社内でWebアプリケーションのヘルスチェックエンドポイントとKubernetesのProbeについて説明したい場面があったのでエントリにしました。

まとめると、以下を意識しましょうという話でした。

  • readinessProbe は必ず設定する
  • livenessProbe は再起動による解決が妥当な場面で使う。再起動が頻繁に起こる状態はバグの可能性が高いので調査する
  • readinessProbe, livenessProbe は単体の生存確認を行う
  • readinessProbe, livenessProbe に使うヘルスチェックエンドポイントの中身が何を確認しているかを知る
  • 外形監視とProbeではヘルスチェックエンドポイントの要件が異なるので使い分ける