{{announcement.body}}
{{announcement.title}}

Kubernetes With Bitnami and Sentry

DZone 's Guide to

Kubernetes With Bitnami and Sentry

Tutorial for Developers: build, deploy and monitor container-based applications on Kubernetes with Bitnami and Sentry.io.

· Microservices Zone ·
Free Resource

Introduction

No matter the size of your development team or the scale at which it operates, bugs are inevitable in software development. That's why tools that quickly identify and debug errors are critical in a continuous deployment environment. These tools also need to enable the swift deployment of patches and updates to your applications.

Sentry is a popular cloud-based framework that helps developers diagnose and fix application errors in real-time. Bitnami offers a curated catalog of secure, optimized, and up-to-date containers and Helm charts for enterprises to build and deploy applications across platforms and cloud vendors. Together, this combination gives enterprise developers all the tooling they need to create and publish applications consistently, monitor and debug errors in those running applications, and release new and improved versions on an iterative basis.

This article walks you through the process of developing a basic Node.js/Express application, deploying it on Kubernetes with Bitnami's Node.js container image and Helm chart, and monitoring errors thrown by it in real-time with Sentry.

Assumptions and Prerequisites

This guide makes the following assumptions:

Step 1: Create an Express Application

Tip: If you already have an Express application, you can use that instead and skip to Step 2. If you are using a MEAN application, you may need to adapt the MongoDB connection string in the application source code as explained in our MEAN tutorial.

The first step is to create a simple Express application. Follow the steps below:

  • Begin by creating a directory for your application and making it the current working directory:

mkdir myapp
cd myapp

  • Create a package.json file listing the dependencies for the project:

    JSON
     




    x
    12


     
    1
    {
    2
      "name": "simple-sentry-app",
    3
      "version": "1.0.0",
    4
      "description": "Sentry app",
    5
      "main": "server.js",
    6
      "scripts": {
    7
        "start": "node server.js"
    8
      },
    9
      "dependencies": {
    10
        "express": "^4.13"
    11
      }
    12
    }


  • Create a server.js file for the Express application. This is a skeleton application that randomly returns a greeting or an error.

    JSON
     




    xxxxxxxxxx
    1
    28


     
    1
    'use strict';
    2
     
                
    3
    // constants
    4
    const express = require('express');
    5
    const PORT = process.env.PORT || 3000;
    6
    const app = express();
    7
     
                
    8
    // route
    9
    app.get('/', function (req, res) {
    10
      let x = Math.floor((Math.random() * 4) + 1);
    11
      switch (x) {
    12
        case 1:
    13
          res.send('Hello, world\n');
    14
          break;
    15
        case 2:
    16
          res.send('Have a good day, world\n');
    17
          break;
    18
        case 3:
    19
          throw new Error('Insufficient memory');
    20
          break;
    21
        case 4:
    22
          throw new Error('Cannot connect to source');
    23
          break;
    24
        }
    25
    });
    26
     
                
    27
    app.listen(PORT);
    28
    console.log('Running on http://localhost:' + PORT);


Step 2: Integrate Sentry

Sentry works by logging errors using a unique application DSN. Therefore, the next step is to register your application with Sentry and obtain its unique logging DSN.

  • Log in to the Sentry dashboard.
  • Navigate to the "Projects" page and click "Create project".
  • Select "Express" as the platform.
  • Add a project name and set up alerts (optional).
  • Click "Create a project" to create a new project.
  • On the project quickstart page, note and copy the unique DSN for your project.

Tip: You can obtain the project DSN from the "Settings -> Projects -> Client Keys (DSN)" page at any time.

