0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go初心者が始める!gRPCサーバーのヘルスチェックの実装

Last updated at Posted at 2025-01-16

自己紹介

はじめまして!数年前に家電メーカーから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ヘルスサービスの実装方法を簡単にまとめしました。これからも引き続き、勉強したことをまとめていきたいと思います!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?