Docker for Developers - Load Balance using Nginx

In the previous article of the Docker series, you learnt how to setup a Node.js app. Let's take the example forward by load balancing the Node.js application. Before introducing Nginx as a reverse proxy, I want you to check some subtle changes in 3 files of this commit:

Series

Part 1: Docker for Developers - Setting up your developer machine
Part 2: Docker for Developers - Setting up MongoDB
Part 3: Docker for Developers - Setting up Node.js
Part 4: You are HERE

Checkout the commit
$ cd nginx-and-nodejs-on-docker
$ git checkout 2e3a77e
.env File

The container names are changed:
.env file

docker-compose.yml

A new service called node_server_2 is created and the initial server is renamed to node_server_1.
docker-compose.yml file

web/server.js

This is an optional change. x-powered-by header that defaults to EXPRESS is disabled, and a new header X-Server is added to find out which container served the request.
web/server.js file

Rebuild & Recreate containers

As usual, rebuild using docker-compose build followed by docker-compose up -d. Once the containers are up, you should be able to browse to http://192.168.99.100:81/users & http://192.168.99.100:82/users. Check the headers using Chrome inspect tool. Notice the X-Server header.

header using Chrome Inspect tool

Add Nginx for load balancing

Nginx is super light and a great fit for reverse proxy scenarios like this one. If you are unfamiliar with Nginx, you may want to read my book Nginx: From Beginner to Pro. It is extremely simple and straightforward to use it with Docker.

Let's Dive in

Step 1: Change your directory and checkout the commit

$ cd nginx-and-nodejs-on-docker
$ git checkout bd9e91f

OR view the differences online.

Review the following files:

.env file

It now contains the information about NGINX

# Project Information
COMPOSE_PROJECT_NAME=node-nginx-seed  
TAG=1.0

# Database Information
DATABASE_VERSION=mongo:3.4.8  
DATABASE_CONTAINER_NAME=mongodb

# NodeJS Information
NODE_VERSION=node:6.11.3  
NODE_CONTAINER_NAME_1=node_server_1  
NODE_CONTAINER_NAME_2=node_server_2

# Nginx Information
NGINX_VERSION=nginx:1.13.3  
NGINX_CONTAINER_NAME=nginx  
docker-compose.yml file
version: '2'

services:  
  database:
    image: ${DATABASE_VERSION}
    networks:
      - backend
    container_name: ${DATABASE_CONTAINER_NAME}
    volumes:
      - mongo-data:/data/db
      - ./docker/scripts:/scripts
      - ./docker/data:/data

  node_server_1:
    environment:
      - NODE=${NODE_CONTAINER_NAME_1}
    container_name: ${NODE_CONTAINER_NAME_1}
    image: ${NODE_VERSION}
    build:
      context: ./web
      dockerfile: node.dockerfile
    networks:
      - backend
    volumes:
      - ./web:/web
    depends_on:
      - database

  node_server_2:
    environment:
      - NODE=${NODE_CONTAINER_NAME_2}
    container_name: ${NODE_CONTAINER_NAME_2}
    image: ${NODE_VERSION}
    build:
      context: ./web
      dockerfile: node.dockerfile
    networks:
      - backend
    volumes:
      - ./web:/web
    depends_on:
      - database

  nginx:
    container_name: ${NGINX_CONTAINER_NAME}
    image: ${NGINX_VERSION}
    build:
      context: ./nginx
      dockerfile: nginx.dockerfile
    networks:
      - backend
    ports:
      - 80:80
    depends_on:
      - node_server_1
      - node_server_2
      - database

networks:  
  backend:
    driver: bridge

volumes:  
  mongo-data:

Notice the following changes:

  • ports key has been removed from individual node servers. This is to ensure that you can't reach the Node.js application directly. It avoids exposing your application to the end users.
  • depends_on key is added to imply dependencies between services.
    • docker-compose up starts services in dependency order.
    • docker-compose up SERVICE_NAME will automatically include SERVICE’s dependencies.
  • nginx service sets the context to the nginx directory and refers to the nginx.dockerfile for its image. It is connected to the backend network so that it is able to talk to the Node.js apps and maps external port 80 to the internal port 80.
nginx.dockerfile file
FROM nginx:1.13.3  
MAINTAINER Rahul Soni <rahul@attosol.com>

# Copy the Nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Expose website on port
EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]  

To avoid the container being shutdown, you tell Nginx to run as a foreground process and listen to the requests.

Rebuild & Recreate containers

Rebuild again using docker-compose build followed by docker-compose up -d. Once the containers are up, you should be able to browse to http://192.168.99.100/users. Check the headers using Chrome inspect tool. Notice the X-Server header. You should see it switch values between node_server_1 and node_server_2 if you refresh the page.

What next?

I hope this series has been helpful to you thus far. Stay tuned for upcoming articles. Say hi, share this article, leave a comment or Subscribe now to stay updated through our monthly newsletter. Also, check out our services or contact us at contact@attosol.com for your software and consultancy requirements.

Happy Docking!

Ads:

Rahul Soni

⌘⌘ Entrepreneur. Author. Geek. ⌘⌘

Kolkata, India

Subscribe to Attosol Technologies

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!