This blog post demonstrates how to deploy a Node.js web application, which uses the Redis server using Docker compose. The Redis server is used to store the number of visitors to our Node.js application.
Pre-requisites
As we are deploying the app using Docker and Docker compose we need to install this software first. Here am using Ubuntu 18.04 as my hosting server.
Docker installation
To install Docker on the Ubuntu machine execute the below commands.
- Setup the repository to install additional packages.
$ sudo apt-get update
$ sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
2. Add Docker’s official GPG key:
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
3. Set up the stable repository.
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
4. Install Docker engine.
$ sudo apt-get update $ sudo apt-get install docker-ce docker-ce-cli containerd.io
5. Check the installed Docker version.
$ sudo docker --version Docker version 20.10.11, build dea9396
Docker 20.10.11 has been installed successfully.
6. Install Docker compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
7. Apply execution permission to the binary.
sudo chmod +x /usr/local/bin/docker-compose
8. Create a symlink to the /usr/bin directory.
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
9. Confirm the installed docker-compose version with the below command
docker-compose --version
docker-compose version 1.29.2, build 5becea4c
Create the application
The Docker and docker-compose installation has been completed, next, we have to code our application, for this I will create a directory called “mywebapp”. Inside this folder create a file called package.json, this file contains the configurations for our application. Copy and paste the below code to the package.json file.
{
“dependencies”: {
“express”: “*”,
“redis”: “2.8.0”
},
“scripts”: {
“start”: “node index.js”
}
}
In the above code snippet, I have mentioned the dependencies are express ( * means any version of it) and Redis (version 2.8.0). Under the script, I mentioned “start index.js” this is command which start our node.js application.
Next, create another file called index.js in the same folder, which is the index file of the node.js application, this file we mentioned in the package.json file.
const express = require('express');
const redis = require('redis');
const app = express();
const client = redis.createClient({
host: 'redis-server',
port: 6379,
});
client.set('visits', 0);
app.get('/', (req, res) => {
client.get('visits', (err, visits) => {
res.send('Number of visits ' + visits);
client.set('visits', parseInt(visits) + 1);
});
});
app.listen(8081, () => {
console.log('listening on port 8443');
});
The above code creates a website and displays the number of hits to the site. In the code, we have used the arguments host and port to connect to the Redis server. We have created our website and the next step is to build the Docker image. This image will be used to created a container for our node.js application.
Build Docker image
To build a docker image, create a file called Dockerfile in the same folder and copy-paste the below code.
FROM node:alpine WORKDIR '/app' COPY package.json . RUN npm install COPY . . CMD ["npm","start"]
This docker file combines the index.js and package.json files together. The Docker file is using a node base image (alpine version) and creates a working directory called “/app”. It copies the package.json file from the “mywebapp” folder to the image. “RUN npm install” install all the dependencies mentioned and the COPY command will copy all files (index.js) to the container image and the last command CMD will start the node.js server as a container.
As I mentioned first, our application uses a Redis server to store the number of hits to the application, for this we need a Redis server. We will not build a custom Redis server instead we use the existing Redis image which is present in the Docker Hub.
Now, we have two Docker images and we can run two containers (Node app and Redis server) from these images but how we can connect the Node app and Redis server? to establish this we will use docker-compose. Docker-compose will spin up the container in the same network (docker network) and open the mentioned ports as well. It helps to connect both containers
Create Docker-compose file
Create another file named docker-compose.yaml inside the same directory and copy-paste the below code.
version: '3'
services:
redis-server:
image: 'redis'
node-app:
build: .
ports:
- '8443:8080'
$ docker-compose up --build
The command will automatically download base images, build images with Dockerfile, create network and run the container. Adding sample output for reference.
azureuser@docker-machine
:~/mywebapp$ sudo docker-compose up --build
Creating network "mywebapp_default" with the default driver
Pulling redis-server (redis:)...
latest: Pulling from library/redis
e5ae68f74026: Pull complete
37c4354629da: Pull complete
Status: Downloaded newer image for redis:latest
Building node-app
Sending build context to Docker daemon 5.12kB
Step 1/6 : FROM node:alpine
alpine: Pulling from library/node
Status: Downloaded newer image for node:alpine
---> bb1fcdaff936
Step 2/6 : WORKDIR '/app'
---> Running in cf8db57a40b5
Removing intermediate container cf8db57a40b5
---> 4bf1d37e2859
Step 3/6 : COPY package.json .
---> 5f85cdec800a
Step 4/6 : RUN npm install
---> Running in 468a9c7d4271
Creating mywebapp_redis-server_1 ... done
Creating mywebapp_node-app_1 ... done
Attaching to mywebapp_node-app_1, mywebapp_redis-server_1
redis-server_1| 1:M 10 Dec 2021 10:16:52.479 * Ready to accept connections
node-app_1|
node-app_1| > start
node-app_1| > node index.js
node-app_1|
node-app_1| listening on port 8443
From the output it is clear that the docker-compose has created two containers (mywebapp_redis_server_1 and mywebapp_node_app_1) and created a network called “mywebapp_default”.
Go to the web browser and access our application with the public IP and the port (8443), http://<public_ip>:8443/ and you can see first time show the number of visits as o
Access the application multiple time and you can see the number of visits is increasing.
We have created a multi-node docker container with the help of docker-compose. To understand more about how the containers connected execute below command and list all networks in the system.
azureuser@docker-machine:~/mywebapp$ sudo docker network ls NETWORK ID NAME DRIVERSCOPE bba20c7fe1bf bridge bridgelocal ceb75a72bcca host hostlocal a44f1f58cd07 mywebapp_default bridgelocal 1ab04fdceec0 none nulllocal
Here you can see a network named mywebapp_default is created, now execute below command to get more detail about this network. (output is trimmed)
$ sudo docker network inspect mywebapp_default "Containers": { "155c0e11068f48bdcd84e07c4b8bbbae851705c6319171b6f369267c4e04e471": { "Name": "mywebapp_redis-server_1", "EndpointID": "74f5597331942acede83efa6a01e7b4cedcfe9a4ecc808a7fae557b7ad02024c", "MacAddress": "02:42:ac:16:00:03", "IPv4Address": "172.22.0.3/16", "IPv6Address": "" }, "557f8f0d936744e84a085d19e7bed6b3e976b8d5aeb46d105302973fd986e0ea": { "Name": "mywebapp_node-app_1", "EndpointID": "341721094df29832e18bfcd03614c81e0cfeaf07e1c78a1feaf5b25fc9b6dafb", "MacAddress": "02:42:ac:16:00:02", "IPv4Address": "172.22.0.2/16", "IPv6Address": "" }
Look closely at the command output and you can see both containers mywebapp_redis-server_1 (172.22.0.3/16) and mywebapp_node-app_1 (172.22.0.2/16) have the same network range address.