04- Patroni Cluster on Ubuntu24.04 - HAProxy & KeepAlived Installation (SSL)

We will install and configure HAProxy & Keepalived

HAProxy does not need SSL certificates – traffic is pure TCP passthrough.

TLS terminates on PostgreSQL, not on HAProxy.

ETCD uses full TLS, so all etcdctl commands must use--cacert, --cert, --key

 

 

The following installation will be carried out on HAProxy nodes (192.168.204.11 haproxy01 & 192.168.204.12 haproxy02)

Install HAProxy on both servers

sudo su
apt update
apt -y install haproxy
nano /etc/haproxy/haproxy.cfg

 

Paste the following config at the end of haproxy.cfg (the same on both nodes):

frontend postgres_frontend
    bind *:5432  #HAProxy listens on port 5432.
    timeout client 30s
    mode tcp
    option tcplog
    default_backend postgres_backend

backend postgres_backend
    mode tcp
    option httpchk GET /primary  # patroni provides an endpoint to check node roles
    http-check expect status 200  # expect 200 for the primary node
   #It checks Patroni REST API on port 8008 (/primary) to identify the leader. Only the leader node receives traffic.
    server postgres01 192.168.204.16:5432 check port 8008
    server postgres02 192.168.204.17:5432 check port 8008
    server postgres03 192.168.204.18:5432 check port 8008

HAProxy listens to the request on port 5432 and checks the primary backend node and then forwards the request to its primary backend node. It checks the backend nodes' health by using port 8008.

Check HAProxy config whether it is valid and reload the service 

haproxy -c -f /etc/haproxy/haproxy.cfg   # validate first
systemctl reload haproxy
systemctl enable haproxy

 

Install KeepAlived on both servers:

apt update
apt install keepalived -y
nano /etc/keepalived/keepalived.conf

 

Node 1 (haproxy01 – 192.168.204.11)

global_defs {
    enable_script_security
    script_user keepalived_script
}

vrrp_script check_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 2
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass 34Istanb
    }

    virtual_ipaddress {
        192.168.204.10
    }

    track_script {
        check_haproxy
    }
}

 

Node 2 (haproxy02 – 192.168.204.12)

global_defs {
    enable_script_security
    script_user keepalived_script
}

vrrp_script check_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 2
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33 # update with your nic
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 34Istanb # change
    }
    virtual_ipaddress {
        192.168.204.10
    }
    track_script {
        check_haproxy
    }
}

 

 

Create a check script on each node. This script will check if HAProxy is up or down. If down keepalived consider its node as down and will not get requests.

#!/bin/bash
PORT=5432

# Check if HAProxy process is running
if ! pidof haproxy > /dev/null; then
    echo "HAProxy process is not running"
    exit 1
fi

# Check if HAProxy is listening on 5432
if ! ss -ltn | grep -q ":${PORT}"; then
    echo "HAProxy is not listening on port ${PORT}"
    exit 2
fi

exit 0

 

We need to add a user to execute these scripts. Set ownerships and permissions. Restart keepalived.

useradd -r -s /bin/false keepalived_script
chmod +x /etc/keepalived/check_haproxy.sh
chown keepalived_script:keepalived_script /etc/keepalived/check_haproxy.sh
chmod 700 /etc/keepalived/check_haproxy.sh
systemctl restart keepalived
journalctl -u keepalived -f

 

Journalctl command above will show the nodes' keepalived status (MASTER or BACKUP). Also we should be able to ping 192.168.204.10 (Virtual IP) at this moment. We can stop the haproxy service on Node1 and check if the service moves to node2 without problems.

systemctl stop haproxy

Node2 successfully takes the MASTER role

 

To add our Postgres cluster to PGAdmin, Right click Servers > Register > Server.

We add Virtual IP as the host address. The password is the one that we defined in /etc/patroni/config.yml file.

 

Make sure SSL mode is selected as require