DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Common Performance Management Mistakes
  • Common Performance Management Mistakes
  • 13-Step Guide to Performance Testing in Kubernetes
  • Containerization of a Node.js Service

Trending

  • System Coexistence: Bridging Legacy and Modern Architecture
  • MCP Servers: The Technical Debt That Is Coming
  • IoT and Cybersecurity: Addressing Data Privacy and Security Challenges
  • A Deep Dive Into Firmware Over the Air for IoT Devices
  1. DZone
  2. Software Design and Architecture
  3. Containers
  4. 7 Ways of Containerizing Your Node.js Application

7 Ways of Containerizing Your Node.js Application

This article lists seven ways to containerize your Node.js application, so let’s look at them briefly.

By 
Webs Optimization user avatar
Webs Optimization
·
Mar. 22, 23 · Analysis
Likes (1)
Comment
Save
Tweet
Share
7.7K Views

Join the DZone community and get the full member experience.

Join For Free

Node.js has been a favorite among serious programmers for the last five years running. The JavaScript runtime environment for maximum throughput is a free and open-source program that aims to improve the performance of JavaScript across several platforms. 

Because of its event-driven, non-blocking I/O approach, Node.js is small in size and quick in processing requests, making it an excellent choice for data-intensive, real-time, and distributed applications.

Developers are increasingly turning to Node.js application optimization services; thus, it's important to streamline the process of designing and releasing cross-platform applications. So, let's get into the context of the article.

Suggestions for Containerizing and Optimizing Node Apps

Here are listed seven ways of containerizing your Node.js application. Let's have a look at them in brief.

1. Use a Specific Base Image Tag Instead of "Version:Latest"

Useful tags that define version information, intended destination (prod or test, for example), stability, or other relevant information for distributing your application across environments should always be included when creating Docker images.

Outside of the development environment, you shouldn't depend on the most recent tag that Docker automatically downloads. The usage of the most recent version of a program might result in strange or even harmful effects. 

Suppose you're constantly updating to the most recent version of an image. In that case, eventually, one of those updates is certain to include a brand-new build or untested code that will cause your app to stop functioning as intended.

Take this example Dockerfile that targets that node:

 
# Create image based on the official Node image from dockerhub

FROM node:lts-buster

 

# Create app directory

WORKDIR /usr/src/app

 

# Copy dependency definitions

COPY package.json ./package.json

COPY package-lock.json ./package-lock.json

 

# Install dependencies

#RUN npm set progress=false \

#    && npm config set depth 0 \

#    && npm i install

RUN npm ci

 

# Get all the code needed to run the app

COPY . .

 

# Expose the port the app runs in

EXPOSE 3000

 

# Serve the app

CMD ["npm", "start"]


Instead of using node:latest, you should use the lts-buster Docker image. Considering that lts-buster is a static picture, this method may be preferable.

2. Use a Multi-Stage Build

One single Docker base image may be used throughout several stages of a build, including compilation, packaging, and unit testing. However, the actual code that executes the program is stored in a different image. 

As the finished image won't have any development or debugging tools, it'll be more secure and take up less space. In addition, if you use Docker's multi-stage build process, you can be certain that your builds will be both efficient and repeatable. 

You can create multiple stages within a Dockerfile to control how you build that image. You can containerize your Node application using a multi-layer approach.

Different parts of the application, like code, assets, and even snapshot dependencies, may be located in each of the many layers that make up the program. What if we wish to create an independent image of our application?

To see an example Dockerfile of this in action, please check the following:

 
FROM NODE:LTS-BUSTER-SLIM AS DEVELOPMENT

 

WORKDIR /USR/SRC/APP

 

COPY PACKAGE.JSON ./PACKAGE.JSON

COPY PACKAGE-LOCK.JSON ./PACKAGE-LOCK.JSON

RUN NPM CI

 

COPY . .

 

EXPOSE 3000

 

CMD [ "NPM", "RUN", "DEV" ]

 

FROM DEVELOPMENT AS DEV-ENVS

RUN <<EOF

