kubernetes上で動くgRPCサーバーのヘルスチェック: Health checking of gRPC server on kubernetes
TL;DR
- KubernetesのLiveness & Readiness Probeを使って、Pod内のコンテナ、プロセスのヘルスチェックが行える
grpc-health-probe
で簡単に実装できる
gRPCについて
gRPCはGoogleによって開発されたRPCシステムです。Protocol Buffersをインターフェースの定義に使用しています。gRPCについては以下の公式サイトで詳細に記述されています。 2017年からCNCFにホストされており、パフォーマンスの面や異なる言語間でもprotoファイルによってインターフェースを定義できるといった特定から、マイクロサービスにおいてよく使用されています。
gRPCサーバーのヘルスチェックについて
gRPCを使ったサーバーを開発し、Kubernetes上で稼働させる際にヘルスチェックを導入しました。しかし、その調査をしていた際に、gRPCサーバーのヘルスチェックの手法に関して日本語での記事が少なかったので今回記事にしました。 Kubernetes上でのgRPCサーバのヘルスチェックに関しては公式サイトにて提案がされています。公式サイトにもあるように2018/10時点ではKubernetesはgRPCサーバのhealth checkingをサポートしていないため、開発者が用意する必要があります。
If you’re unfamiliar, Kubernetes health checks (liveness and readiness probes) is what’s keeping your applications available while you’re sleeping. They detect unresponsive pods, mark them unhealthy, and cause these pods to be restarted or rescheduled. Kubernetes does not support gRPC health checks natively. This leaves the gRPC developers with the following three approaches when they deploy to Kubernetes:
公式サイトでは4つの手法が提案されていますが、本記事では、その中でもおすすめされているgrpc-health-probe
を使ってヘルスチェックを行います。
引用サイト: Health checking gRPC servers on Kubernetes - Kubernetes
図にあるようにcontainer内にstandard health checking protocolを利用してgRPCのサーバのヘルスチェックを行うprobeを導入することでヘルスチェックを行います。ヘルスチェックによってserverのstatusをチェックし、SERVING
が返ってくればOK、そうでなければUnhealthy として判断します。
デモ
今回はGo言語を使用してサーバを実装します。protoファイルは以下のようになっており、名前を送信するとメッセージが返ってくるだけの至極シンプルなサービスです。クライアントとやりとりを行うGateway
サービスと実際のメッセージを生成するBackend
サービスを用意しました。
追記:2019/4/28 ソースコードはこちらに置いておきます
syntax = "proto3"; package proto; service GreetingServer { rpc Greeting(GreetingRequest) returns (GreetingResponse) {} } message GreetingRequest { string name = 1; } message GreetingResponse { string message = 1; }
全体像としては以下のようになります。
このサービスを提供するサーバを実装していきます。記事とは関係がないので内容は省略します。
また、ヘルスチェック用としてstandard health checking protocol
を満たすサーバを実装する必要があります。実際のprotoとしては公式サイトにもあるように以下のprotoで定義されています。
syntax = "proto3"; package grpc.health.v1; message HealthCheckRequest { string service = 1; } message HealthCheckResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; NOT_SERVING = 2; } ServingStatus status = 1; } service Health { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); }
実際のサービスの場合は、protoc
などでprotoファイルを元にコードを生成し、interfeceを満たすようにサーバの中身を実装しますが、golangではgoogle.golang.org/grpc/health/grpc_health_v1
にstandard health checking protocol
をカバーしたものがあるので今回はこれを利用します。自分でprotoからファイルを生成しても問題はないと思います。
実装したヘルスチェック用のサーバは以下のようになります。
package server import ( "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" health "google.golang.org/grpc/health/grpc_health_v1" <- ここ "google.golang.org/grpc/status" ) func RegisterHeathCheck(s *grpc.Server){ health.RegisterHealthServer(s, &healthServer{}) } type healthServer struct{} func(h *healthServer)Check(context.Context, *health.HealthCheckRequest) (*health.HealthCheckResponse, error){ return &health.HealthCheckResponse{ Status:health.HealthCheckResponse_SERVING, <- "SERVING"を返す },nil } func (h *healthServer) Watch(*health.HealthCheckRequest, health.Health_WatchServer) error { return status.Error(codes.Unimplemented, "service watch is not implemented current version.") }
standard health checking protocol
にはなかったWatch
メソッドがありますが、これはもう少し発展的なヘルスチェック用に使うことができます。今回は特に使わないのでUNIMPLEMENTED
で返しておきます。他にもgoogle.golang.org/grpc/codes
の中で様々なstatusが定義されており使用することができます。
一旦これでヘルスチェックを通すことができるようになりました。一旦このアプリケーションをapplyしてgrpc-health-probe
コマンドを使用して手動でヘルスチェックをしてみます。すると以下のようにSERVING
status が返ってくることが確認できました。
$ > grpc-health-probe -addr=localhost:XXX status: SERVING
Liveness & Readiness Probe
Kubernetesではクラスタ内のPodの正常判断を行うための機構がLiveness Probe
とReadiness Probe
です。この二種類のヘルスチェック機構はそれぞれ役割と失敗した際の挙動が異なります。
Linevess Probe
Podが正常に動作しているのかどうかをチェックするために使用する。失敗した際にはPodを再起動する
Readiness Probe
Podがサービスを提供できる状態にあるのかどうかをチェックする。失敗した際には該当のPodに対してトラフィックを流さないがPodは再起動しない。
本記事ではこれらの機構を使用してヘルスチェックを行います。手順に関してはgrpc-health-probeのREADME.mdに記述があります。まずはgrpc_health-probe
を使用できるようにDockerfile
に以下を追加します。
RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ chmod +x /bin/grpc_health_probe
そして、以下のようにKubernetesのPodマニフェストにReadiness Probe
とLiveness Probe
に関する記述を追加します。
containers: - name: server image: "[YOUR-DOCKER-IMAGE]" ports: - containerPort: 5000 readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:5000"] initialDelaySeconds: 5 livenessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:5000"] initialDelaySeconds: 10
ヘルスチェックの機構を追加したマニフェストをapplyします。
そうすると、最初はPodのStatusがRunnning
にもかかわらず、READY
が0になっていることが確認できます。
Probe
に関するデータを探すとLiveness Probe
とReadiness Probe
が設定されていることが確認できます。
NAME READY STATUS RESTARTS AGE gateway-54d7cd4b8c-9rtfd 0/1 Running 0 5s
これは Readiness Probe
のヘルスチェックが一度も成功していないからです。少し待つとSTATUS
も1/1
になってPodに対してトラフィックを流せる状態になります。
NAME READY STATUS RESTARTS AGE gateway-54d7cd4b8c-9rtfd 1/1 Running 0 18s
podの設定を確認すると正しくprobeが設定されていることがわかります。細かいオプションや条件に関しては公式サイトに詳しく掲載されています。
$> kd pod | grep probe Liveness: exec [/bin/grpc_health_probe -addr=:50001] delay=10s timeout=1s period=10s #success=1 #failure=3 Readiness: exec [/bin/grpc_health_probe -addr=:50001] delay=5s timeout=1s period=10s #success=1 #failure=3
Readiness probe を失敗させてみる
せっかくですのでわざと、ヘルスチェックが失敗するようにしてみます。Readiness probe
がヘルスチェックを行なっているポート番号を適当に変更して失敗するようにしてapplyしてみます。
そうすると下にあるようにPodは起動するが、一分ほど待ってもREADY
が0のままです。Readiness probe
の紹介の通りの挙動をしていることが確認できます。
NAME READY STATUS RESTARTS AGE gateway-6867dfdcdf-tl7pc 0/1 Running 0 1m
Liveness probe を失敗させてみる
次にLiveness probe
が失敗するようにしてみます。そうすると何回もRESTARTS
が行われ、CrashLoopBackOffになっていることが確認できます。これもLiveness probe
によって正しくヘルスチェックが行えています。
NAME READY STATUS RESTARTS AGE gateway-bc555785b-8vg9l 0/1 CrashLoopBackOff 6 7m
Conculusion
思ってたより簡単にヘルスチェックができました。 ただし、このやり方だとアプリケーション側のコードにも変更が必要だったり、マイクロサービスが大きくなった時に管理が大変だったりしそうなので、IstioやLinkerdなどのコントロールプレーンでヘルスチェックもコントロールできるようになる(もうなってる?)と思うのであくまで現状での解決方に過ぎないと思います。 質問、意見、修正などあれば気軽にコメント、連絡お願いします。
Twitter ID: @tomiokasyogo
参考資料、サイト
https://grpc.io/docs/guides/concepts/ https://www.cncf.io/blog/2017/03/01/cloud-native-computing-foundation-host-grpc-google/ https://grpc.io/docs/guides/concepts/ https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ https://github.com/grpc-ecosystem/grpc-health-probe