Skip to main content

Adding the Fair Module to Nginx in Docker

Background

My blog used to be single-server, but now I want to deploy on multiple servers - one overseas and one domestic. Can domestic requests go to the domestic server while overseas requests go to the overseas server? This would reduce latency and load.

The answer is yes. Since the server uses nginx for load balancing, I plan to use nginx's fair module (third-party) for load balancing. Fair doesn't use the built-in round-robin algorithm - instead, it intelligently load balances based on page size and response time.

Since I also deploy nginx via Docker, I need to remove the previously deployed nginx and rebuild a custom nginx image with the fair module.

Steps

Before stopping the previous nginx, we can build the custom nginx image first. Then we just need to stop the old one and start the new one - very simple.

Build Custom Nginx Image

Create a dedicated nginx folder in the user directory

mkdir nginx_ws

Download the fair module package and nginx installation package in advance

nginxupstreamfairmaster.zip

wget http://nginx.org/download/nginx-1.18.0.tar.gz

Place both in the folder you just created.

Add Dockerfile

touch Dockerfile

Add content to Dockerfile

FROM centos:7.8.2003

MAINTAINER runnable.run

# Add local files
ADD nginx-1.18.0.tar.gz /usr/local/src
ADD nginx-upstream-fair-master.zip /usr/local/src
# Enter specified directory
WORKDIR /usr/local/src
# Install unzip tool and extract fair module
RUN yum install -y unzip && unzip nginx-upstream-fair-master.zip
# Enter specified directory
WORKDIR /usr/local/src/nginx-1.18.0

RUN yum install -y gcc gcc-c++ glibc make autoconf openssl openssl-devel \
&& yum install -y libxslt-devel -y gd gd-devel GeoIP GeoIP-devel pcre pcre-devel \
&& useradd -M -s /sbin/nologin nginx && BUILD_CONFIG="--prefix=/usr/local/nginx \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream \
--with-http_v2_module \
--with-threads \
--add-module=/usr/local/src/nginx-upstream-fair-master" && ./configure $BUILD_CONFIG \
&& mkdir -p /var/cache/nginx && make && make install


ENV PATH /usr/local/nginx/sbin:$PATH

EXPOSE 80
EXPOSE 443

ENTRYPOINT ["nginx"]

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

Command explanations:

FROM: Specifies base image, must be the first command

MAINTAINER: Maintainer information

ADD: Adds local files to container, tar files are auto-extracted (network compressed resources are not), can access network resources like wget

WORKDIR: Working directory, similar to cd command

RUN: Commands executed when building the image

ENV: Sets environment variables

EXPOSE: Specifies ports for external interaction

ENTRYPOINT: Configures container to be executable. Combined with CMD, can omit "application" and use only parameters.

CMD: Called after container is built, i.e., when container starts.

Build

docker build -t centos7.8_nginx1.18:v1 .

Success Message

When you see this, it's successful:

Successfully built f91ed12a53e7

Start a Simple Nginx First to Copy Config Files

docker run --name nginx -p 8081:80 -d --rm nginx

Copy config files from container to specified directory:

docker cp nginx:/usr/local/nginx/conf/nginx.conf /root/nginx_ws/dockerData/nginx/conf/nginx.conf

Delete the container you just created. Since we added --rm when creating it, stopping it will auto-delete:

docker stop nginx

Modify Config File

Reference configuration:

#user  nobody;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include 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 logs/access.log main;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;
upstream blogServer {
fair;
#server ip1:8080;
server ip2:8080;
}

server {
listen 443;
server_name runnable.run;
ssl on;

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;

ssl_certificate /ssl/6349085_www.runnable.run.pem;
ssl_certificate_key /ssl/6349085_www.runnable.run.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;


location / {
proxy_pass http://blogServer;
error_page 404 https://www.runnable.run;
}

error_page 404 https://www.runnable.run;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

server{
listen 80;
server_name runnable.run;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
rewrite ^(.*) https://$host$1 permanent;
}



server {
listen 443;
server_name www.runnable.run;
ssl on;

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;

ssl_certificate /ssl/6349085_www.runnable.run.pem;
ssl_certificate_key /ssl/6349085_www.runnable.run.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;


location / {
proxy_pass http://blogServer;
error_page 404 https://www.runnable.run;
}

error_page 404 https://www.runnable.run;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

server{
listen 80;
server_name www.runnable.run;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
rewrite ^(.*) https://$host$1 permanent;
}

}

Start Using Custom Image

docker run -d -p 80:80 -p 443:443 --name nginx \
-v /root/nginx_ws/dockerData/nginx/conf/nginx.conf:/usr/local/nginx/conf/nginx.conf \
-v /root/nginx_ws/dockerData/nginx/ssl:/ssl/ \
-v /root/nginx_ws/dockerData/nginx/www:/usr/share/nginx/html \
centos7.8_nginx1.18:v1

References

This article referenced:

Installing solo blog from scratch

Compiling and installing nginx in Docker

Nginx Load Balancing - fair