I have recently created my own nginx cluster. I use nginx as a reverse proxy and needed a high-availability solution. There’s already support for this in Nginx Plus , but I’m compiling my own version of the Open Source version of nginx, so I looked at their documentation as inspiration and created my own scripts. I have two servers a Primary (Node A - Active) and a Secondary (Node B - Passive), my Primary node is the where I edit/update my nginx configration and then it synchronizes the changes to my secondary node. Here’s what you need to get started
Prerequisites
- 3 IP addresses - Each server needs an IP address and the last is used as the floating IP address which is the one users should access
- 2 RHEL/CentOS Servers
- Nginx - Webserver
- Keepalived - VRRP software
- Rsync - Synchronization software
Install software
Install the following packages on both servers
yum install -y nginx keepalived rsync
Firewall Configuration
This should be applied to both servers. If you are serving websites on non-standard ports, then remember to open them as well
firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=https --permanent
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload
Tweak the Linux Kernel
The Nginx and Keepalived process needs the ability to bind to a non-local IP address. This is done by creating a file in /etc/sysctl.d/
echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/90-keepalived.conf
NOTE. The above needs to be done on both servers
Node A - Primary Nginx Server - 192.168.1.11
- /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
enable_script_security # Enable Script Security
script_user YOUR-USERNAME # Run script as this user. For security reasons, don't use root
}
vrrp_script track_nginx { # Tracking script to determine if the service is healthy
script "/usr/sbin/pidof nginx" # Checks if nginx is running
interval 2 # Checks every 2 seconds
timeout 1
}
vrrp_instance VI_1 {
state MASTER # MASTER/BACKUP
interface ens192 # Name of interface to be used for VRRP
virtual_router_id 51 # Router ID needs to match on all nodes
priority 200 # A higher number has higher priority
advert_int 1
authentication {
auth_type PASS
auth_pass YOUR-PASSWORD # Maximum 8 characters
}
unicast_peer {
192.168.1.12 # IP address of our secondary node
}
virtual_ipaddress {
192.168.1.10/24 dev ens192 # Floating/Virtual IP and the interface name to bind it to
}
track_script {
track_nginx # Tracking script defined above
}
}
- Start Keepalived and Nginx
systemctl enable --now keepalived.service
systemctl enable --now nginx.service
Sync Nginx files
Now we need to detect changes in /etc/nginx and synchronize them to our secondary server. This is a two step process, first we need a script to do the actual synchronization and then we need to run it when a change has been made.
- Create a script called NginxSync.sh
#!/bin/bash
NodeB="192.168.1.12" # IP address of the Node B/Secondary nginx server
## Check if nginx configuration is valid before synchronization
if out=$(nginx -t 2>&1); then
## Sync Files
rsync -a --delete /etc/nginx/ $NodeB:/etc/nginx
## Restart Nginx
ssh $NodeB "systemctl restart nginx.service"
echo "Success"
else
echo "Failure, because $out"
fi
1a. Make the script executable
```bash
chmod +x NginxSync.sh
2. We need to create a ssh key and copy the public key to Node B
```bash
ssh-keygen -t rsa -b 4096 -C "Nginx Primary" -f ~/.ssh/id_NodeA_rsa -N ""
## Copy public key to Node B
ssh-copy-id -i ~./ssh/id_NodeA_rsa.pub root@192.168.1.12
Now we need to create the monitor script. It’s made of two files placed in /etc/systemd/system/
- nginxFileChange.service
[Unit] Description = Starts the synchronization job from Node A to Node B Documentation = man:systemd.service [Service] Type=oneshot ExecStart=/YOUR-PATH-TO/NginxSync.sh # Remember to change this line to your needs
2. **nginxFileChange.path**
```bash
[Unit]
Description = Triggers the nginxFileChange.service which synchronizes changes
Documentation = man:systemd.path
[Path]
PathModified=/etc/nginx/ # Path to the nginx config folder
Unit=nginxFileChange.service
[Install]
WantedBy=multi-user.target # Requires at least runlevel 3 otherwise our NginxSync.sh script wont work
3. Start the nginxFileChange.path service
```bash
systemctl daemon-reload
systemctl enable --now nginxFileChange.path # Enables the file monitor check
systemctl status nginxFileChange.service # Shows status of the sync service
### Node B - Secondary Nginx Server - 192.168.1.12
1. /etc/keepalived/keepalived.conf
```bash
! Configuration File for keepalived
global_defs {
enable_script_security
script_user root
}
vrrp_script track_nginx {
script "/usr/bin/killall -0 nginx"
interval 2
timeout 1
}
vrrp_instance VI_1 {
state BACKUP
interface ens192
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass YOUR-PASSWORD
}
unicast_peer {
192.168.1.11
}
virtual_ipaddress {
192.168.1.10/24 dev ens192
}
track_script {
track_nginx
}
}
- Start Keepalived and Nginx
systemctl enable --now keepalived.service
systemctl enable --now nginx.service
Troubleshooting
Here is a few useful commands to see if it’s working
ip address # Shows network interfaces and IP
journalctl -r -u keepalived # Shows nginx systemd log
journalctl -r -u nginx # Shows keepalived systemd log
systemctl enable service-name.service # Auto starts the service at boot
systemctl enable --now service-name.service # Equal to systemctl enable + systemctl start
systemctl status nginx.service # Shows service status
systemctl status keepalived.service
systenctl status nginxFileChange.path
systemctl start nginx.service # Start Service
systemctl start keepalived.service
systemctl start nginxFileChange.path
systemctl stop nginx.service # Stops Service
systemctl stop keepalived.service
systemctl stop nginxFileChange.path