HAproxy with Keepalived not behaving as expected

I have two servers in a public subnet with both public and private IPs. I want to configure HAproxy with keepalived and make server1 act as the master and server2 as backup.

If server1 goes down, I want server2 to takeover.

Let’s classify it as follows:

server1: private_ip1 & public_ip1
server2: private_ip2 & public_ip2

virtual_public_ip

I have a flask app on both servers with the following code:

app.py

from flask import *
import socket

app = Flask(__name__)

@app.route("/")
def index():
    hostname = socket.gethostname()
    ip = socket.gethostbyname(hostname)
    return f"<h1>Hello from computer {hostname} with ip {ip}</h1>"

if __name__ == "__main__":
    app.run(debug=False)

The code will basically display the hostname of the server and its IP address.

Below is the config file for HAproxy and Keepalived for each servers.

server1 HAproxy

global
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

    # utilize system-wide crypto-policies
    ssl-default-bind-ciphers PROFILE=SYSTEM
    ssl-default-server-ciphers PROFILE=SYSTEM

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

frontend main
    bind *:80
    default_backend app_servers

backend app_servers
    balance roundrobin
    server haproxy-01 public_ip1:5000 check
    server haproxy-02 public_ip2:5000 backup

server1 keepalived

global_defs {
	enable_script_security
	script_user th3pl4gu3
}

vrrp_script chk_haproxy {
    script "pidof haproxy"
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 101
    virtual_ipaddress {
        virtual_public_ip
    }
    track_script {
        chk_haproxy
    }
}

server2 HAproxy

global
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

    # utilize system-wide crypto-policies
    ssl-default-bind-ciphers PROFILE=SYSTEM
    ssl-default-server-ciphers PROFILE=SYSTEM

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000


frontend main
bind *:80
    default_backend app_servers

backend app_servers
    balance roundrobin
    server haproxy-01 public_ip1:5000 backup
    server haproxy-02 public_ip2:5000 check

server2 keepalived

global_defs {
    enable_script_security
    script_user th3pl4gu3
}


vrrp_script chk_haproxy {
    script "pidof haproxy"
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 100
    virtual_ipaddress {
        virtual_public_ip
    }
    track_script {
        chk_haproxy
    }
}

If i go on my browser and enter http://public_ip1:5000 in the URL, i can see the hostname for server1 and the virtual_public_ip.

If i go on my browser and enter http://public_ip2:5000 in the URL, i can see the hostname for server2 and still the virtual_public_ip.

But if i try to access the app. using the virtual IP itself, it doesn’t work http://virtual_public_ip.

What am i doing wrong ?

this works for me on an Exchange cluster for keepalived

Server1
vrrp_script check_haproxy {
  script "killall -0 haproxy"
  interval 2
  weight 2
}

vrrp_instance VI_1 {
  interface ens192
  state MASTER
  virtual_router_id 10
  priority 101
  virtual_ipaddress {
    VIP ipv4
  }
  virtual_ipaddress_excluded {

   VIP ipv6

  }
  track_script {
    check_haproxy
  }
  smtp_alert
}

Server2
vrrp_script check_haproxy {
  script "killall -0 haproxy"
  interval 2
  weight 2
}

vrrp_instance VI_1 {
  interface ens192
  state MASTER
  virtual_router_id 10
  priority 100
  virtual_ipaddress {
    VIP ipv4
  }
  virtual_ipaddress_excluded {

   VIP ipv6

  }
  track_script {
    check_haproxy
  }
  smtp_alert
}