Published on

How to Create a Simple Nginx Web Server?

Authors

Target Audience

I've aimed this article at people who want to learn about Nginx, Docker, Containers, Node.js.

Learning Objectives

After completing this article, you will know how to do the following:

  • Create a simple node.js application
  • Create and setup nginx configuration
  • Create docker images for node.js and nginx
  • Setup docker-compose.yml configuration
  • Use docker compose to start entire application

What is Nginx ?

Nginx is a web server that can also be used as a reverse proxyload balancer, mail proxy and HTTP cache.

In this article, we will use nginx as a load balancer.

Load balancing 

A reverse proxy server can act as a “traffic cop,” sitting in front of your backend servers and distributing client requests across a group of servers in a manner that maximizes speed and capacity utilization while ensuring no one server is overloaded, which can degrade performance. If a server goes down, the load balancer redirects traffic to the remaining online servers.

You can see an example nginx configuration for load balancing below:

#ngnix.conf file
http {
    upstream myapp1 {
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

In this example, we are listening port 80 to handle requests. For each request, nginx will route the client to srv1 to srv3. When the load balancing method is not specifically configured, it defaults to round-robin.

You can check the other load balancing methods here.

Docker

Docker is a platform for deploying applications. It works by isolating the application into an image. The image will be run by a container, where it contains the operating system and supporting software. (source)

What is a docker container ?

A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings.

When you containerize an application using docker, you don't have to worry about different environments. So when your application runs on your local machine, once you containerize it using docker it will run on a server too. Because you specify which libraries used, so your local environment and a server environment use the same version of libraries and operating system.

An example for Dockerfile for a node application

FROM node:12
WORKDIR /home/node
COPY . /home/node/
RUN npm install
CMD npm run app

Creating a simple http server in node.js

In this article, we will create a simple demo. We will create a HTTP web server using HTTP library.

Here is the implementation for HTTP web server in node.js:

const http = require('http')

const defaultPort = 999
const portNumber = process.argv[2] || defaultPort

const httpServer = http.createServer((req, res) => {
  res.statusCode = 200
  res.end('this is port: ' + portNumber)
})

httpServer.listen(portNumber, '0.0.0.0', () => {
  console.log('listening on port ', portNumber)

  console.log(httpServer.address())
})

After this implementation, we will start two web servers with different ports. You can see the example command line below:

node index.js 2222 #this will create a web server on port 2222
node index.js 3333 #this will create a web server on port 3333

After starting two web servers, we can test it using web browser or using curl. You can use curl to make a request to a web server. We can see the get request in the example below:

curl -v localhost:2222 #make a get request on localhost:2222

Response:

*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 2222 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 2222 (#0)
> GET / HTTP/1.1
> Host: localhost:2222
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 09 Jul 2022 15:15:47 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 18
<
* Connection #0 to host localhost left intact
this is port: 2222* Closing connection 0

Nginx configuration

In this demo, we will use nginx as a load balancer to route between two web servers.

Here is the implementation:

# nginx.conf file
http
{
  upstream all
  {
    server 192.168.1.36:3333;
    server 192.168.1.36:2222;
  }

  server
  {
    listen 8080;
    location /
    {
      proxy_pass http://all;
    }
  }
}

events
{
}

Dockerfile:

FROM nginx

COPY nginx.conf /etc/nginx/nginx.conf

We have to build this image to use it in docker compose. You can build a docker image using build command. Here is an example

docker build -t nginxapp .

After creating nginx image we can configure the dockore-compose file.

Here is the implementation for docker-compose.yml

version: '3'

services:
  nginxapp1:
    image: nginxapp
    ports:
      - '8080:8080'

You can start the application using docker compose upand kill and remove the containers using docker compose down.

After starting the nginx application using docker compose up. You will see the example output in the terminal:

node-nginx-docker-tut-lb-1  | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
node-nginx-docker-tut-lb-1  | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
node-nginx-docker-tut-lb-1  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
node-nginx-docker-tut-lb-1  | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
node-nginx-docker-tut-lb-1  | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
node-nginx-docker-tut-lb-1  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
node-nginx-docker-tut-lb-1  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
node-nginx-docker-tut-lb-1  | /docker-entrypoint.sh: Configuration complete; ready for start up

This means we started the nginx server, so now we can test it using curl.

curl localhost:8080
this is port: 3333%
curl localhost:8080
this is port: 2222

You can see in this example output when we make a request to the localhost:8080 each request goes to the different HTTP web server using round-robin algorithm.