Notes on Nginx
NGINX is a web server
- A web server is a piece of software that responds to HTTP requests made by clients (usually web browsers).
- The browser makes a request, and the web server responds with the static content (usually HTML) corresponding to that request.
NGINX is a reverse proxy
- A reverse proxy is a server that sits in front of a group of web servers.
-
When a browser makes an HTTP request, the request first goes to the reverse proxy, which then sends the request to the appropriate web server.
- Some benefits of a reverse proxy:
Security
: with a reverse proxy, the web server never reveals its IP address to the client, which makes the server more secure.SSL Encryption (more security)
: encrypting and decrypting SSL communications is expensive, and would make web servers slow. The reverse proxy can be configured to decrypt incoming requests from the client and encrypt outgoing responses from the server.Load Balancing
: if a website is very popular, it’s unlikely that all the traffic is handled by a single web server. Usually, the website will be distributed across many web servers, with a reverse proxy in front of them. (see below for more on load balancing)
NGINX is a HTTP load balancer
- A load balancer is responsible for routing client HTTP requests to web servers in an efficient manner. This prevents any individual web server from being overworked.
- A load balancer can also be configured so that if a web server goes down, the reverse proxy will no longer forward requests to that web server
Nginx conf
- The location
/
means that when we visit the root url (localhost:80/
), we use this configuration
$ cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1
...
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
....
access_log /var/log/nginx/access.log main;
...
include /etc/nginx/conf.d/*.conf; # <= Includes more conf files
}
$ cat /etc/nginx/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name localhost; # <= What name and port the server is listening on
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
Nginx as reverse proxy
- Assume we have 2 nodejs web servers running on
localhost:5000
andlocahost:8000
, and we want to map them tolocalhost:80/server1
andlocalhost:80/server2
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name nodeserver;
location /server1 {
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:5000;
}
location /server2 {
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8000;
}
}
Example: Nodejs server with a Nginx reverse proxy
- Code layout
[rritesh-a02:rritesh:~/Docker/nginx_revproxy_example] $ ls -lR
total 8
-rw-r--r-- 1 rritesh staff 245 Jan 30 15:32 docker-compose.yml # <= YAML file used by docker compose to start all the services
drwxr-xr-x 4 rritesh staff 128 Jan 30 15:36 nginx # <= Nginx project folder
drwxr-xr-x 4 rritesh staff 128 Jan 30 11:03 nodejs # <= nodejs project folder
./nginx:
total 16
-rw-r--r-- 1 rritesh staff 60 Jan 30 15:31 Dockerfile
-rw-r--r-- 1 rritesh staff 911 Jan 30 15:36 default.conf
./nodejs:
total 16
-rw-r--r-- 1 rritesh staff 97 Jan 30 18:15 Dockerfile
-rw-r--r-- 1 rritesh staff 139 Jan 30 18:15 package.json
-rw-r--r-- 1 rritesh staff 1037 Jan 30 18:31 server.js
- Docker compose file for both
nodejs
andnginx
. Created containers for both the type using theDockerfile
in the two directories.
docker-compose.yml:
version: "3.8"
services:
nodeserver:
build:
context: ./nodejs # <= Which folder has nodejs Dockerfile (Build context)
ports:
- "1234:3000" # <= Port mappings from HOST to Container
nginx:
restart: always
build:
context: ./nginx # <= Which folder has nginx Dockerfile
ports:
- "80:80" # <= Starting with - means an ARRAY in YAML.
redis-server:
image: 'redis'
Nodejs
nodejs
server file and Docker file
package.json
{
"dependencies": {
"express": "*",
"redis": "2.8.0"
},
"scripts": {
"start": "node server.js"
}
}
//server.js:
const express = require('express');
const redis = require('redis');
const app = express();
const client = redis.createClient({
host: 'redis-server',
port: 6379,
});
// Two get requests endpoints, have 2 counters
client.set('rootvisits', 0);
client.set('server1visits', 0);
app.get('/', (req, res) => {
client.get('rootvisits', (err, visits) => {
console.log("Root visit number: ", visits)
res.send('Number of root visits ' + visits);
client.set('rootvisits', parseInt(visits) + 1);
});
});
app.get('/server1', (req, res) => {
client.get('server1visits', (err, visits) => {
console.log("Server1 visit number: ", visits)
res.send('Number of server1 visits ' + visits);
client.set('server1visits', parseInt(visits) + 1);
});
});
app.listen(3000, () => {
console.log('listening on port 3000');
});
FROM node:alpine
WORKDIR '/app'
COPY package.json .
RUN npm install
COPY . .
CMD ["npm","start"]
Nginx
default.conf:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name localhost;
location / {
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://nodeserver:3000; <= The destination has the name of the nodejs service in the YAML file.
<= Note, the port here is 3000, NOT 1234. 1234 of HOST is mapped to 3000 of nodeserver
<= Nginx should redirect directly to nodeserver:3000, nginx and nodeserver are essentially on the SAME network, and can communicate to each other using service name.
}
}
FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf
- The networking between the service (check the IP Address)
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d67b3b2600db nginx_revproxy_example_nodeserver "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 0.0.0.0:1234->3000/tcp nginx_revproxy_example_nodeserver_1
f5d394540fc2 nginx_revproxy_example_nginx "/docker-entrypoint.…" 11 minutes ago Up 11 minutes 0.0.0.0:8080->80/tcp nginx_revproxy_example_nginx_1
39afbcd85082 redis "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 6379/tcp nginx_revproxy_example_redis-server_1
[rritesh-a02:rritesh:~/Docker/nginx_revproxy_example] $ docker exec -it d67b3b2600db ip addr show dev eth0
51: eth0@if52: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
[rritesh-a02:rritesh:~/Docker/nginx_revproxy_example] $ docker exec -it f5d394540fc2 hostname -I
172.18.0.4
[rritesh-a02:rritesh:~/Docker/nginx_revproxy_example] $ docker exec -it 39afbcd85082 hostname -I
172.18.0.2
Written on July 24, 2021