APT-GET UPDATE

APT-GET INSTALL -Y --NO-INSTALL-RECOMMENDS GIT

EOF

 

 

# INSTALL DOCKER TOOLS (CLI, BUILDX, COMPOSE)

COPY --FROM=GLOURSDOCKER/DOCKER / /

CMD [ "NPM", "RUN", "DEV" ]

We first add an AS DEVELOPMENT label to the NODE:LTS-BUSTER-SLIM statement. This lets us refer to this build stage in other build stages. Next, we add a new development stage labeled DEV-ENVS. We'll use this stage to run our development.

Now, let's rebuild our image and run our development. To execute just the development build stage, we'll use the same Docker build command as before, but this time we'll use the --target development parameter.

 
docker build -t node-docker --target dev-envs


3. Fix Security Vulnerabilities in Your Node Image

In order to create modern services, programmers often use preexisting third-party software. However, it's important to be cautious when integrating third-party software into your project since it may present security holes. 

Using verified image sources and maintaining vigilant container monitoring are both useful security measures. Docker Desktop will notify you to do security checks on the newly created node:lts-buster-slim Docker image.

Let's have a look at our Node.js app with the help of the Snyk Plugin for Docker Desktop. Begin by setting up Docker Desktop 4.8.0+ on your Mac, Windows, or Linux PC. Next, choose the Allow Docker Extensions checkbox under Settings > Extensions.

After that, you can search for Snyk in the Extensions Marketplace by selecting the "Add Extensions" option on the left sidebar. 

Put in the Snyk and log onto the network:

Put in the Snyk and log onto the network.

Type "Node Docker Official Image" into the "Choose image name" box. In order to begin scanning, you will need to log in to Docker Hub. If you don't have an account, don't fret; making one is easy, quick, and completely free.

With Docker Desktop, the outcome of a scan looks like this:

With Docker Desktop, the outcome of a scan looks like this.

During this scan, Snyk discovered 70 vulnerabilities of varied severity. After you've identified them, you may start fixing them to improve your reputation.

Not just that: using the Docker scan command on your Dockerfile will execute a vulnerability scan.

 Using the docker scan command on your Dockerfile will execute a vulnerability scan.

4. Leverage HEALTHCHECK

The HEALTHCHECK directive instructs Docker on how to check the health of a container. For example, this may be used to determine whether or not a web server is in an endless loop and unable to accept new connections, even while the server process is still active.

 
# syntax=docker/dockerfile:1.4

 FROM node:lts-buster-slim AS development

 # Create app directory

WORKDIR /usr/src/app

 COPY package.json ./package.json

COPY package-lock.json ./package-lock.json

RUN npm ci

 COPY . .

 EXPOSE 3000

 CMD [ "npm", "run", "dev" ]

 FROM development as dev-envs

RUN <<EOF

apt-get update

apt-get install -y --no-install-recommends git

EOF

 RUN <<EOF

useradd -s /bin/bash -m vscode

groupadd docker

usermod -aG docker vscode

EOF

 HEALTHCHECK CMD curl --fail http://localhost:3000 || exit 1 

  # install Docker tools (cli, buildx, compose)

COPY --from=gloursdocker/docker / /

CMD [ "npm", "run", "dev" ]


In the production stage, applications are often managed by an orchestrator such as Kubernetes or a service fabric. HEALTHCHECK allows you to inform the orchestrator about the health of your containers, which may be used for configuration-based management.

Here's a case in point:

 
BACKEND:

    CONTAINER_NAME: BACKEND

    RESTART: ALWAYS

    BUILD: BACKEND

    VOLUMES:

      - ./BACKEND:/USR/SRC/APP

      - /USR/SRC/APP/NODE_MODULES

    DEPENDS_ON:

      - MONGO

    NETWORKS:

      - EXPRESS-MONGO

      - REACT-EXPRESS

    EXPOSE:

      - 3000

    HEALTHCHECK:

      TEST: ["CMD", "CURL", "-F", "HTTP://LOCALHOST:3000"]

      INTERVAL: 1M30S

      TIMEOUT: 10S

      RETRIES: 3

      START_PERIOD: 40S


