The first, install keepalived to manage Virtual IP. My concept is use 2 Virtual IPs bind on 2 Nodes. Node 1 is MASTER on Virtual IP 1 and BACKUP on Virtual IP 2, Node 2 is MASTER on Virtual IP 2 and BACKUP on Virtual IP 1. DNS Round-robin to 2 Public IPs, every Public IP map to one Virtual IP.
I think this concept is working with both NGINX or HAProxy.
Insall KeepAlived
On both Nodes:
sudo dnf install -y keepalived
id -u keepalived &> /dev/null || useradd -s /usr/sbin/nologin -r keepalived_script
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv4.ip_nonlocal_bind=1" >> /etc/sysctl.conf
firewall-cmd --add-rich-rule='rule protocol value="ah" accept' --permanent
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent && sudo firewall-cmd --reload
firewall-cmd --add-port=112/udp --permanent && firewall-cmd --reload
firewall-cmd --add-port=112/tcp --permanent && firewall-cmd --reload
Edit /etc/keepalived/keepalived.conf:
On node 1:
global_defs {
router_id NODE_01
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_script chk_haproxy { # Requires keepalived-1.1.13
script "killall -0 haproxy" # cheaper than pidof
interval 2 # check every 2 seconds
weight 2 # add 2 points of prio if OK
}
vrrp_instance VI_1 {
state MASTER
interface ens160 # change as your network interface
virtual_router_id 51
priority 101 # greater is more priority (99-100-101...)
advert_int 1
authentication {
auth_type PASS
auth_pass hacluster
}
virtual_ipaddress {
10.0.0.1 # Virtual IP 1
}
track_script {
chk_haproxy
}
}
vrrp_instance VI_2 {
state BACKUP
interface ens160
virtual_router_id 52 # other ID
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass hacluster
}
virtual_ipaddress {
10.0.0.2 # Virtual IP 2
}
track_script {
chk_haproxy
}
}
On node 2:
global_defs {
router_id NODE_02
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_script chk_haproxy { # Requires keepalived-1.1.13
script "killall -0 haproxy" # cheaper than pidof
interval 2 # check every 2 seconds
weight 2 # add 2 points of prio if OK
}
vrrp_instance VI_1 {
state BACKUP
interface ens160
virtual_router_id 51
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass hacluster
}
virtual_ipaddress {
10.0.0.1 # Virtual IP 1
}
track_script {
chk_haproxy
}
}
vrrp_instance VI_2 {
state MASTER
interface ens160
virtual_router_id 52 # other ID
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass hacluster
}
virtual_ipaddress {
10.0.0.2 # Virtual IP 2
}
track_script {
chk_haproxy
}
}
systemctl enable --now keepalived
systemctl reload keepalived
Install somethings
sudo dnf -y install epel-release
sudo dnf -y groupinstall 'Development Tools'
sudo dnf install -y wget git unzip perl perl-devel perl-ExtUtils-Embed libxslt libxslt-devel libxml2 libxml2-devel gd gd-devel pcre-devel GeoIP GeoIP-devel
Install HAProxy community edition
Install from repo or source.
dnf -y install haproxy
setsebool -P haproxy_connect_any=1
sudo systemctl daemon-reload
sudo chkconfig haproxy on
systemctl enable --now haproxy
id -u haproxy &> /dev/null || useradd -s /usr/sbin/nologin -r haproxy
Open listen ports on firewall, or disable (for test):
sudo systemctl stop firewalld
Now, on both Nodes, update your haproxy with the same config. Your config files should be ascii, and have one new empty line.
In /etc/haproxy/haproxy.cfg:
# haproxy -c -V -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
global
log /dev/log local0 warning
log /dev/log local1 notice
chroot /var/lib/haproxy
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
stats enable
stats refresh 10s
stats uri /ha-stats
In /etc/haproxy/conf.d, create a router file for listening:
/etc/haproxy/conf.d/router.cfg:
frontend listen_80
mode http
bind :80
http-request add-header X-Forwarded-Proto https
redirect scheme https if !{ ssl_fc }
frontend frontend_router
bind *:443 ssl crt /etc/haproxy/cert.pem
option http-server-close
option forwardfor
http-request add-header X-Forwarded-Proto https
http-request add-header X-Forwarded-Port 443
http-response add-header Strict-Transport-Security max-age=15768000
# WebSocket Check
acl is_websocket req.hdr(Upgrade) -i WebSocket && req.path_beg /ws
# Router
## check_dns for app1
acl is_match_domain hdr_dom(host) -i app1.domain.com app1-test.domain.com
use_backend app1_backend_socket if is_match_domain is_websocket
use_backend app1_backend if is_match_domain
## check_dns for app2
acl is_match_domain hdr_dom(host) -i app2.domain.com app2-test.domain.com
use_backend app2_backend_socket if is_match_domain is_websocket
use_backend app2_backend if is_match_domain
/etc/haproxy/conf.d/app1.cfg:
# if app1 listen on custom Port
frontend app1_frontend_other_port
mode http
bind :3003
bind *:3003 ssl crt /etc/haproxy/cert.pem
acl is3003 dst_port 3003
acl is_app1_main hdr_dom(host) -i app1.domain.com
acl is_app1_test hdr_dom(host) -i app1-test.domain.com
http-request redirect prefix https://app1.domain.com code 301 if is3003 is_app1_main
http-request redirect prefix https://app1-test.domain.com code 301 if is3003 is_app1_test
backend app1_backend
mode http
balance roundrobin
server node1 10.5.10.202:3000 ssl verify none #if backend has SSL
server node2 10.5.10.202:3001 ssl verify none
backend app1_backend_socket
mode http
balance roundrobin
server node1 10.5.10.202:3200 ssl verify none #if backend has SSL
server node2 10.5.10.202:3201 ssl verify none
server node3 10.5.10.202:3202 ssl verify none
server node4 10.5.10.202:3203 ssl verify none
/etc/haproxy/conf.d/app2.cfg:
backend app2_backend
mode http
balance roundrobin
server node1 10.5.10.203:3000 #if backend has no SSL
server node2 10.5.10.203:3001
backend app2_backend_socket
mode http
balance roundrobin
server node1 10.5.10.203:3200
server node2 10.5.10.203:3201
Recheck config and apply
# haproxy -c -V -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
Reload your keepalived again.
Test
Config your DNS (example.domain.com) to 2 public IP.
In both nodes, run terminal server test:
python3 -m http.server -b 0.0.0.0 3009
# install python3 if does not install
On browser, connect http://example.domain.com/?ha-test then refresh. Show your terminal.