Nagios Core+Docker+TypeScript = 🤔😊😍

Remie Bolte
7 min readApr 14, 2019

--


tl;dr
using @remie/nagios-cli you can create your Nagios configuration using TypeScript and host it with Docker. Check out the project on GitHub!

I’ve been working with Nagios for the better part of my professional life. Although there are many alternatives that tried to improve it, the bottom line is: Nagios just works. It only does one job, and it is really good at it.

Nevertheless, there is one thing that I’ve always regretted about Nagios: I never managed to get anyone excited about creating service checks. More importantly, I’ve never seen a developer who was eager to integrate Nagios into the development workflow.

With the arcane configuration files and 90’s user interface, I can’t really blame anyone. Fixing this would involve bringing Nagios into the 21st century, and more specifically, to allow developers to use present day technologies to write service checks.

I never managed to wrap my head around how to achieve this, until I started working with TypeScript. As you might imagine, I’m really happy to share that I finally got it working. You can check out the results on NPM (@remie/nagios-cli) and GitHub (@remie/nagios) or continue reading about how I achieved my goal!

Introducing the TypeScript Nagios Object Defintion

For a recent project, I tried to imagine how I could achieve this longtime dream (no worries, I also have other less nerdy dreams).

There are several libraries and packages that have tried to simplify working with Nagios, but their approach just never felt right to me. I did not want to create yet another web or command-line interface that would generate Nagios configuration files, using the same non-intuitive configuration quirks (yes, I mean the object inheritance implementation) which you would have to manually copy to your Nagios instance.

My goal was to create a project that can live in GitHub, be build and tested by a Continuous Integration solution (e.g. Travis/CircleCI) and which could be deployed to AWS/GCP/Azure. Basically a workflow similar to creating a (web) application, using technologies and best practices that people are familiair with.

So I choose NodeJS with TypeScript, as this has become my default technology stack. The end goal was to be able to run npm test and npm start like I would with any other application and have it validate my Nagios configuration and start a Nagios instance respectively.

TypeScript decorators, interfaces and abstract classes to the rescue!

I ended up creating an NPM package that is both a library that uses TypeScript decorators and a set of abstract classes and interfaces to enable developers to create a NodeJS+TypeScript project and a CLI that ulimtately transforms those classes into the appropriate Nagios configuration, including support for Nagios object inheritance.

The CLI accepts a single application entry point, comparable to how Nagios uses the nagios.cfg file. From here, it will start collecting the configuration objects.

The nagios.cfg file redefined

To give a quick example, a typical entry point for Nagios would be the nagios.cfg file, which would look something like this:

A TypeScript project using the CLI would create a src/index.ts file instead which looks something like this:

https://github.com/remie/nagios/blob/master/example/src/index.ts

To give you an idea on how this works: the @Nagios(cfg, cgi, resources) decorator will tell the CLI that this class is actually the main Nagios configuration file. Each decorator will allow you to pass a JSON object with supported Nagios configuration properties. In this case, you can pass three objects: one for the nagios.cfg, one for the cgi.cfg and one for resources.cfg.

The @Include() decorator is used to tell the CLI that it should also process a class that is not part of the regular references you can make in code (more on that later).

On the application entry point, a single property is required (as mandated by extends NagiosCfg). The hosts property will accept an array of classes which extend the HostObj and HostGroupObj classes. This is were the fun starts.

Defining hosts and services in TypeScript

The definition of host and services follows a similar pattern: you tell the CLI that a class is a host by adding the @Host() decorator. If you want to attach services to the host, you add them to the services class property.

Here is an example of a host definition in TypeScript:

Fun fact, you can set Nagios configuration properties in two different ways. Either by passing them in the decorator:

@Host({ host_name: 'localhost'})
export class MyHost() extends HostObj {}

or by setting them as class properties:

@Host()
export class MyHost() extends HostObj {
public host_name: string = 'localhost';
}

As I mentioned before, you can reference Nagios objects from within the classes. The above example does so in the contacts and services properties. When processing the Nagios configuration, the CLI will look at the type of the property (string, number, boolean, etc) and if it detects the property refers to another Nagios configuration object (or array of) it will also process those classes. As Nagios allows for recursive references, the CLI will continue processing all references until it does not find any more unique configuration objects.

Nagios object inheritance in TypeScript

It is pretty common in Nagios to create templates to implement a DRY strategy for shared configurations.

Templates are basically Nagios objects with two differences: the template will have a name property and will have register set to 0 (or false). This will tell Nagios that the object definition is used for inheritance and should be ignored for other purposes. To make use of a template, any other object will add a use property with the value of the templatename property.

To replicate this behavior in TypeScript, you have two methods: extending a class and the @Use() decorator.

In this case, the Service class does not have a @Service() decorator. Instead, it has the @Use(BaseService) reference which will tell the CLI that it is extending BaseService.

The BaseService class does have the @Service() decorator. It will result in the CLI to create the generic-service template and refer to this from the object definition of the service.

Writing checks in NodeJS instead of bash

Another big con regarding Nagios is that many of the checks are written in unix Shell or Python. As it would be really unsettling to be able to write all your code within the TypeScript project only to have to refer to some Shell script, the CLI compiler comes with the feature of turning NodeJS classes into service checks.

If your class implements the Check interface, and returns a valid CheckResult, the compiler will make sure that Nagios executes it as a script.

Which means that you can use Axios to monitor your website uptime:

As you may recall, this particular check was invoked by the example host object. It was part of the array of services:

new Service('HTTP', new HTTP(`http://${this.address}`))

Notice also the use of ${this.address}. Normally, in Nagios, you would have to use magic variables in commands (calles Macros) to access host object information, like $HOSTADDRESS$. This is no longer necessary as you can access all class properties directly.

Running your project in Docker

If the whole TypeScript configuration and NodeJS checks aren’t convincing enough, perhaps you will reconsider knowing that the project actually met the end goal: that you can easily test and run your Nagios configuration locally by using npm test and npm start commands.

These commands will first compile your project from TypeScript to Javascript, execute the CLI compiler to generate the Nagios configuration and start a Docker container to test the configuration.

If the Nagios configuration is valid you can start the Docker container locally and see it in action:

The best part of using Docker, is that you’re Nagios instance is now also ready to deploy to production. There is no more need to first generate the configuration files, SCP them to the production server and reload Nagios.

With this workflow, you can create and test your Nagios configuration and checks locally, push it to your Git repository, have your favorite CI tool automatically test the configuration, build the Docker image and deploy it to your favorite cloud provider.

Get started with your own Nagios TypeScript project

If I managed to get you excited and want to give it a spin, simply install the NPM package globally:

npm install -g @remie/nagios-cli

Afterwards, create a new folder in your workspace and type:

nagios-cli init

This will copy an example project which you can use to start creating your own Nagios configuration with hosts, host groups and services.

For more documentation on the inner works of the compiler, you can check out the project on GitHub:

🎉🎉 Acknowledgements 🎉🎉

A big shoutout to the development team of Guidion, which served as a guinee pig for this project. They provided real-life examples, good discussions on architecture and a clear direction on what a developer would expect out of working with Nagios.

Like any other tech company, they are always looking for good people. If you fancy working in an inspiring environment that incubated this project, check out their career site (sorry, dutch site only):

Oh right, and before I forget: Nagios, the Nagios logo, and Nagios graphics are the servicemarks, trademarks, or registered trademarks owned by Nagios Enterprises. This project is not affiliated with Nagios Enterprises in any way.

--

--

Remie Bolte

App developer for the Atlassian product suite, DevOps / NodeJS / Docker enthusiast