Docker – Docker Container for Node.js
Node.js is an open-source, asynchronous event-driven JavaScript runtime that is used to run JavaScript applications. It is widely used for traditional websites and as API servers. At the same time, a Docker container is an isolated, deployable unit that packages an application along with its dependencies, making it highly scalable and maintainable. In this article, we will create a Docker container for node.js and run a simple express.js application on the container.
What is Docker Container ?
A Docker is an open platform that provides the Docker runtime. It makes the development, running, and deployment of applications straightforward. A Docker container is quite popular because it is lightweight and efficient in using the underlying system resources. It shares the host OS’s kernel, unlike a VM machine which makes it faster and capable of handling many simultaneous requests. Read here to learn more about docker.
What is Node.js ?
Node.js is a server environment that is open-source and free to use. It provides the JavaScript runtime so that JavaScript applications can run on the server. It also enables JavaScript code to run outside a browser. It does a lot more, like generating dynamic page content, creating, saving, and closing files on the server, and handling databases in the backend.
Why use Docker Container for Node.js ?
One of the problems developers face is having different environments for the development of an application and the deployment of the same. One might develop their own local system, which might be totally different from a cloud instance where the application is finally deployed.
A docker container solves this problem, and It uses the concept of images that store a record of a docker container at a particular time, like all the libraries. It is required to run an application along with their versions. Then a developer can share this image with others to have the same environment as the developer does and run the application.
A docker container is the best tool for microservice to learn more about Docker architecture adaptation. Many companies are shifting to Docker because an application running on docker is more maintainable, easily modifiable, and highly scalable.
Step by Step to Dockerize Node.js Application
Step 1: Dockerized a Node.js web app
Let’s start with an effortless express application that prints “Hello World! This is Node.js from a docker container” on visiting the root endpoint.
To create a folder named express_app and move inside the folder using the following commands.
mkdir express_app
cd express_app
Now, create a file named app.js as follows.
-
mkdir stands for “make directory”.
-
express_app is the name of the directory you’re creating.
-
cd stands for “change directory”.
-
express_app is the directory you want to change into.
Step 2: Create the Node.js app
Create a file that consists of all the files and dependencies required that describe your app and name it as a "package.json".
{
"name": "docker-example",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"nodemon": "^2.0.12"
}
}
Now, initialize the node project using the following command.
npm init
This will add the package.json file, which holds information about our projects like scripts, dependencies, and versions. It will ask for the package’s name, version, and many others (you can go with defaults by pressing ENTER).
-
npm: This stands for Node Package Manager. It’s a tool that comes bundled with Node.js, and it’s used to manage packages (bits of code that you can use in your own projects) for Node.js. npm helps you install, uninstall, update, and manage these packages.
-
init: This is short for “initialize”. When you run npm init, you’re asking npm to help you set up the initial configuration for your project. Essentially, you’re saying, “I want to start a new project. Please guide me through the setup process.”
Install the Express library and add it to the package.json file as a dependency.
npm install --save express
-
npm: Stands for Node Package Manager. It’s a tool that helps developers manage and install packages (which are like plugins or libraries) for their Node.js applications.
-
install: This is the command you’re giving npm. You’re telling it that you want to install something.
-
-–save: This part is saying that you want to save the package you’re installing as a dependency in your project’s package.json file. The package.json file keeps track of all the dependencies your project needs to run. Saving the package as a dependency means that if someone else wants to run your project, they can just look at the package.json file, see what packages are needed, and install them easily.
-
express: This is the name of the package you want to install. In this case, it’s a popular package for creating web servers and building web applications with Node.js. When you install it, npm will fetch all the necessary files and put them in a folder called node_modules in your project directory. This folder contains all the code for the packages you’ve installed.
Install a tool called nodemon that automatically restarts the node application when it detects any changes.
npm install --save nodemon
We are explicitly adding these dependencies to our package.json file to download them when we run this application inside a docker container.
Add a script to the scripts part of the package.json file to run the application with nodemon.
Step 3: Express.js
Express is a framework for Node.js that helps developers organize their web applications into MVC architecture on the server side. It makes web applications fast and easy as compared to developing an application using only Node.js.
For example, A node.js application might need MongoDB in the backend, and Express is the framework that helps manage everything like routes, requests, and views.
Then, create a server.js file that defines a web app using the Express.js framework:
// import and create an express app
const express = require('express');
const app = express()
// message as response
msg = "Hello world! this is nodejs in a docker container.."
// create an end point of the api
app.get('/', (req, res) => res.send(msg));
// now run the application and start listening
// on port 3000
app.listen(3000, () => {
console.log("app running on port 3000...");
})
At this stage, we can run our application on our local system using the following command:
npm run start
But actually, we want to dockerize this application. For that, we need to create an image by providing information like which runtime we need, the port the app will use, and the files needed that are available on our local system.
-
npm: npm is a command-line tool that comes bundled with Node.js. It’s used for managing Node.js packages and dependencies, and also for running scripts defined in the package.json file.
-
run: This tells npm that you want to execute a script.
-
start: This is the name of the script you want to run. It’s a special name recognized by npm. You can customize it, but "start" is a convention for running the main entry point of your application.
-
Create a Dockerfile that contains all the information about the image that will run the application. The docker software understands this special file, and it is used to build an image.
Step 4: Create a Dockerfile in your Node.js app project
First, create a file name as Dockerfile.
FROM node:latest
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
CMD ["npm", "start"]
Explanation of Dockerfile
-
The FROM takes the name of the base image to use optionally with its version.
-
WORKDIR states the directory that holds the files of the application in the container.
-
COPY command copies the package.json file to the app directory.
-
The RUN command runs the provided command to install all the dependencies mentioned in the package.json file.
-
Then COPY is used to copy the rest of the files to the app directory in the container.
-
Finally, we provide the script to run the application.
The folder structure after creating all the required files is as follows:
Finally, use this command to build the image we will run in our docker container.
Step 5: Building Your Image
After creating a Dockerfile build an image using the help Dockerfile by using the following command.
docker build -t docker-container-nodejs .
-
docker build: This is the command used to build a Docker image. It tells Docker to build an image based on the instructions provided in the Dockerfile.
-
-t docker-container-nodejs: The -t flag is short for “tag,” and it is used to specify a name and optionally a tag for the image being built. In this case, we’re giving the image the name “docker-container-nodejs”. The name is typically in the format
/ , where docker-container-nodejs is the image name. -
.: The dot . at the end of the command represents the build context. It tells Docker to look for the Dockerfile and any other necessary files in the current directory. The build context is where Docker looks for files referenced in the Dockerfile, such as source code, dependencies, and configuration files.
The command uses the flag -t to specify the name of the image, and then we have to give the address where. Then, our Dockerfile is situated; since we are in the directory while running the commands, we can use the period, which stands for the current directory.
Confirm that the image has been created.
docker images
The output will be a list of images in your system. It should contain our recently created image with the name we provided with the -t flag.
To run the docker container with this image, use the following command.
Step 6: Run the image
Run the image as a container and deploy the application in the form of containers.
docker run -d -p 8000:3000 -v address_to_app_locally:/app docker-container-nodejs
The above command runs a docker container. The flag -p is used to map the local port 8000 to the container’s port 3000 where our application is running. The -v flag is used to mount our application files into the app directory of the container. It also needs the image name that we want to run in our container, which is, in this case, docker-container-nodejs that.
-
docker run: This command is used to create and start a new Docker container from an image.
-
-d: This flag stands for “detached mode”. It runs the container in the background, so your terminal is free for other commands.
-
-p 8000:3000: This option maps a port on your host machine to a port inside the Docker container.
- 8000: The port on your host machine (i.e., your computer).
- 3000: The port inside the Docker container where the Node.js application is running.
- This means that when you access http://localhost:8000 on your host machine, it will be forwarded to http://localhost:3000 inside the container.
-
-v address_to_app_locally:/app: This flag mounts a volume, linking a directory on your host machine to a directory inside the container.
- address_to_app_locally: The path to your application’s directory on your host machine.
- /app: The directory inside the Docker container where the application will be available.
- This allows you to develop your application locally and see changes inside the container without rebuilding it.
-
docker-container-nodejs: This is the name of the Docker image from which the container is created. Make sure you have built or pulled this image before running the command.
Step 7: Test the Application
Visit this address localhost:8000, and our express application will return the following response.
Using Docker Compose for node
version: '3.8'
services:
app:
build: .
ports:
- "8000:3000"
volumes:
- .:/app
command: npm start
-
version: '3.8': Specifies the version of the Docker Compose file format. Version 3.8 is commonly used and supports the latest features.
-
services: Defines the services that make up your application. Here, we have one service named app.
-
app: The name of the service. This service represents your Node.js application.
-
build: .: Instructs Docker Compose to build an image from the Dockerfile located in the current directory (.).
-
ports: Maps ports between the host machine and the Docker container.
- "8000:3000": Maps port 8000 on your host machine to port 3000 inside the Docker container. When you access http://localhost:8000 on your host machine, it forwards the request to port 3000 in the container where the Node.js app is running.
-
volumes: Mounts a directory from the host machine into the Docker container.
- .:/app: Mounts the current directory on your host machine (.) to the /app directory inside the container. This allows you to edit your code on your host machine, and the changes will be reflected inside the container.
-
command: Specifies the command to run within the container.
- npm start: Runs npm start, which typically starts your Node.js application.
Docker Node best practices
-
Use Official Node Images: Start with a lightweight base image like node:alpine for smaller image sizes.
-
Leverage Multi-Stage Builds: Use multi-stage builds to reduce the final image size by only including necessary files.
-
Cache Dependencies: Copy package.json and package-lock.json first and run npm install before copying other files to leverage Docker’s layer caching.
-
Set Working Directory: Use WORKDIR to set a working directory inside the container.
-
Use .dockerignore: Create a .dockerignore file to exclude unnecessary files and directories from the build context.
-
Run as Non-Root User: Add a non-root user inside the Dockerfile and switch to it for better security.
-
Optimize CMD: Use CMD ["node", "your-app.js"] to define the default command for the container.
Conclusion
In this article, we learned about Docker containers, images, and their benefits. Then we learned how to create an image that will run on a docker container. Finally, we created a small Express application to demonstrate how to run an application using Node.js running in a docker container.