自己紹介
はじめまして!数年前に家電メーカーからSIerに転職したエンジニアです。
転職後に初めてGo言語やgRPC、Kubernetes、マイクロサービスの開発に携わり、日々四苦八苦しながら働いています。
この記事は、学習の一環として自分が調べた内容をまとめたものです。同じようにgRPCやKubernetesの初心者の方に少しでも役立つ情報を提供できればと思います!
ヘルスチェックとは何か
ヘルスチェックはサーバーが正常にレスポンスを返せる状態かを判断するためのAPIです。
gRPCサーバーのヘルスチェックのRPCのインターフェースは標準のサービスAPIとして下記のように規定されています。
詳細はgRPCの公式ドキュメントをご覧ください: gRPC Health Checking
syntax = "proto3";
package grpc.health.v1;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
SERVICE_UNKNOWN = 3; // Used only by the Watch method.
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
gRPCサーバーを実装する
1. プロジェクトの準備
最初に、プロジェクトのディレクトリを作成します。
git init grpc-health-service
cd grpc-health-service
その後、grpc_health_v1
をインストールします。
go mod init grpc-health-service
go get google.golang.org/grpc google.golang.org/grpc/health/grpc_health_v1 google.golang.org/grpc/examples/features/proto/echo
grpc_health_v1
パッケージはgRPCのヘルスチェックのサービスの実装に必要なコードを提供しています。
2. サーバーの実装
実際にgRPCサーバーを実装する場合、メインの機能を提供するサービスに加えて、ヘルスチェックのサービスを登録することになります。この記事ではメインの機能を提供するサービスの代わりとして、Echoサービスを登録しています。
main.go
package main
import (
"context"
"google.golang.org/grpc/reflection"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/examples/features/proto/echo"
"google.golang.org/grpc/health/grpc_health_v1"
)
// HealthService implements the gRPC health checking service.
type HealthService struct{}
// Check メソッドの実装
func (h *HealthService) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
// 商用サービスなら、ここでデータベースに接続できるかなどをチェックする
return &grpc_health_v1.HealthCheckResponse{
// サーバーが正常な状態でなければ NOT_SERVING を返すようにする
Status: grpc_health_v1.HealthCheckResponse_SERVING,
}, nil
}
// Watch メソッドの実装
func (h *HealthService) Watch(req *grpc_health_v1.HealthCheckRequest, srv grpc_health_v1.Health_WatchServer) error {
// ここでは常に SERVING と NOT_SERVING を連続して返し続けてい
// 本来はサーバーの状態に応じてステータスが変わったときだけ返すようにする
for {
if err := srv.Send(&grpc_health_v1.HealthCheckResponse{
Status: grpc_health_v1.HealthCheckResponse_SERVING,
}); err != nil {
return err
}
if err := srv.Send(&grpc_health_v1.HealthCheckResponse{
Status: grpc_health_v1.HealthCheckResponse_NOT_SERVING,
}); err != nil {
return err
}
}
}
// EchoService implements the Echo service defined in the proto file.
type EchoService struct {
echo.UnimplementedEchoServer
}
// Echo returns the same message sent by the client.
func (e *EchoService) Echo(ctx context.Context, req *echo.EchoRequest) (*echo.EchoResponse, error) {
return &echo.EchoResponse{Message: req.GetMessage()}, nil
}
func main() {
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
server := grpc.NewServer()
// メインのサービスの登録
echoServer := &EchoService{}
echo.RegisterEchoServer(server, echoServer)
// ヘルスサービスの登録
healthServer := &HealthService{}
grpc_health_v1.RegisterHealthServer(server, healthServer)
// grpcurl で proto ファイルを指定せずにサービスを呼び出すため
reflection.Register(server)
log.Println("gRPC server is running on port 50051")
if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err)
}
// 商用サービスならグレースフルシャットダウンなどの実装が必要
}
gRPC のヘルスサービスには Check と Watch の2つのメソッドを実装できます。
CheckメソッドとWatchメソッドの違い
- Checkメソッド: クライアントが現在のサーバーの状態を1回だけ確認するためのメソッド(Unary RPC)です。ロードバランサーからのサーバーの死活監視や、k8sのliveness probeなどで使用されます
-
Watchメソッド: サーバーの状態の変化を連続して確認するためのメソッド(Streamin RPC)です。gRPCのドキュメントでは
client side health check
で使うとされていますが、私は使ったことがないので詳細はよくわからないです...
3. サーバーの動作確認
クライアントを使ってサーバーが正常に動作するかを確認します。
grpcurl
grpcurl
を使用してヘルスチェックを実行します。
$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
$ grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Check
{
"status": "SERVING"
}
$ grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Watch
{
"status": "SERVING"
}
{
"status": "NOT_SERVING"
}
// ずっと繰り返す
4. KubernetesでのgRPC liveness probe
Kubernetesの v1.27 以降では、gRPCサーバーのヘルスチェックを使用してPodのLiveness Probeを設定することが可能です。
この設定をすると、サーバーが正常に動作しているかをKubernetesが監視し、正常に動作していない場合はpodを再起動してくれます。
以下は、livenessProbe
の設定例です。
apiVersion: v1
kind: Pod
metadata:
name: grpc-server
spec:
containers:
- name: grpc-server
image: grpc-server-image
ports:
- containerPort: 50051
livenessProbe:
grpc:
port: 50051
initialDelaySeconds: 5
おわりに
この記事では、Goを使用したgRPCヘルスサービスの実装方法を簡単にまとめしました。これからも引き続き、勉強したことをまとめていきたいと思います!