How to create a simple Nginx web server ?
Published: 2022-07-09 15:44:11

# 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](https://en.wikipedia.org/wiki/Web_server "Web server") that can also be used as a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy "Reverse proxy"), [load balancer](https://en.wikipedia.org/wiki/Load_balancer "Load balancer"), mail proxy and [HTTP cache](https://en.wikipedia.org/wiki/HTTP_cache "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](https://www.nginx.com/solutions/adc/) redirects traffic to the remaining online servers.


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

```nginx
#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](http://nginx.org/en/docs/http/load_balancing.html#:~:text=Default%20load%20balancing%20configuration,-The%20simplest%20configuration&text=All%20requests%20are%20proxied%20to,SCGI%2C%20memcached%2C%20and%20gRPC.).


## 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](https://towardsdatascience.com/containerize-your-application-with-docker-b0608557441f))


## What is a docker container ?
A [Docker container image](https://www.docker.com/resources/what-container/) 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

```dockerfile
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:

```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:

```bash
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: 

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

Response:

```bash
*   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
# 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:

```docker
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

```bash
docker build -t nginxapp .
```

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

Here is the implementation for `docker-compose.yml`

```yaml
version: "3"

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

```

You can start the application using `docker compose up`and 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:

```bash
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.

```bash
❯ 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.