Next, revisit the application source code and integrate the Sentry Node SDK as follows:

  • Update the package.json file to include the Sentry Node SDK in the dependency list:

    JSON
     




    xxxxxxxxxx
    1
    13


     
    1
    {
    2
      "name": "simple-sentry-app",
    3
      "version": "1.0.0",
    4
      "description": "Sentry app",
    5
      "main": "server.js",
    6
      "scripts": {
    7
        "start": "node server.js"
    8
      },
    9
      "dependencies": {
    10
        "express": "^4.13",
    11
        "@sentry/node": "^5.15"
    12
      }
    13
    }


  • Update the server.json file to integrate Sentry with your application. Replace the SENTRY-DSN placeholder in the script below with the unique DSN for your project, as obtained previously.

    JSON
     




    xxxxxxxxxx
    1
    37


     
    1
    'use strict';
    2
     
                
    3
    // constants
    4
    const express = require('express');
    5
    const PORT = process.env.PORT || 3000;
    6
    const app = express();
    7
    const Sentry = require('@sentry/node');
    8
     
                
    9
    // define Sentry DSN
    10
    Sentry.init({ dsn: 'SENTRY-DSN' });
    11
     
                
    12
    // add Sentry middleware
    13
    app.use(Sentry.Handlers.requestHandler());
    14
     
                
    15
    // route
    16
    app.get('/', function (req, res) {
    17
      let x = Math.floor((Math.random() * 4) + 1);
    18
      switch (x) {
    19
        case 1:
    20
          res.send('Hello, world\n');
    21
          break;
    22
        case 2:
    23
          res.send('Have a good day, world\n');
    24
          break;
    25
        case 3:
    26
          throw new Error('Insufficient memory');
    27
          break;
    28
        case 4:
    29
          throw new Error('Cannot connect to source');
    30
          break;
    31
        }
    32
    });
    33
     
                
    34
    app.use(Sentry.Handlers.errorHandler());
    35
     
                
    36
    app.listen(PORT);
    37
    console.log('Running on http://localhost:' + PORT);


Tip: For more information about how Sentry integrates with Node.js and Express, refer to the official documentation.

Step 3: Create and publish a Docker image of the application

Bitnami's Node.js Helm chart can pull a container image of your Node.js application from a registry such as Docker Hub. Therefore, before you can use the chart, you must create and publish a Docker image of the application by following these steps:

  • Create a file named Dockerfile in the application's working directory, and fill it with the following content:

    Dockerfile
     




    xxxxxxxxxx
    1
    30


     
    1
    # First build stage
    2
    FROM bitnami/node:14 as builder
    3
    ENV NODE_ENV="production"
    4
     
                
    5
    # Copy app's source code to the /app directory
    6
    COPY . /app
    7
     
                
    8
    # The application's directory will be the working directory
    9
    WORKDIR /app
    10
     
                
    11
    # Install Node.js dependencies defined in '/app/packages.json'
    12
    RUN npm install
    13
     
                
    14
    # Second build stage
    15
    FROM bitnami/node:14-prod
    16
    ENV NODE_ENV="production"
    17
     
                
    18
    # Copy the application code
    19
    COPY --from=builder /app /app
    20
     
                
    21
    # Create a non-root user
    22
    RUN useradd -r -u 1001 -g root nonroot
    23
    RUN chown -R nonroot /app
    24
    USER nonroot
    25
     
                
    26
    WORKDIR /app
    27
    EXPOSE 3000
    28
     
                
    29
    # Start the application
    30
    CMD ["npm", "start"]


    This Dockerfile consists of two build stages:

    • The first stage uses the Bitnami Node.js 14.x development image to copy the application source and install the required application modules using npm install.
    • The second stage uses the Bitnami Node.js 14.x production image and creates a minimal Docker image that only consists of the application source, modules, and Node.js runtime.

Tip: Bitnami's Node.js production image is different from its Node.js development image. The production image (tagged with the suffix prod) is based on minideb and does not include additional development dependencies. It is therefore lighter and smaller in size than the development image and is commonly used in multi-stage builds as the final target image.

