Getting started with Docker for NodeJs, MongoDB and Redis

Ashok Vishwakarma
Ashok Vishwakarma

Oct 17, 2018 • 5 min read

Every application we develop rely on several dependencies like platform, database, tooling and technology. Most of my applications require NodeJS, MongoDB and Redis along with NPM to run. We all have installed our favourite software on our machines and we love to use them in most of our applications.

But have you ever face any challenges to having the same software in other environments and machines like dev, beta or production or may some of our co-developers who are working with us on the same application?

Well, most of the time they all have their favourite software installed with the specific versions they like and keeping them in sync is nearly impossible.

Docker is the answer to all questions related to software dependencies for application to develop, test and deploy applications faster.

Introduction to Docker

As per Wikipedia

Docker is a computer program that performs operating-system-level virtualization, also known as “containerization”. It was first released in 2013 and is developed by Docker, Inc. Docker is used to run software packages called “containers”.

In a simple word, Docker is a platform help you to package your application and all its dependencies in a container and ensure that your application runs on any platform or machine.

The Dockerfile

A Dockerfile is a plain text document containing all the required step by step commands to test, build or run the application.

The docker-compose.yml

A docker-compose.yml is a YML file containing the required configuration for each container in the multi-container Docker environment.

In our below example we will be using both to build and run our application.

Install Docker

To install docker on your machine follow the below links

For more details visit https://docs.docker.com/

Getting started

To get started, let's define the core dependencies we will be needing for our next awesome application.

Dependencies

  • Node ≥ 10.x
  • MongoDB ≥ 4.x
  • Redis ≥ 4.x

Docker compose file

version: '3'
services:
  myapp:
    container_name: myapp
    restart: always
    build: .
    ports:
      - '4300:4300'
      - '4301:4301'
    links:
      - redis
      - mongo
  mongo:
    container_name: myapp-mongo
    image: 'mongo:4'
  redis:
    container_name: myapp-redis
    image: 'redis:4.0.11'

In the above docker-compose.yml I have defined the specific container services my application is required. The very first service is myapp having container name myapp and exposed the ports 4300 and 4301 which will build the current directory using Dockerfile

The links property in myapp service will be used to connect with these two (mongo and redis) services within the app.

The other two services are mongo named as myapp-mongo and redis named as myapp-redis. These two services run an image mentioned in image property with their specific version.

Dockerfile

# Install node v10
FROM node:10

# Set the workdir /var/www/myapp
WORKDIR /var/www/myapp

# Copy the package.json to workdir
COPY package.json ./

# Run npm install - install the npm dependencies
RUN npm install

# Copy application source
COPY . .

# Copy .env.docker to workdir/.env - use the docker env
COPY .env.docker ./.env

# Expose application ports - (4300 - for API and 4301 - for front end)
EXPOSE 4300 4301

# Generate build
RUN npm run build

# Start the application
CMD ["npm", "run", "run:prod"]

In the above Dockerfile the node:10 the image is taken on which our application will be running as myapp container service in docker-compose.yml, set the working directory to /var/ww/myapp docker will create the directory if not exists.

Copy the package.json from main machine root (where Dockerfile lives) to the myapp container’s working directory.

Run npm install into myapp container to install the npm dependencies.

Copy the .env.docker file to the working directory and renamed it as .env The .env.docker the file contains connection information for mongo and redis containers.

Expose the ports 4300 and 4301 on which applications (API and Frontend) are running.

Run npm run build to generate application builds.

Start the application with CMD command.

Docker commands

