Search code examples
node.jstypescriptkubernetesgrpcbazel

How to Setup Kubernetes Health Checking for gRPC with NodeJs And Bazel? (grpc-health-probe)


How to setup GRPC Health Checking for gRPC with Node.js an Bazel?


Solution

  • 1. Add the grpc-health-probe binary to Bazel

    In your WORKSPACE file add

    load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
    
    http_file(
        name = "grpc_health_check_bin",
        downloaded_file_path = "grpc_health_probe",
        urls = ["https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/v0.3.2/grpc_health_probe-linux-amd64"],
    )
    

    to download the executable binary.

    2. Implement Service

    Option 1: Use the grpc-health-check npm module

    • just read the docs :)

    Option 2 Implement the Health service yourself

    1. Go to this page and copy the health-checking.proto file, which currently looks like this:
      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);
      }
      
    2. In your BUILD.bazel file create a filegroup with the proto file, such that we can later add it to the NodeJs image
      filegroup(
          name = "health_checking_proto",
          srcs = ["health-checking.proto"],
      )
      
    3. Implement the Health service
      import * as protoLoader from '@grpc/proto-loader'
      import * as grpc from '@grpc/grpc-js'
      
      async function main() {
        const packageDefinition = await protoLoader.load('health-checking.proto')
        const grpcObject = grpc.loadPackageDefinition(packageDefinition)
        const {service} = (grpcObject.grpc as any).health.v1.Health
      
        const server = new grpc.Server()
        const implementation = {
          // status can be on of UNKNOWN, SERVING, NOT_SERVING
          Check: (_call: any, callback: any) => callback(null, {status: 'SERVING'}),
        }
        server.addService(service, implementation)
      
        server.bindAsync('0.0.0.0:9090', grpc.ServerCredentials.createInsecure(), () => server.start())
      }
      
      main()
      

    3. Build and Deploy the NodeJs image with Bazel

    The final BUILD file might look like this:

    load("@npm//@bazel/typescript:index.bzl", "ts_library")
    load("@k8s_deploy//:defaults.bzl", "k8s_deploy")
    load("@io_bazel_rules_docker//nodejs:image.bzl", "nodejs_image")
    
    package(default_visibility = ["//visibility:public"])
    
    ts_library(
        name = "lib",
        srcs = glob(include = ["**/*.ts"]),
        deps = [
            "@npm//@grpc/grpc-js",
            "@npm//@grpc/proto-loader",
            "@npm//@types/node",
        ],
    )
    
    filegroup(
        name = "health_checking_proto",
        srcs = ["health-checking.proto"],
    )
    
    nodejs_image(
        name = "image",
        data = [
            # nodejs application
            ":lib",
            # health-checking.proto file
            ":health_checking_proto",
            # grpc-health-probe executable binary
            "@grpc_health_check_bin//file",
        ],
        entry_point = ":index.ts",
    )
    
    k8s_deploy(
        name = "k8s",
        images = {"k8s:placeholder_name": ":image"},
        template = ":k8s.yaml",
    )
    

    4. Add Liveness and Readiness Probes to Kubernetes

    spec:
      containers:
      - name: server
        image: "[YOUR-DOCKER-IMAGE]"
        ports:
        - containerPort: 9090
        readinessProbe:
          exec:
            command: ["/app/<path to dir with BUILD file>/image.binary.runfiles/grpc_health_check_bin/file/grpc_health_probe", "-addr=:9090"]
          initialDelaySeconds: 5
        livenessProbe:
          exec:
            command: ["/app/<path to dir with BUILD file>/image.binary.runfiles/grpc_health_check_bin/file/grpc_health_probe", "-addr=:9090"]
          initialDelaySeconds: 10