Nginx Load Balancer with docker for nodejs services
Nginx Load Balancer with docker has become a fine recipe for managing services in closed systems. Although services like Kubernetes provide container orchestration, sometimes architecturally it could be an overkill. In this example, I will be showing how to use docker services using Nginx load balancer.
Request and Response applications
First, create a simple request and response application to test the load balancer. For the request app, I will use a simple http request which prints the load time
const fetch = require('node-fetch'); /** * Test HTTP REST */ const TEST_ROUNDS = 100; let httpTimeStarted = Date.now(); const httpTest = () => { const promises = []; for (i = 0; i < TEST_ROUNDS; i++) { promises.push(fetch('http://localhost:3000/http')); } return promises; } Promise.all(httpTest()).then((data) => { data[0].text().then((text) => { console.log('\x1b[31m', 'http response data: ', text); }) const nowTime = Date.now(); console.log('\x1b[0m', '***********************************') console.log('\x1b[32m','HTTP Response:'); console.log('\x1b[0m', 'Total: ', nowTime - httpTimeStarted, 'ms'); console.log('\x1b[31m','Average: ', (nowTime - httpTimeStarted) / TEST_ROUNDS , 'ms'); console.log('\x1b[0m'); }); const wsTest = () => { for (i = 0; i < TEST_ROUNDS; i++) { ws.send('test'); } };
For the response app, I will be using a simple express server. In the following code, there are some other handlers that will connect to websocket and http as well.
import http from 'http'; import express from 'express'; import connectHttp from './ConnectHttpServer'; import connectWebsocket from './ConnectWebsocketServer'; import MongoClient from './MongoClient'; /** * Docker configs can be found in docker-compose.yml * {localhost}:{APP_PORT}/http endpoint will handle http connections * {localhost}:{APP_PORT}/websocket endpoint will handle http connections */ // Creates test data in Mongo MongoClient.CreateTestData(); const APP_PORT = 3001; const app = express(); const server = http.createServer(); connectHttp(app); connectWebsocket(app, server); // connect the app with express // Serve under the app port server.listen(APP_PORT, () => { console.log(`Request API listening on port ${APP_PORT}!`) });
The Nginx config and Dockerfiles
The most important part of the service is run by the dockerfile and the nginx config files. The following are the configs of the files.
Add following configs to a nginx.config
file to tell nginx proxy to balance the load between the response APIs. Both Dockerfile and Nginx file can be added to a single folder and so that Docker can find the config file easily.
/ nginx
– Dockerfile
– nginx.config
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { upstream nginx { server service1:3001; server service2:3001; server service3:3001; server service4:3001; server service5:3001; } include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; server { location /http { proxy_pass http://nginx; } location /websocket { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_pass "http://nginx"; } } #gzip on; #include /etc/nginx/conf.d/*.conf; }
Following is the docker file to use it. This is a simple docker file which takes the docker base image for nginx and
FROM nginx:latest RUN echo "copy content to the response-server docker volume" COPY nginx.conf /etc/nginx/nginx.conf RUN ls -a /etc/nginx/
Response server Dockerfile
As my response server is with node.js, I will be using the node base image to install the packages and run the response server. The commands will execute to creaet a mount point as /response-api and install the node modules.
FROM node:8.15.1-jessie ADD . /response-api WORKDIR /response-api RUN echo "copy content to the response-server docker volume" RUN ls -a RUN npm install -g yarn RUN yarn install
This file will contain instructions for Docker to run a node image containing the response server code.
Docker compose file to run the containers
Following is the docker-compose.yml file which will run the containers. Note that the mongo client files have been created for each to show the demo. And also make sure you have good understanding about the names of the container instances that are used as hosts in nginx conf and other request response services.
version: '3' services: # microservices service1: environment: - service=1 build: context: ../ dockerfile: ./docker/response-server/Dockerfile command: yarn start #volumes: #- ../:/response-server links: - mongo2 mongo1: hostname: mongo1 build: context: ./mongo dockerfile: Dockerfile service2: environment: - service=2 build: context: ../ dockerfile: ./docker/response-server/Dockerfile command: yarn start #volumes: #- ../:/response-server links: - mongo2 mongo2: hostname: mongo2 build: context: ./mongo dockerfile: Dockerfile service3: environment: - service=3 build: context: ../ dockerfile: ./docker/response-server/Dockerfile command: yarn start #volumes: #- ../:/response-server links: - mongo3 mongo3: hostname: mongo3 build: context: ./mongo dockerfile: Dockerfile # nginx dependency nginx: build: context: ./nginx dockerfile: Dockerfile hostname: nginx links: - service1 - service2 - service3 requestapi: build: context: ../ dockerfile: ./docker/request-api/Dockerfile command: npm start ports: - "127.0.0.1:3000:3000" links: - nginx
That’s it. The main parts you should understand and takedown are;
- nginx.conf file
- Dockerfile of Nginx and other images
- docker-compose.yml file
- Names of containers and host port mapping in each of the above containers.
So I hope you got the idea of how it works and will be able to make your own one. Let me know if you have any issues or need any source code.