5. Use .dockerignore

We suggest creating a .dockerignore file in the same folder as your Dockerfile to improve build times. This guide requires a single line in your.dockerignore file:

 
NODE_MODULES


The node modules directory, which includes Maven's output, is not included in the Docker build context, thanks to this line. There are numerous advantages to having a well-organized .dockerignore file, but for the time being, this simple file will suffice.

Next, I'll describe the built environment and why it's so important. Docker images may be created using the Docker build command by combining a Dockerfile and a "context." In this setting, everything you do applies to the directory structure or URL you just gave me. Any of these files may be used in the construction process.

Meanwhile, the Node developer operates in the compilation context. A directory on Mac, Windows, or Linux. Everything required to run the program may be found in this folder, including the source code, settings, libraries, and plugins. 

If you provide a. dockerignore file, we may use it to skip over certain parts of your project while creating your new image: code, configuration files, libraries, plugins, etc. For example, if you want to keep the node modules directory out of your build, you may do so by adding the following to your .dockerignore file.

Backend

Backend 

Frontend

Frontend

6. Run as a Non-Root User for Security Purposes

It is safer to run apps with the user's permission since this helps reduce vulnerabilities - even with Docker containers. Docker containers and their contents automatically get root access to the host system. That's why it's recommended to never run Docker containers as the root user.

This may be accomplished by including certain USR directives in your Dockerfile. When executing the image and for any future RUN, CMD, or ENTRYPOINT instructions, the USR command specifies the desired user name (or UID) and, optionally, the user group (or GID):

 
FROM NODE:LTS-BUSTER AS DEVELOPMENT

 

WORKDIR /USR/SRC/APP

 

COPY PACKAGE.JSON ./PACKAGE.JSON

COPY PACKAGE-LOCK.JSON ./PACKAGE-LOCK.JSON

 

RUN NPM CI

 

COPY . .

 

EXPOSE 3000

 

CMD ["NPM", "START"]

 

FROM DEVELOPMENT AS DEV-ENVS

RUN <<EOF

APT-GET UPDATE

APT-GET INSTALL -Y --NO-INSTALL-RECOMMENDS GIT

EOF

 

RUN <<EOF

USERADD -S /BIN/BASH -M VSCODE

GROUPADD DOCKER

USERMOD -AG DOCKER VSCODE

EOF

# INSTALL DOCKER TOOLS (CLI, BUILDX, COMPOSE)

COPY --FROM=GLOURSDOCKER/DOCKER / /

CMD [ "NPM", "START" ]

7. Explore Graceful Shutdown Options for Node

Temporary storage spaces created in Docker for Node. They are easy to prevent, destroy, and then either replace or repurpose. It is possible to kill containers by giving the process the SIGTERM signal. 

In order to make the most of this brief window of opportunity, your app must be able to process incoming requests and free up any associated resources without delay.

Node.js, on the other hand, is crucial for a successful shutdown of your app since it takes and passes signals like SIGINT and SIGTERM from the OS. Because of Node.js, your app may select how to respond to the signals it receives. 

If you don't program for them or use a module that does, your app won't terminate properly. However, it will continue to operate normally until Docker or Kubernetes terminates it due to a timeout.

If you're unable to modify your application's code, you may still use the docker run —init or tini init option inside your Dockerfile. It is recommended, however, that you provide code to manage appropriate signal handling for graceful shutdowns.

Conclusion

In this tutorial, we covered a wide range of topics related to Docker image optimization, from constructing a solid Dockerfile to using Snyk to check for vulnerabilities. It's not difficult to make better Node.js applications. If you master some basic skills, you'll be in good condition.

Node.js application Docker (software) Kubernetes Testing

Opinions expressed by DZone contributors are their own.

Related

  • Common Performance Management Mistakes
  • Common Performance Management Mistakes
  • 13-Step Guide to Performance Testing in Kubernetes
  • Containerization of a Node.js Service

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!