Haproxy + Keepalived


HAProxy + Keepalived

Install package

sudo apt update
sudo apt install haproxy keepalived -y
touch /etc/keepalived/check_haproxy.sh && chmod +x /etc/keepalived/check_haproxy.sh

Configure keepalived

/etc/keepalived/check_haproxy.sh

#!/bin/bash
pidof haproxy > /dev/null 2>&1
if [ $? -ne 0 ]; then
    exit 1
fi
exit 0

/etc/keepalived/keepalived.conf

master

vrrp_script chk_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 2
    weight -10
    fall 1
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface enp1s0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 12345
    }
    virtual_ipaddress {
        192.168.122.10
    }
    track_script {
        chk_haproxy 
    }
}

backup

vrrp_script chk_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 1
    weight -10
    fall 1
    rise 1
}

vrrp_instance VI_1 {
    state BACKUP
    interface enp1s0
    virtual_router_id 51
    priority 95
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 12345
    }
    virtual_ipaddress {
        192.168.122.10
    }
    track_script {
         chk_haproxy
    }
}

Configure haproxy

/etc/haproxy/haproxy.cfg
For all servers

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon
        maxconn 2000

    # Default SSL material locations
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

    # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
    log global
    mode    http
    option  httplog
    option  dontlognull
        #timeout connect 5000
        #timeout client  50000
        #timeout server  50000
        timeout connect 5000ms
        timeout client  50000ms
        timeout server  50000ms
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

frontend http_front
    bind *:80
   # Stick table
    stick-table type ip size 1m expire 10s store http_req_rate(10s)

    # ip associate with stick table
    tcp-request connection track-sc1 src

    # Rate limit: max 100 request per 10 seconds
    http-request deny if { sc_http_req_rate(0) gt 100 }
    default_backend http_back

backend http_back
    balance roundrobin
    server node01 192.168.122.226:80 check
    server node02 192.168.122.185:80 check

# enable haproxy stats  http://<server>:9000/stats
listen stats
    bind *:9000
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats realm Haproxy\ Statistics
    stats auth admin:adminpassword123
    stats admin if TRUE

Check config

haproxy -c -f -V /etc/haproxy/haproxy.cfg

Restart services

systemctl restart haproxy
systemctl restart keepalived

Check correct work

Stop some service

systemctl stop haproxy
# or 
systemctl stop keepalive

ip 192.168.122.10 must be assigned to backup server

Script for manage haproxy

#!/bin/bash

SOCKET="/run/haproxy/admin.sock"
COMMAND="socat $SOCKET stdio"

function check_args {
    if [ "x$1" == "x" ]
    then
        echo "unknown backend for drain $0 drain <backend> <server>"
        exit 1
    elif [ "x$2" == "x" ]
    then
        echo "unknown server for drain $0 drain $1 <server>"
        exit 1
    fi
}

case $1 in
    help|--help|-h)
        echo "help" | $COMMAND
    ;;
    status)
          echo "Server states"
          echo "show servers state" | $COMMAND
    ;;
    drain)
          check_args $2 $3
        echo "set server $2/$3 state drain" | $COMMAND

        while true; do
            SESS=$(echo "show stat" | $COMMAND | grep "$2,$3" | cut -d',' -f5)
            echo "Active sessions: $SESS"
            [ "$SESS" -eq 0 ] && break
            sleep 2
        done
          STATUS=$(echo "show stat" | $COMMAND | grep "$2,$3" | cut -d',' -f18)
          echo "$2 $2 is $STATUS"
    ;;
    ready)
          check_args $2 $3
        echo "set server $2/$3 state ready" | $COMMAND
          sleep 1
        STATUS=$(echo "show stat" | $COMMAND | grep "$2,$3" | cut -d',' -f18)
        echo "$2 $2 is $STATUS"
    ;;
    maint)
          check_args $2 $3
        echo "set server $2/$3 state maint" | $COMMAND
          sleep 1
        STATUS=$(echo "show stat" | $COMMAND | grep "$2,$3" | cut -d',' -f18)
        echo "$2 $2 is $STATUS"
    ;;
    *)
        echo "$*" | socat $SOCKET stdio
    ;;
esac

or using python lib for manage haproxy