How to Build and Deploy a Cloud Native Node.js App in 15 minutes

RisingStack
Node.js Collection
Published in
6 min readMar 19, 2019

The goal of this tutorial is to show how you can turn a simple Hello World Node.js app into an application running on Kubernetes with all the best-practices applied. The tutorial shows you how to use the assets and tools provided by CloudNativeJS in order to build an enterprise-grade application, turn it into a Docker container image and then deploy that image easily on Kubernetes.

This article was written by Tamas Hodi — a senior engineer at RisingStack. RisingStack is software development and consulting agency specialized in Node.js, Kubernetes & Microservices. RisingStack is a contributor & supporter of CloudNativeJS. You can read why in their recent announcement: RisingStack Joins the Cloud Native Node.js Project

You can find the code of the sample Node.js application in this repository: https://github.com/RisingStack/cloudnativejs-risingstack-tutorial/tree/master/src

By the end of this tutorial, you will:

  • Implement lifecycle management for a Node.js application
  • Set up monitoring and tracing
  • Build the Docker image for the application
  • Prepare the application to be deployed into Kubernetes with Helm

Before you begin

Getting started with the Node.js app

You can find a prepared example app, a Node.js web server based on express, within the ./src folder which we will use during the tutorial. In case you are more into adventures, you can use the Hello world example app written by express or your existing application as a starting point.

Either way you go, start with installing your dependencies by using npm i on a prepared project or npm init && npm i express.

At this point, you should be able to run the server by node server.js which is reachable on localhost:3000.

Lifecycle management with Kubernetes

Kubernetes provides health checking in order to help you manage the lifecycle of your apps and detect the malfunctioning ones. We are going to prepare liveness and readiness probes to know when to restart the container and make the application more available and more available and resilient against downtime.

To do so, we are going to use middlewares provided by CloudNativeJS/cloud-health-connect. Add it to your dependencies with:

npm install @cloudnative/health-connect

Now, edit the server.js and register the new endpoints called /health and /ready.

const health = require('@cloudnative/health-connect')

const healthcheck = new health.HealthChecker()

// This should be resolved once your app is up and running.
// The result will be checked every time you hit /ready endpoint.
const readyPromise = new Promise(resolve => {
// This will make the app ready after 60 seconds for testing purposes.
setTimeout(() => {
console.log('READY!')
resolve()
}, 60000)
})

// Naming your check helps registering multiple checks and identifying the hanging ones.
healthcheck.registerReadinessCheck(new health.ReadinessCheck('testReadyCheck', readyPromise))

// Register the endpoints.
app.use('/health', health.LivenessEndpoint(healthcheck))
app.use('/ready', health.ReadinessEndpoint(healthcheck))

In order to see the application’s state, restart the app and open localhost:3000/ready.

You will see that the application is still booting, waiting for the resolution of all the registered ReadinessChecks, so the response is 503 UNAVAILABLE:

{
status: "STARTING",
checks: [{
name: "testReadyCheck",
state: "STARTING",
data: {
reason: ""
}
}]
}

Once all the ReadinessChecks have got resolved, it will send you 200 OK with the following payload, so Kubernetes will be able to start forwarding traffic to this container:

{
status: "UP",
checks: []
}

Monitoring with Prometheus

Prometheus, is an open-source monitoring solution under CNCF, which helps you powering applications with metrics and alerting.

Service monitoring means tasks of collecting, processing, aggregating, and displaying real-time quantitative data about a system. Prometheus gives you the ability to observe the application’s state and address issues before they impact your business.

You can find additional information about getting started with Prometheus here or use this helm chart to deploy the tool and start monitoring and collecting metrics.

The package called appmetrics-prometheus from CNJS provides the basic metrics (CPU, RAM usage) to be scraped by Prometheus.

After installing the module with npm install appmetrics-prometheus we have to require it before creating a web server, like in the example below:

const express = require('express')

const prometheus = require('appmetrics-prometheus')

const PORT = process.env.PORT || 3000

// The module patches the default http and https client and server
prometheus.attach()

// We can use any other frameworks even the native http server
const app = express()

The prometheus.attach() must be called before instantiating the server because it will patch the constructor as well as the createServer method of the native modules in order to provide the /metrics endpoint with the collected metrics.

Distributed Tracing with Zipkin

Distributed tracing is a method used to profile and monitor applications, especially those built using a microservices architecture. Distributed tracing helps pinpoint where failures occur and what causes poor performance.

We will use CloudNativeJS/appmetrics-zipkin to instrument our application automatically for an OpenTracing based request tracker called Zipkin.

Install the necessary package with npm install @cloudnative/appmetrics-zipkin.

Now you only have to include the require('appmetrics-zipkin') line in the entry point of your app before requiring other packages to let it correctly instrument other modules, so add it to the first line of your server.js file.

Please note that this way the instrumentation will use the default configurations, but you probably want to use your Zipkin instance running within your Kubernetes cluster instead sending the traces to localhost:9411 so for further options take a look at the custom configurations.

For further reading about OpenZipkin please visit the following guide or use this helm chart to get your first instance up and running quickly.

Create a Docker container image

In order to use the template files, copy them from the CloudNativeJS/docker project into your application directory.

git clone git@github.com:CloudNativeJS/docker.git && rm -rf ./docker/.git

The original Dockerfile prepares some configurations for you, which you might like to customize:

# default node version is 8, you might use different node version, so feel free to change it
FROM node:8.15.0-alpine

# provide the proper NODE_ENV for your current build, you might provide this only for the running container
ENV NODE_ENV production

ENV PORT 3000

# default port for your application to listen on
EXPOSE 3000

# default way to start your application
CMD ["npm", "start"]

Our example app has been already configured to respect these rules, like the port:

const express = require('express')

const PORT = process.env.PORT || 3000

const app = express()

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`))

start script has been added to the package.json as well to start the server:
node server.js

We can build the image with running the following command:

docker build -t cloudnativejs-example:1.0.0 -f ./docker/Dockerfile ./src

Test the image by running the container:

docker run -i -p 3000:3000 -t cloudnativejs-example:1.0.0

Deploy the Node.js application with Helm Charts

CloudNativeJS/helm provides template Helm Charts for deploying a Node.js web application into any Kubernetes based cloud. To do so, copy the template files from the project into your application directory.

git clone git@github.com:CloudNativeJS/helm.git && rm -rf ./helm/.git

Now take a look at the ./helm/chart/nodeserver/values.yaml which contains the initial parameters for a simple deployment.

First, we have to change the value of image.repository to use our own image called cloudnativejs-example.

Please note that we have used the same image tag during build, so you might change this later.

You can change other parameters as you wish, like Deployment (resources.limits, hpa) and Service (type, port) configurations.

In order to check the generated resources you can run the following command:

helm template ./helm/chart/nodeserver

You can deploy and run your application by kubectl apply -f directly on these objects or in case of having Tiller installed on the cluster, use instead:

helm install --name my-server ./helm/chart/nodeserver

Feel free to save and commit your application Chart alongside your source code.

How Cloud Native JS Helps

As you can see, with the libraries provided by CNJS you can easily add monitoring and distributed tracing to your app which you can then Dockerize and deploy to Kubernetes using Helm Charts.

At RisingStack we believe that these tools are extremely useful for Node.js developers to standardize applications within the cloud and speed up the development process.

Originally published at https://www.cloudnativejs.io/blogs.html on March 19, 2019.

--

--

RisingStack
Node.js Collection

Consulting, training & development services — with a strong focus on JavaScript, Node.js, DevOps, Microservices & Kubernetes | contact: info@risingstack.com