In this tutorial, we will dockerize a Node,Express and Mongo Application. We will be using a Docker Linux container.
When our applications are ready for deployment, we have to configure a production machine with all the dependencies for the application. This is often a tedious and time consuming job. To make this approach easy, we can make use of Docker.
Note that, this article concentrates on dockerizing Node.js, Express.js and MongoDB application. This application does not explains Node.js and Express. If you want to read basics of Node and Express then please visit:
www.dotnetcurry.com/nodejs/1256/using-expressjs-implement-routing
www.dotnetcurry.com/aspnet-mvc/1135/using-nodejs-express-mongodb-meanstack-in-aspnet-mvc
www.dotnetcurry.com/tutorials/nodejs
Step 1: Create a folder and name it as men_docker. In this folder add a new folder and name it as dockernodeapp. Open the men_docker folder in VSCode. Open Node.js command prompt as Administrator (Note that I am using Windows 10 machine for this application). In the command prompt, navigate to the dockernodeapp folder and run the following command to create a package.json file
npm init –y
This command will create a package.json file. Modify this file as shown in the following code listing
Listing 1: package.json with all dependencies
Note that the scripts block in the above package.json has start command. This will run the server.js. We will create this file in the next step.
Step 2: In the dockernodeapp folder, add a new JavaScript file. Name this file as server.js. Add the following code in this file
Listing 2: The Express Code for implementing REST API and perform GET and POST operations against MongoDB database
The above code does the following (note that following line numbers matches with comment numbers given to code.)
Step 3: In the dockernodeapp folder, add a new file and name it as dockerfile. In this file, we need to configure all dependencies needed for Node and Express application. The following listing shows the docker file:
The dockerfile has the following specifications:
node_modules
npm-debug.log
This will make sure that, node_module folder and npm-debug.log file will not be copied in the image.
Step 4: Run the following command from the node.js command prompt. This will build the docker image for the application
docker build -t nodeexpressmongo:prod .
The –t switch is used to tag the image. This command will create and build an image as shown in the following image.
Figure 2: Downloading all dependencies
The above image shows that the required dependencies will be downloaded. Once the download is completed, the result will be shown in the following image:
Figure 3: The final step of the image build
To run the image run the following command
docker run -it -v :/usr/src/app -v /usr/src/app/node_modules -p 4007:4007 --rm nodeexpressmongo:prod
We are trying to run the image and try to map the port 4007 of the image with the port 4007 of the local machine.
The above command will show the following result:
Figure 4: Running the image
The image is running and it invokes node server.js command and listens on port 4007, but since the server.js file contains code for connecting to MongoDB database on port 27017, it throws MongoNteworkError.
So this means that we need to make sure that MongoDB must be configured on the image. But we cannot specify MongoDB image using dockerfile because it is already present on the Docker Hub. This MongoDB image has its own container. So now the challenge is how to connect our image with the MongoDB’s already available image in its container?
We can make this possible using Docker compose.
Step 5: In the men_docker folder, add a new file and name it as docker-compose.yml. In this file, add the following configuration:
The above listing has the following specifications (Note: Following numbers matches with the comments applied on the above code)
Figure 5: Pulling the Mongo image
The command will also build an image for node and express application as shown in the image 3. Once the build is successfully executed, the MongoDB Database and Express application will be started as shown in Figure 6.
Figure 6: Output of the docker-compose.yml file
The above image shows that the db instance for MongoDB is started and the express application also starts. Since the ports 4007 and 27017 are mapped with the local machine, the express API can be accessed from the following link:
http://localhost4007/api/products
Enter this link in the browser, it will show the result as following
{"status":200,"data":[]}
Open the Postman or Fiddler and perform the POST request as shown in Figure 7.
Figure 7: The POST request using Postman.
This will post the Product data to the API and will be saved in the MongoDB database. Now enter the address http://localhost:4007/api/products in the browser, it will show the result as shown in Figure 8.
Figure 8: The final result
The list of images created on the local machine can be seen using following command
Figure 9: List of images
Let’s first take an overview of Docker and then we will directly jump to the implementation of the application.
What is Docker and why to use it?
When our applications are ready for deployment, we have to configure a production machine with all the dependencies for the application. This is often a tedious and time consuming job. To make this approach easy, we can make use of Docker.
Docker provides a way to run applications securely and isolated in a container with all the applications dependencies packaged in it. Docker containers wrap up software and its dependencies into a standardized unit for software development. This makes sure that all the dependencies to run the application are packaged so that application will always run.
Some additional advantages of using Docker are:
- Once a docker image is created with all its dependencies, you need not worry about the operating system configuration for your application deployment. Since Docker is platform independent, a docker image that has successfully build can run on any platform using docker.
- This docker image can be easily shared with a client (or anyone) who wants to use the application. They can run the application simply by installing docker on their machine.
Docker can be downloaded from this link.
The dockerized application will be as arranged as shown in Figure 1.
The dockerized application will be as arranged as shown in Figure 1.
Figure 1: The structure of the application dockerization
Creating the Node, Express and MongoDB Application
The code for this application is implemented using Microsoft Visual Studio Code (VS Code). It can be downloaded from this link.
We Also need to install Node.js. It can be downloaded from this link. We will create a REST API using Express.js. This will perform GET and POST operations using MongoDB Collection.
Note that, this article concentrates on dockerizing Node.js, Express.js and MongoDB application. This application does not explains Node.js and Express. If you want to read basics of Node and Express then please visit:
www.dotnetcurry.com/nodejs/1256/using-expressjs-implement-routing
www.dotnetcurry.com/aspnet-mvc/1135/using-nodejs-express-mongodb-meanstack-in-aspnet-mvc
www.dotnetcurry.com/tutorials/nodejs
Step 1: Create a folder and name it as men_docker. In this folder add a new folder and name it as dockernodeapp. Open the men_docker folder in VSCode. Open Node.js command prompt as Administrator (Note that I am using Windows 10 machine for this application). In the command prompt, navigate to the dockernodeapp folder and run the following command to create a package.json file
npm init –y
This command will create a package.json file. Modify this file as shown in the following code listing
{ "name": "dockernodeapp", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node server.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@types/node": "10.12.18", "body-parser": "1.18.3", "cors": "^2.8.5", "express": "4.16.4", "mongoose": "^5.4.6" } }
Listing 1: package.json with all dependencies
Note that the scripts block in the above package.json has start command. This will run the server.js. We will create this file in the next step.
Step 2: In the dockernodeapp folder, add a new JavaScript file. Name this file as server.js. Add the following code in this file
// 1. load all required packages for the application var express = require("express"); var path = require("path"); var bodyParser = require("body-parser"); var mongoose = require("mongoose"); mongoose.Promise = global.Promise; var cors = require("cors"); // 2. router, body parser and cors var instance = express(); var router = express.Router(); instance.use(router); instance.use(bodyParser.urlencoded({ extended: false })); instance.use(bodyParser.json()); instance.use(cors()); // 3. connect to mongodb. since we will be configuring // mongodb on docker image, the port must be specified. // This will map from the docker-compose.yml file mongoose.connect( "mongodb://db:27017/ProductsAppsDb", { useNewUrlParser: true } ); // 4. connection with the mongodb database var dbConnect = mongoose.connection; if (!dbConnect) { console.log("Sorry Connection is not established"); return; } // 5. schema for the collection var productsSchema = mongoose.Schema({ ProductId: Number, ProductName: String, CategoryName: String, Manufacturer: String, Price: Number }); // 6 mapping with the collection var productModel = mongoose.model("Products", productsSchema, "Products"); // 7. REST Apis for get/post instance.get("/api/products", function (request, response) { productModel.find().exec(function (err, res) { if (err) { response.statusCode = 500; response.send({ status: response.statusCode, error: err }); } response.send({ status: 200, data: res }); }); }); instance.post("/api/products", function (request, response) { // parsing posted data into JSON var prd = { ProductId: request.body.ProductId, ProductName: request.body.ProductName, CategoryName: request.body.CategoryName, Manufacturer: request.body.Manufacturer, Price: request.body.Price }; // pass the parsed object to "create()" method productModel.create(prd, function (err, res) { if (err) { response.statusCode = 500; response.send(err); } response.send({ status: 200, data: res }); }); }); // 8. listen on the port instance.listen(4007, function () { console.log("started listening on port 4007"); });
Listing 2: The Express Code for implementing REST API and perform GET and POST operations against MongoDB database
The above code does the following (note that following line numbers matches with comment numbers given to code.)
- Since we will be using Express, MongoDB, Body-Parser and Mongoose for application implementation, we need to load these modules in the application.
- This step defines an express instance and configures body-parser object to it so that Http Post operation can be implemented.
- This step will connect to MongoDB database. Since we will be configuring the MongoDB database image, we need to make sure that our application should connect to the MongoDB database instance on the port 27017. In the connect() method we have mentioned the MongoDB Connection string as mongodb://db:27017/ProductsAppsDb. The db here is the MongoDB image service which represents the MongoDB database instance running and accessible on the port 27017.
- This steps make sure that the connection with the database is established successfully.
- Once the database is created, we need the collection in it. This step defines the expected schema for the collection.
- This step will map the schema with the MongoDB collection. If the Products collection is not already present then it will be created.
- This step defines the use of express methods for performing Http GET and POST
- This step will start listening on port 4007
FROM node:10.15.0 # 1. Creating the app directory WORKDIR /usr/src/app # 2. Install app dependencies for the application # A package*.json is used to make sure the # package.json, package-lock.json are to be copied COPY package*.json ./ # 3 RUN npm install --only=production for production RUN npm install --only=production # Bundle the app source COPY . . EXPOSE 4007 CMD [ "npm", "start" ]Listing 3: docker file
The dockerfile has the following specifications:
- Since we need an image with node.js installed on it we specify node.js version.
- we are setting working directory as /user/src/app. This will be working directory for all our commands.
- copy package.json and package-lock.json file in the working directory. The package.json contains list of all dependencies for the application.
- run the npm install –only=production. This will install all dependencies for the application.
- bundle application source as the original folder structure of the application using COPY . . command.
- Using EXPOSE command we are specifying that the port 4007 will be exposed from the image for the application.
- Using CMD command we are specifying that the npm start command to run server.js from the image so that the application will start listening on 4007.
node_modules
npm-debug.log
This will make sure that, node_module folder and npm-debug.log file will not be copied in the image.
Step 4: Run the following command from the node.js command prompt. This will build the docker image for the application
docker build -t nodeexpressmongo:prod .
The –t switch is used to tag the image. This command will create and build an image as shown in the following image.
Figure 2: Downloading all dependencies
The above image shows that the required dependencies will be downloaded. Once the download is completed, the result will be shown in the following image:
Figure 3: The final step of the image build
To run the image run the following command
docker run -it -v :/usr/src/app -v /usr/src/app/node_modules -p 4007:4007 --rm nodeexpressmongo:prod
We are trying to run the image and try to map the port 4007 of the image with the port 4007 of the local machine.
The above command will show the following result:
Figure 4: Running the image
The image is running and it invokes node server.js command and listens on port 4007, but since the server.js file contains code for connecting to MongoDB database on port 27017, it throws MongoNteworkError.
So this means that we need to make sure that MongoDB must be configured on the image. But we cannot specify MongoDB image using dockerfile because it is already present on the Docker Hub. This MongoDB image has its own container. So now the challenge is how to connect our image with the MongoDB’s already available image in its container?
We can make this possible using Docker compose.
Docker Compose
Compose is a tool for defining and running multi-container Docker applications. We need to to create docker-compose.yml file. This compose file can be used to configure an application services, this file will execute dockerfile for individual application service and then download dependencies for image or pull already available image.Step 5: In the men_docker folder, add a new file and name it as docker-compose.yml. In this file, add the following configuration:
# 1. specify docker-compose version version: "3.0" # 2. Defining the application containers to be run services: # 2a. name of the application express: # 2b. specify the directory of the application containing of the Dockerfile build: dockernodeapp ports: # 2c. specify ports mapping - "4007:4007" links: # 2d. link this service to the database service - db # 2e. name of the database service db: # 2f. specify image to build container from mongo image: mongo ports: # 2g. specify port mapping for the database - "27017:27017"Listing 4: The docker-compose.yml file
The above listing has the following specifications (Note: Following numbers matches with the comments applied on the above code)
- Specify the docker-compose version. Please read about it from this link.
- Define the services block. This is used to define services that makes up our application - in our case its express application and mongo. These services can run in an isolated environment.
Step 6: Run the following command to execute docker-compose.yml. This will build images and will make them ready to access:
docker compose up
This command will pull the Mongo from the docker hub repository as shown in Figure 5.
Figure 5: Pulling the Mongo image
The command will also build an image for node and express application as shown in the image 3. Once the build is successfully executed, the MongoDB Database and Express application will be started as shown in Figure 6.
Figure 6: Output of the docker-compose.yml file
The above image shows that the db instance for MongoDB is started and the express application also starts. Since the ports 4007 and 27017 are mapped with the local machine, the express API can be accessed from the following link:
http://localhost4007/api/products
Enter this link in the browser, it will show the result as following
{"status":200,"data":[]}
Open the Postman or Fiddler and perform the POST request as shown in Figure 7.
Figure 7: The POST request using Postman.
This will post the Product data to the API and will be saved in the MongoDB database. Now enter the address http://localhost:4007/api/products in the browser, it will show the result as shown in Figure 8.
Figure 8: The final result
The list of images created on the local machine can be seen using following command
Figure 9: List of images
Conclusion:
Dockerization of an application makes it easy to share the application across different platforms without worrying about the application dependencies on the target production machine.
No comments:
Post a Comment