Skip to content

This is a Node package that allows you to track the health of your application providing readiness and liveness functionalities.

License

Notifications You must be signed in to change notification settings

gritzkoo/nodejs-health-checker-lw

Repository files navigation

nodejs-health-checker-lw

npm

test build Coverage Status GitHub GitHub issues npm Tag Status Languages Status Repo Size Status

contributors

contributors

Made with contributors-img.


A successor package for nodejs-health-checker to simplify health checks.

The main purpose of this package is to substitute nodejs-health-checker package and standardize the liveness and readiness actions for Nodejs applications running in Kubernetes deployments, without adding complexity and extra package installs.

Read more about migrating from nodejs-health-checker or creating your tests in MIGRATIONS GUIDELINES


Liveness method

Will return a JSON as below:

{
  "status": "fully functional",
  "version": "v1.0.0"
}

Readiness method

Will return a JSON as below:

The status prop will return true once all your integrations works. If one of then fails, this status prop will reflect that something went wrong and you need to check the status inside integrations prop

{
  "name": "myapp",
  "version": "v1.0.0",
  "status": true,
  "date": "2022-07-10T00:46:19.186Z",
  "duration": 0.068,
  "integrations": [
    {
      "name": "github integration",
      "status": true,
      "response_time": 0.068,
      "url": "https://github.com/status"
    }
  ]
}

How to install

npm i --save nodejs-health-checker-lw

OR

yarn add nodejs-health-checker-lw

How to init

First, you need to write your test like below:

this example is using http check for API integrations. Remember that you can write using your own methods and patterns. You need only to return an instance of nodejs-health-checker Check.

// file src/integrations/github.ts
import { Check } from 'nodejs-health-checker-lw'
import { fetch } from 'node-fetch'
export async function TestGithubIntegration() Promise<Check> {
  return new Promise((resolve, _) => {
    let result = new Check({ url: 'https://github.com/status' })
    // call a url to test connectivity and HTTP status
    fetch(result.url, { timeout: 10000 })
      .then(response => {
        if (response.status !== 200) {
          result.error = {
            status: response.status,
            body: response.body
          }
        }
      })
      .catch(error => result.error = error)
      .finally(() => resolve(result))
  })
}

Then in your main declarations, you MUST create a const with HealthChecker from nodejs-health-checker-lw and fill a few props to create a re-usable pointer to be called after as below.

Read more about the version in this topic

// file src/healthchecker.ts
import { HealthChecker} from 'nodejs-health-checker-lw'
import { TestGithubIntegration } from 'src/integrations/github' // as the example above. This list can grow as mush as you need
export const check = new HealthChecker({
  name: 'myapp', // set your application name
  version: 'v1.0.0', // set the version of your application
  // integrations are the list of tests that needs to be executed.
  integrations: [
    {
      // set the name of the integration that will be tested
      name: 'github integration', 
      // pass the functions that tests this integration
      handle: MyIntegrationTest
    }
  ]
})

How to use it

Once you create a constant with an instance of HealthChecker, you can now call the methods, liveness, and readiness, in your application, in CLI or API mode

CLI interface

import check from 'src/healthchecker' // as the example above
const cliArgs = process.argv.slice(2)
switch (cliArgs[0]) {
  case 'liveness':
    console.log(check.liveness())
    break
  case 'readiness':
    check.readiness().then(response => {
      if (!response.status) {
        // do something like trigger an alarm or log to other obervability stacks
        console.warning(JSON.stringify({message: "health check fails", results: response}))
      } else {
        // or just log OK to track
        console.info(JSON.stringify(response));
      }
    })
    break
  default:
    console.error(`invalid option: ${cliArgs[0]}... accepty only liveness or readiness`)
}

In Kubernetes deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-nodejs-app
spec:
  selector:
    matchLabels:
      app: your-nodejs-app
  template:
    metadata:
      labels:
        app: your-nodejs-app
    spec:
      containers:
      - name: your-nodejs-app
        image: your-nodejs-app:tag
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80
        livenessProbe:
          exec:
            command:
              - "/bin/node"
              - "your-script.js"
            args:
              - "liveness"
        readinessProbe:
          exec:
            command:
              - "/bin/node"
              - "your-script.js"
            args:
              - "readiness"

HTTP interface

In Javascript

import express from 'express'
import check from 'src/healthchecker' // as the example above
const PORT = process.env.PORT||80;
const server = express()
server.get('/health-check/liveness', (_, res) => {
  res.json(check.liveness())
})
server.get('/health-check/readiness', async (_, res) => {
  const result = await check.readiness()
  if (!response.status) {
    // do something like trigger an alarm or log to other obervability stacks
    console.warning(JSON.stringify({message: "health check fails", results: response}))
  } else {
    // or just log OK to track
    console.info(JSON.stringify(response));
  }
  res.json(result)
})
server.listen(PORT, () => {
  console.log(`[SERVER] Running at http://localhost:${PORT}`);
});

In Kubernetes deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-nodejs-app
spec:
  selector:
    matchLabels:
      app: your-nodejs-app
  template:
    metadata:
      labels:
        app: your-nodejs-app
    spec:
      containers:
      - name: your-nodejs-app
        image: your-nodejs-app:tag
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /health-check/liveness
            port: http
        readinessProbe:
          httpGet:
            path: /health-check/readiness
            port: http

It's important to share that you MUST return always an OK status in Kubernetes liveness and readiness because if one of your integration fails, this can teardown all of your pods and make your application unavailable. Use other observability stacks like Zabbix or Grafana alarms to track your application logs and then take actions based on observability! (I learned it by the hard way =/ )


More pieces of information


Version in your HealthChecker

I highly recommend you fill this prop using a dynamic file content loading like:

  • reading the package.json file and using the version value to fill the HealthChecker.version placeholder like below:

    import fs from 'fs'
    import path from 'path'
    import { HealthChecker } from 'nodejs-health-checker-lw'
    import { TestGithubIntegration } from 'src/integrations/github' 
    
    const versionFilePath = path('package.json')
    const file = {
      content: null
      error: undefined
    }
    try {
      let tmpRawData = await fs.readFileSync(versionFilePath, {encoding: 'utf8'})
      file.content = JSON.parse(tmpRawData)
    } catch (error) {
      file.error = error
    }
    
    export const check = new HealthChecker({
      name: 'myapp',
      version: file.error || file.content.version
      integrations: [/*and the list of your integrations here*/]
    })
  • creating a file like version.txt using a command like the below in the pipeline before a docker build/push steps:

    git show -s --format="%ai %H %s %aN" HEAD > version.txt

    then use it in your code like:

    import fs from 'fs'
    import path from 'path'
    import { HealthChecker } from 'nodejs-health-checker-lw'
    import { TestGithubIntegration } from 'src/integrations/github' 
    
    const versionFilePath = path('version.txt')
    const file = {
      content: null
      error: undefined
    }
    try {
      file.content = await fs.readFileSync(versionFilePath, {encoding: 'utf8'})
    } catch (error) {
      file.error = error
    }
    export const check = new HealthChecker({
      name: 'myapp',
      version: file.error || file.content
      integrations: [/*and the list of your integrations here*/]
    })