Add a health check executable
Is your feature request related to a problem? Please describe.
When deploying a Node.js gRPC server in a container, there is no straight-forward way to preform a health check when using grpc-health-check.
Describe the solution you'd like
It would be great to have an executable that we can run like:
npx grpc-health-check 0.0.0.0:50051
Describe alternatives you've considered
In order to perform a health check within the container, there seems to be a few options currently:
- pull in an additional binary such as https://github.com/grpc-ecosystem/grpc-health-probe
- install grpcurl
- build a custom client like https://gist.github.com/HarlemSquirrel/c635b7f78a411cf68b49614af7256117
I believe the binary and building from proto are the ways that you're intended to do healthchecks in the gRPC ecosystem. How would this feature work? Are you imagining that it'd just bundle the health probe and add a script to package.json?
I'm new to gRPC but it seems strange there's no easier way to perform a health check within a Docker container.
The readme doesn't really explain well how to write a health check client.
Yeah, the entry-level documentation on gRPC definitely leaves something to be desired.
What problem are you trying to solve?
The way that we do our k8s healthchecks with our service is that we have a service defined using the common healthcheck proto, and then we use the grpc-health-probe binary you've mentioned above within the same container as the healthcheck entrypoint on the liveness check definition on the k8s manifest.
k8s also has built-in gRPC liveness probes. We don't use them but I can't remember why off the top of my head.
I'm not using kubernetes so I want a way to check the health of my container running a gRPC server ideally without pulling in a binary that's not in the OS repository
As a workaround while this feature is not present, here are the steps I would take to write a health check client:
- Start with the hello world client example.
- Modify that example so that instead of loading a proto file and using
hello_world.Greeter, create theClientclass by passing theserviceobject exported bygrpc-health-checktogrpc.makeClientConstructor, and use that to construct the client. - Call the
checkmethod instead ofsayHello. The signature of the method can be found in health.proto - Run the client with the server address as an argument.
OK thanks, I was able to revise my script based on your steps.
It is significantly slower than the binary. That may be TypeScript's fault or just the reality of interpreted JS vs compiled binary.
root@container:/code# time yarn check-health
Starting health check on 0.0.0.0:50051
{ status: 'SERVING' }
real 0m1.704s
user 0m2.815s
sys 0m0.285s
root@container:/code# time ./vendor/bin/grpc_health_probe-linux-amd64 -addr=:50051 -tls=false
status: SERVING
real 0m0.005s
user 0m0.002s
sys 0m0.001s
There are reasons to expect the compiled library to be faster: compiled code is faster in general, the Go gRPC library is more optimized than the Node gRPC library, and the grpc_health_probe probably has compiled proto file statically linked in, while the Node code loads the proto file from the file system and parses it at runtime. However, that doesn't account for the multiple seconds you see. I modified your code slightly to just be JavaScript, and when I ran it I got this:
$ time node ./health-client.js
Starting health check on 0.0.0.0:50051
{ status: 'SERVING' }
real 0m0.190s
user 0m0.171s
sys 0m0.040s
Is it possible that you are including TypeScript compilation time in your measurement?
Yeah that's definitely it, thanks.