In this example, we're going to send a request to Load balancer (HAProxy) then get the response from load balanced two different web servers. Request will never go to web servers directly. Load balancer will decide which server to be hit with the request. Example uses vagrant machines and is a layer 4 (transport layer) load balancing. Visit here for full configuration manual for you HAProxy version.

Two web servers will have only apache running and the load balancer will have HAProxy running. Load balancer GUI will be accessible from the host machine to see the health and stats about the web servers.


Note


This is a vagrant based example so if the Keepalived floating IP doesn't work as expected, it doesn't mean that it won't work as expected if you do everything manually on the servers so I suggest you to try installing packages manually on a fresh servers.


What we do here



Configuration


I'm assuming that you already have installed vagrant and Oracle VM software. I also assume that the ubuntu/trusty64 box is already added to your filesystem with vagrant box add ubuntu/trusty64 command. If you're not sure, you can confirm it with ls -l ~/.vagrant.d/boxes/ command.


Create a new project folder


mkdir lay4-hap1-web2
$ cd lay4-hap1-web2/

Create webserver.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ----------------- --\n"
echo -e "-- BEGIN ${HOSTNAME} --\n"
echo -e "-- ----------------- --\n"

# VARIABLES ####################################################################
echo -e "-- Setting global variables\n"
APACHE_CONFIG=/etc/apache2/apache2.conf
LOCALHOST=localhost

# BOX ##########################################################################
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# APACHE #######################################################################
echo -e "-- Installing Apache web server\n"
apt-get install -y apache2 > /dev/null 2>&1

echo -e "-- Adding ServerName to Apache config\n"
grep -q "ServerName ${LOCALHOST}" "${APACHE_CONFIG}" || echo "ServerName ${LOCALHOST}" >> "${APACHE_CONFIG}"

echo -e "-- Restarting Apache web server\n"
service apache2 restart

# TEST #########################################################################
echo -e "-- Creating a dummy index.html file\n"
cat > /var/www/html/index.html <<EOD
<html>
<head>
<title>${HOSTNAME}</title>
</head>
<body>
<h1>${HOSTNAME}</h1>
<p>Hi sir, I am going to serve you today!</p>
</body>
</html>
EOD

# END ##########################################################################
echo -e "-- -------------- --"
echo -e "-- END ${HOSTNAME} --"
echo -e "-- -------------- --"

Create haproxy.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------- --\n"
echo -e "-- BEGIN HAPROXY --\n"
echo -e "-- ------------- --\n"

# BOX ##########################################################################
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# HAPROXY ######################################################################
echo -e "-- Installing HAProxy\n"
apt-get install -y haproxy > /dev/null 2>&1

echo -e "-- Enabling HAProxy as a start-up deamon\n"
cat > /etc/default/haproxy <<EOF
ENABLED=1
EOF

echo -e "-- Configuring HAProxy\n"
cat > /etc/haproxy/haproxy.cfg <<EOF
global
log /dev/log local0
log localhost local1 notice
user haproxy
group haproxy
maxconn 2000
daemon

defaults
log global
mode http
option httplog
option dontlognull
retries 3
timeout connect 5000
timeout client 50000
timeout server 50000

frontend http-in
bind *:80
default_backend webservers

backend webservers
balance roundrobin
stats enable
stats auth admin:admin
stats uri /haproxy?stats
option httpchk
option forwardfor
option http-server-close
server webserver1 192.168.50.10:80 check
server webserver2 192.168.50.20:80 check
EOF

echo -e "-- Validating HAProxy configuration\n"
haproxy -f /etc/haproxy/haproxy.cfg -c

echo -e "-- Starting HAProxy\n"
service haproxy start

# END ##########################################################################
echo -e "-- ----------- --"
echo -e "-- END HAPROXY --"
echo -e "-- ----------- --"

Create Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"

# Configs for web server 1
config.vm.define :webserver1 do |webserver1_config|
webserver1_config.vm.provider :virtualbox do |vb_config|
vb_config.name = "Web Server 1 - lay4-hap1-web2"
end
webserver1_config.vm.hostname = "webserver1"
webserver1_config.vm.network "private_network", ip: "192.168.50.10"
webserver1_config.vm.provision :shell, path: "webserver.sh"
end

