How to Setup Kubernetes Health Checking for gRPC with NodeJs And Bazel? (grpc-health-probe)
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
- 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);}
- 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"],)
- 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