Let's take a closer look at the steps in the first build stage:

  • The FROM instruction kicks off the Dockerfile and specifies the base image to use. Bitnami offers several container images for Docker which can be used as base images. Since the example application used in this guide is a Node.js application, Bitnami's Node.js development container is the best choice for the base image.
  • The NODE_ENV environment variable is defined so that npm install only installs the application modules that are required in production environments.
  • The COPY instruction copies the source code from the current directory on the host to the /app directory in the image.
  • The RUN instruction executes a shell command. It's used to run npm install to install the application dependencies.
  • The WORKDIR instructions set the working directory for the image.
  • Here is what happens in the second build stage:

    • Since the target here is a minimal, secure image, the FROM instruction specifies Bitnami's Node.js production container as the base image. Bitnami production images can be identified by the suffix prod in the image tag.
    • The COPY instruction copies the source code and installs dependencies from the first stage to the /app directory in the image.
    • The RUN commands create a non-root user account that the application will run under. For security reasons, it's recommended to always run your application using a non-root user account. Learn more about Bitnami's non-root containers.
    • The CMD instruction specifies the command to run when the image starts. In this case, the npm start will start the application.
  • Build the image using the command below. Replace the DOCKER-USERNAME placeholder in the command below with your Docker account username.

docker build -t DOCKER-USERNAME/myapp:1.0 .

The result of this command is a minimal Docker image containing the application, the Node.js runtime, and all the related dependencies (including the Sentry SDK).

  • Log in to Docker Hub and publish the image. Replace the DOCKER-USERNAME placeholder in the command below with your Docker account username.

docker login
docker push DOCKER-USERNAME/myapp:1.0

Step 4: Deploy the Application on Kubernetes

You can now proceed to deploy the application on Kubernetes using Bitnami's Node.js Helm chart.

  • Deploy the published container image on Kubernetes with Helm using the commands below. Replace the DOCKER-USERNAME placeholder in the command below with your Docker account username.

    Dockerfile
     




    xxxxxxxxxx
    1


     
    1
    helm repo add bitnami https://charts.bitnami.com/bitnami
    2
    helm install node bitnami/node \
    3
      --set image.repository=DOCKER-USERNAME/myapp \
    4
      --set image.tag=1.0 \
    5
      --set getAppFromExternalRepository=false \
    6
      --set service.type=LoadBalancer


    Let's take a closer look at this command:

    • The service.type=LoadBalancer parameter makes the application available at a public IP address.
    • The getAppFromExternalRepository=false parameter controls whether the chart will retrieve the application from an external repository. In this case, since the application is already published as a container image, such retrieval is not necessary.
    • The image.repository and image.tag parameters tell the chart which container image and version to pull from the registry. The values assigned to these parameters should match the image published in Step 3.
  • Wait for the deployment to complete. Obtain the public IP address of the load balancer service:

kubectl get svc | grep node

Tip: See the complete list of parameters supported by the Bitnami Node.js Helm chart.

Step 5: Test Sentry Error Logging

To test the application, browse to the public IP address of the load balancer. You should randomly be presented with either a greeting or an error message, as shown in the images below:

Refresh the page a few times to generate a few errors. Then, log in to your Sentry dashboard and navigate to the project's "Issues" page. You should see a summary of the errors generated by the application, grouped by type, as shown below:

Select one of the errors to see complete details, including the actual lines of code that threw the error, the request headers, and the session details. This information is available for each error captured by Sentry.

Sentry also allows you to search for other errors of a similar nature, both within the project and across your project list. The information provided can be used to debug and identify the root cause of the error.

Once the source of an error is identified and corrected:

  • A new container image of the application can be built, tagged, and published using the Bitnami Node.js container image and the Dockerfile provided in Step 3.
  • The revised application image can then be deployed on Kubernetes by upgrading the Bitnami Node.js Helm chart to use the new container tag, as described in Step 4.

Of course, it's also possible to completely automate these steps; refer to the series linked below to learn about creating an automated CI pipeline with Bitnami containers and charts.

Topics:
ci/cd ,containers ,docker ,error monitoring ,kubernetes ,microservices ,monitoring ,software developent

Published at DZone with permission of Vikram Vaswani . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}