Effective Docker Healthchecks For Node.js

Writing effective healthchecks will make your services running in Kubernetes or Docker Swarm more reliable

When I first started writing healthcheck’s for Node.js, they were pretty naive. They also followed the commonly suggested wisdom of the internet. Just install and hit your API!

Great!

But, turns out it’s not so great.

For one, many of my services do not have API endpoints over HTTP. Most of my internal services use AMQP. I ended up adding on lightweight APIs and installing curl in my production containers just for healthchecks!

Adding more complexity to a service is a never a great solution.

Additionally, just because curl can hit your API, doesn’t mean it’s working correctly.

Maybe your API is responding but your service can’t connect to your database, or queue, or whatever other dependency it needs to be fully operational.

I really wanted to be able to TRUST my healthcheck. If it says healthy, I want to know that the ability to connect to it’s dependencies was part of that state’s computation.

In celebration of Node.js 10’s LTS release, I’ll show you how to create a custom healthcheck for your service, and it’s dependencies, by writing a simple ES6 module with .mjs!

Here we go!

Let’s imagine a service.

We still want our healthcheck to be relatively lightweight, and for my purposes, just checking that all of the dependencies are working and that I’m able to connect with them would be loads better than a curl to a endpoint that just says ‘ok’.

In my example, I’m using two libraries and . These libraries are dependent on , , and .

If our service is unable to connect to any of these three, it is definitely not healthy.

Seems how these libraries can be passed a configuration with their connection options, and that’s how our application is using them internally, no need to do anything extra fancy or reinvent the wheel.

The healthcheck

Here’s the healthcheck example. I store it in .

And that’s it!

Let’s break it down, and then explore how we can use it in production.

This first line executes /bin/sh to call node with the experimental modules flag.

This way, we can just run the file without trying to figure out how to modify our calling code to know or expect to need to use the flag.

Next, we have our imports, and then define the function.

This is so we can call exit and say what we mean, instead of trying to remember whether we want exit code 0 or 1.

Our actual healthcheck function then just returns an array of promises that need to be resolved for the service to be considered healthy:

Notice that I’m able to reuse the same config that my application uses.

Lastly, we call check, and provide a success and a failure function.

Using the healthcheck

Now, to use the healthcheck easily, we are going to do two things.

  1. Set up the section of package.json to expose our healthcheck command.
  2. to be able to call the healthcheck

package.json

Now when we our project, the commands defined in will be available via our CLI.

This doesn’t make a ton of sense to do in our local machine, but it does inside of our container.

Here’s an example multi-stage Dockerfile which does so:

Notice the command in the second stage (second FROM defines second stage).

This makes the command in that container, execute the healthcheck we’ve defined.

This can also be used by Kubernetes livenessProbes and readinessProbes!

Check out the probes above, and then check out the folder for the healthcheck example!

Happy Hacking!

Interested in hearing my DevOps story? Read it on HackerNoon now!

If you’ve found this helpful, and would like to help me, the best way to do so is “clapping” for this article (press and hold) up to 50 times, and sharing it on social media! :)

I make things for the internet, that scale, look nice, and make money!