# Configs for web server 2
config.vm.define :webserver2 do |webserver2_config|
webserver2_config.vm.provider :virtualbox do |vb_config|
vb_config.name = "Web Server 2 - lay4-hap1-web2"
end
webserver2_config.vm.hostname = "webserver2"
webserver2_config.vm.network "private_network", ip: "192.168.50.20"
webserver2_config.vm.provision :shell, path: "webserver.sh"
end

# Configs for haproxy
config.vm.define :haproxy do |haproxy_config|
haproxy_config.vm.provider :virtualbox do |vb_config|
vb_config.name = "HAProxy - lay4-hap1-web2"
end
haproxy_config.vm.hostname = "haproxy"
haproxy_config.vm.network :forwarded_port, guest: 80, host: 8080
haproxy_config.vm.network "private_network", ip: "192.168.50.30"
haproxy_config.vm.provision :shell, path: "haproxy.sh"
end
end

Run vagrant boxes


$ vagrant up --provision
Bringing machine 'webserver1' up with 'virtualbox' provider...
Bringing machine 'webserver2' up with 'virtualbox' provider...
Bringing machine 'haproxy' up with 'virtualbox' provider...
==> webserver1: ......
==> webserver1: ......
==> webserver1: ......
==> webserver1: -- Setting global variables
==> webserver1: -- Updating packages list
==> webserver1: -- Installing Apache web server
==> webserver1: -- Adding ServerName to Apache config
==> webserver1: -- Restarting Apache web server
==> webserver1: * Restarting web server apache2
==> webserver1: ...done.
==> webserver1: -- Creating a dummy index.html file
==> webserver2: ......
==> webserver2: ......
==> webserver2: ......
==> webserver2: -- Setting global variables
==> webserver2: -- Updating packages list
==> webserver2: -- Installing Apache web server
==> webserver2: -- Adding ServerName to Apache config
==> webserver2: -- Restarting Apache web server
==> webserver2: * Restarting web server apache2
==> webserver2: ...done.
==> webserver2: -- Creating a dummy index.html file
==> haproxy: ......
==> haproxy: ......
==> haproxy: ......
==> haproxy: -- Installing HAProxy
==> haproxy: -- Enabling HAProxy as a start-up daemon
==> haproxy: -- Configuring HAProxy
==> haproxy: -- Validating HAProxy configuration
==> haproxy: Configuration file is valid
==> haproxy: -- Starting HAProxy
==> haproxy: * Starting haproxy haproxy
==> haproxy: ...done.

Access the machines


# Server 1
$ vagrant ssh webserver1
vagrant@webserver1:~$

# Server 2
$ vagrant ssh webserver2
vagrant@webserver2:~$

# HAProxy
$ vagrant ssh haproxy
vagrant@haproxy:~$

Tests


In normal life web servers must not be directly accessible but I ignore it for now for testing purposes.


Web server 1


Response to request below will alway be the same.


# Request
http://192.168.50.10/

# Response
webserver1
Hi sir, I am going to serve you today!

Web server 2


Response to request below will alway be the same.


# Request
http://192.168.50.20/

# Response
webserver2
Hi sir, I am going to serve you today!

Load balancer


Response will always change because request is evenly shared between web servers. Load balancer does it!


# Request
http://192.168.50.30/

# Response
webserver1
Hi sir, I am going to serve you today!

# Request
http://192.168.50.30/

# Response
webserver2
Hi sir, I am going to serve you today!

# Request
http://192.168.50.30/

# Response
webserver1
Hi sir, I am going to serve you today!

System outage tests


Bring only web server 1 down


Load balancer will direct all requests to web server 2 because it is available.


Bring only web server 2 down


Load balancer will direct all requests to web server 1 because it is available.


Bring both web server 1 and 2 down


There is web server for load balancer to direct requests to so error below will be generated. Default error message and template can be changed in HAProxy config file.


503 Service Unavailable
No server is available to handle this request.

HAProxy GUI


Visit http://192.168.50.30/haproxy?stats. Login with admin:admin.