Usage:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file
                              (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name
                              (default: directory name)
  --verbose                   Show more output
  --log-level LEVEL           Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  --no-ansi                   Do not print ANSI control characters
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the
                              name specified in the client certificate
  --project-directory PATH    Specify an alternate working directory
                              (default: the path of the Compose file)
  --compatibility             If set, Compose will attempt to convert deploy
                              keys in v3 files to their non-Swarm equivalent

Commands:
  build              Build or rebuild services
  bundle             Generate a Docker bundle from the Compose file
  config             Validate and view the Compose file
  create             Create services
  down               Stop and remove containers, networks, images, and volumes
  events             Receive real time events from containers
  exec               Execute a command in a running container
  help               Get help on a command
  images             List images
  kill               Kill containers
  logs               View output from containers
  pause              Pause services
  port               Print the public port for a port binding
  ps                 List containers
  pull               Pull service images
  push               Push service images
  restart            Restart services
  rm                 Remove stopped containers
  run                Run a one-off command
  scale              Set number of containers for a service
  start              Start services
  stop               Stop services
  top                Display the running processes
  unpause            Unpause services
  up                 Create and start containers
  version            Show the Docker-Compose version information

The magic myapp.sh

To make things easy let’s setup and bash script to run myapp application.

#!/bin/bash

CLEAN="clean"
RUN="run"
STOP="stop"

if [ "$#" -eq 0 ] || [ $1 = "-h" ] || [ $1 = "--help" ]; then
    echo "Usage: ./myapp [OPTIONS] COMMAND [arg...]"
    echo "       ./myapp [ -h | --help ]"
    echo ""
    echo "Options:"
    echo "  -h, --help    Prints usage."
    echo ""
    echo "Commands:"
    echo "  $CLEAN      - Stop and Remove imoney containers."
    echo "  $RUN        - Build and Run imoney."
    echo "  $STOP       - Stop imoney."
    exit
fi

clean() {
  stop_existing
  remove_stopped_containers
  remove_unused_volumes
}

run() {
  echo "Cleaning..."
  clean
  
  echo "Running docker..."
  docker-compose up --build
}

stop_existing() {
  MYAPP="$(docker ps --all --quiet --filter=name=myapp)"
  REDIS="$(docker ps --all --quiet --filter=name=myapp-redis)"
  MONGO="$(docker ps --all --quiet --filter=name=myapp-mongo)"

  if [ -n "$MYAPP" ]; then
    docker stop $MYAPP
  fi

  if [ -n "$REDIS" ]; then
    docker stop $REDIS
  fi

  if [ -n "$MONGO" ]; then
    docker stop $MONGO
  fi
}

remove_stopped_containers() {
  CONTAINERS="$(docker ps -a -f status=exited -q)"
	if [ ${#CONTAINERS} -gt 0 ]; then
		echo "Removing all stopped containers."
		docker rm $CONTAINERS
	else
		echo "There are no stopped containers to be removed."
	fi
}

remove_unused_volumes() {
  CONTAINERS="$(docker volume ls -qf dangling=true)"
	if [ ${#CONTAINERS} -gt 0 ]; then
		echo "Removing all unused volumes."
		docker volume rm $CONTAINERS
	else
		echo "There are no unused volumes to be removed."
	fi
}

if [ $1 = $CLEAN ]; then
  echo "Cleaning..."
	clean
	exit
fi

if [ $1 = $RUN ]; then
	run
	exit
fi

if [ $1 = $STOP ]; then
	stop_existing
	exit
fi

Commands in myapp.sh

Usage:
   ./myapp [OPTIONS] COMMAND [arg...]
   ./myapp [ -h | --help ]
Options:
   -h, --help  print usage.
Commands:
   clean - Stop and Remove all containers.
   run   - Build and Run myapp.
   stop  - Stop myapp.

Conclusion

It’s been almost two and a half year since I am using Docker in all of my projects and it really helps me develop and deploy applications faster.

Whenever a new developer on-board to any of my application all he had to do is install docker on his machine and Docker will take care of everything else.

No more version issues and no worries for dependencies.

Let me know how Docker helps you with your applications.

Ashok Vishwakarma

Ashok Vishwakarma

Google Develover Expert — WebTechnologies and Angular | Principal Architect at Naukri.com | Entrepreneur | TechEnthusiast | Speaker