How to debug a Node.js application deployed with Jenkins X

If you’re a developer working on building software these days, it’s more than likely that you’ve considered using the microservice architectural pattern. With individual services that can be run and deployed independently of one other, this architecture is loosely coupled, fault-tolerant, and more easily scalable. Achieving these lofty goals requires a system that can support running microservices at scale and most organizations view Kubernetes as the optimal orchestration system. Although a powerful platform, K8s comes with a learning curve. And when you want to create CI/CD processes for your microservices running in Kubernetes, as most organizations do, things can get very complex, very quickly.

Debugging K8s-based applications is quite tricky as well. This hands-on blog post is going to explore two unique technologies that can make your Kubernetes journey much easier: Jenkins X and Rookout. Jenkins X aims to remove the learning curve for teams wanting to do CI/CD for modern applications. Rookout provides an SDK for real-time debugging and access to data from running applications in any environment, allowing dev teams to improve their MTTR (the time to restore or repair a service) when things don’t go as expected.

Setting Up Jenkins X

For this blog post, we’re going to look at how we can use Jenkins X and Rookout to dynamically debug and extract data from a running Node.js microservice. While this example showcases Node.js, Python and JVM based languages are also supported.

To start, let’s create a Kubernetes Cluster and install Jenkins X into it. For this example, we’re going to use GKE (Google Kubernetes Engine or Googles managed Kubernetes service) on the Google Cloud Platform (GCP). First, we need to get the Jenkins X binary:

curl -L https://storage.googleapis.com/artifacts.jenkinsxio.appspot.com/binaries/cjxd/latest/jx-darwin-amd64.tar.gz | tar xzv

Then move the binary to the appropriate spot:

sudo mv jx /usr/local/bin

Now, we can create the Kubernetes cluster directly from the GCP dashboard, or we can use Jenkins X to create the cluster for us. In this case, we’ll let Jenkins X create everything for us:

jx create cluster gke --skip-installation -n rookoutcluster

If you are missing any dependencies such as gcloud or Helm, Jenkins X will be kind enough to let you know and even install them for you. Follow the prompts to select the Zone where you want to install JX and the cluster will be created.

You can then run the following command to configure and install Jenkins X resources in your cluster. Note that your user must have the right permissions in order to perform all the required actions as part of the setup:

jx boot

This will guide you through bootstrapping your Jenkins X installation including choosing your project, creating storage buckets, and configuring your Git details.

Creating our Node.js project

Next, let’s create a sample Node.js project to use. If you have an existing project, it can be imported using the jx import command.

We’ll set up a basic Node.js quickstart. Run the following command:

jx create quickstart

In this case, we’ll choose node-http as the quickstart type and name our repository rookout-nodejs. Jenkins X creates the sample Node.js project for us and makes the first commit on GitHub. We also see some handy commands we can run to see what’s going on:

Watch pipeline activity via:     jx get activity -f rookout-nodejs -w
Browse the pipeline log via:     jx get build logs <your-github-id>/rookout-nodejs/master
You can list the pipelines via: jx get pipelines
When the pipeline is complete:   jx get applications

Since Jenkins X made an initial check-in for us, by watching the pipeline activity we can see that the sample application automatically gets built and deployed to the staging environment.  Making a call to:

jx get applications

This shows us that our application is running and available in the staging environment:

APPLICATION            STAGING  PODS   URL
rookout-nodejs 0.0.1     1/1     http://rookout-nodejs.jx-staging.34.82.170.178.nip.io

And we can see our application running:

Hello Node from Jenkins X

Configuring the Rookout SDK

Over time, as we continue to develop the application, we will most assuredly discover bugs and will need to debug them. Jenkins X provides a great feature called DevPods which allows you to edit application code inside of a Kubernetes pod to eliminate issues with configuration drift, or “it works on my machine”.  But, sometimes there are code-level issues where we really wish we could see what’s happening within our code while it’s running in our staging or production environments. Traditionally you would write log output messages within the code and then iterate on deploying them, testing, and validating that you got the log data you need to identify where the defect lies.

Rookout provides a new way of looking at this problem.  By using the Rookout SDK, we can set “Non-breaking” breakpoints within our application code from within the Rookout debugger (or your IDE), allowing us to debug or even send live application data to various logging systems such as Elasticsearch, DataDog, or wherever you’d like to store data.

So let’s install the Rookout SDK within the application. We’ll switch directories to our rookout-nodejs project and install the Rookout SDK:

npm install --save rookout

The Jenkins X quickstart by default uses a Node 9 image, so we’ll update our project’s Dockerfile to a supported Node.js version (8, 10, 11, and 12).

FROM node:10.0-slim
ENV PORT 8080
EXPOSE 8080
WORKDIR /usr/src/app
COPY . .
CMD ["npm", "start"]

Then, we’ll import the SDK into our application code:

var http = require('http');
var fileSystem = require('fs');
const rookout = require('rookout');

rookout.start({
   token: '<INSERT_ROOKOUT_TOKEN>',
   labels: {
     app: ‘rookout-nodejs’,
     type: ‘jenkins-x’
   }
});

var server = http.createServer(function(req, resp){
fileSystem.readFile('./index.html', function(error, fileContent){
if(error){
resp.writeHead(500, {'Content-Type': 'text/plain'});
resp.end('Error');
}
else{
resp.writeHead(200, {'Content-Type': 'text/html'});
resp.write(fileContent);
resp.end();
}
});
});

server.listen(8080);

Now, if we commit the changes above, it will trigger Jenkins X to build our application and deploy it.

Debugging the Application

Once deployed, we can load our sources into Rookout by authenticating with our Git provider:

Debugging the Application

It’s important to note that sources are loaded only locally within the browser. Rookout never sees your source code.

After loading the sources, source files can be opened in the Rookout IDE and we can begin setting non-breaking breakpoints within the application code which will give us a real-time view into the underlying state of the running application. Clicking just to the left of the source code line number allows you to set a breakpoint and if everything went as expected, the breakpoint should turn green. If you encounter issues, check out the breakpoint status page to help you understand what’s happening.  

Now, when you interact with the application or refresh it, you will see breakpoint data captured in the messages window below the source code. This captured application state data can be analyzed and if we want to, we could take this debug data and send it to the logging platform of our choice.

debug data and send it to the logging platform

Rookout also provides the ability to tag or label services that are being deployed with the SDK, in order to filter by application instances, thus further refining the source of data that’s returned. For example, we could create a label called ‘env’ and set its value to an environment type (i.e. env:production) so that we could filter by dev, staging or production environments.

Dev Staging Production

In Conclusion

Container orchestration platforms like Kubernetes have given development and operations teams tremendous power and flexibility over how their applications run and scale on demand.  With this power comes complexity and new challenges in terms of understanding what’s happening within the internals of your deployed applications when something goes wrong.

Jenkins X is a powerful open-source technology that can help organizations deal with the learning curve of getting started with CI/CD in Kubernetes. At the same time, Rookout can help organizations save time over traditional logging processes, and assist in quickly getting to the root of problems when it is needed to dive in and debug applications while they’re running in their native environments.

Still losing hours on getting data from your live code?

No credit card required