Load Balancer 1: lb1.example.com, IP address: 192.168.0.100
Load Balancer 2: lb2.example.com, IP address: 192.168.0.101
SiteVision Server 1: http1.example.com, IP address: 192.168.0.102
SiteVision Server 2: http2.example.com, IP address: 192.168.0.103
We also need a virtual IP address that floats between lb1 and lb2: 192.168.0.99
Here's a little diagram that shows our setup:
shared IP=192.168.0.99
192.168.0.100 192.168.0.101 192.168.0.102 192.168.0.103
-------+------------+--------------+-----------+----------
| | | |
+--+--+ +--+--+ +----+----+ +----+----+
| lb1 | | lb2 | | http1 | | http2 |
+-----+ +-----+ +---------+ +---------+
haproxy haproxy 2 SiteVision servers
heartbeat heartbeat

We back up the original /etc/haproxy.cfg and create a new one like this:
lb1/lb2:
cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg_orig
cat /dev/null > /etc/haproxy/haproxy.cfg
vi /etc/haproxy/haproxy.cfg
Replace content with this:
listen sitevision-cluster 192.168.0.99:80
mode http
stats enable
stats auth someuser:somepassword
balance leastconn
cookie JSESSIONID prefix
option httpclose
option forwardfor
option httpchk GET /robots.txt HTTP/1.0
server webA 192.168.0.102:80 cookie A check
server webB 192.168.0.103:80 cookie B check
Afterwards, make sure the HAProxy start up correctly during boot:
chkconfig --level 35 haproxy on
To allow HAProxy to bind to the shared IP address, we add the following line to /etc/sysctl.conf:
net.ipv4.ip_nonlocal_bind = 1
.. and run:
sysctl -p
Now we have to create three configuration files for heartbeat, /etc/ha.d/authkeys, /etc/ha.d/ha.cf, and /etc/ha.d/haresources. /etc/ha.d/authkeys and /etc/ha.d/haresources must be identical on lb1 and lb2, and /etc/ha.d/ha.cf differs by just one line!
lb1/lb2:
vi /etc/ha.d/authkeys
/etc/ha.d/authkeys should be readable by root only, therefore we do this:
chmod 600 /etc/ha.d/authkeys
lb1:
vi /etc/ha.d/ha.cf
Replace content with:
uname -n
On lb1 and lb2.
The udpport, bcast, mcast, and ucast options specify how the two heartbeat nodes communicate with each other to find out if the other node is still alive. You can leave the udpport, bcast, and mcast lines as shown above, but in the ucast line it's important that you specify the IP address of the other heartbeat node; in this case it's 192.168.0.101 (lb2.example.com).
On lb2 the file looks pretty much the same, except that the ucast line holds the IP address of lb1:
lb2:
vi /etc/ha.d/ha.cf
Replace the content with:
vi /etc/ha.d/haresources
Replace content with:
lb1/lb2:
/etc/init.d/heartbeat start
Then run:
lb1:
ip addr sh eth0
... and you should find that lb1 is now listening on the shared IP address, too:
lb1:~# ip addr sh eth0
2: eth0: <BROADCAST,MULTICAST,UP,10000> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:a5:5b:93 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.100/24 brd 192.168.0.255 scope global eth0
inet 192.168.0.99/24 brd 192.168.0.255 scope global secondary eth0:0
You can check this again by running:
ifconfig
lb1:~# ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:A5:5B:93
inet addr:192.168.0.100 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:63983 errors:0 dropped:0 overruns:0 frame:0
TX packets:31480 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:92604963 (88.3 MiB) TX bytes:2689903 (2.5 MiB)
Interrupt:177 Base address:0x1400
eth0:0 Link encap:Ethernet HWaddr 00:0C:29:A5:5B:93
inet addr:192.168.0.99 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Interrupt:177 Base address:0x1400
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:56 errors:0 dropped:0 overruns:0 frame:0
TX packets:56 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:3888 (3.7 KiB) TX bytes:3888 (3.7 KiB)
As lb2 is the passive load balancer, it should not be listening on the virtual IP address as long as lb1 is up. We can check that with:
lb2:
ip addr sh eth0
The output should look like this:
lb2:~# ip addr sh eth0
2: eth0: <BROADCAST,MULTICAST,UP,10000> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:e0:78:92 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.101/24 brd 192.168.0.255 scope global eth0
inet6 fe80::20c:29ff:fee0:7892/64 scope link
valid_lft forever preferred_lft forever
lb2:~#
The output of:
ifconfig
shouldn't display the virtual IP address either:
lb2:~# ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:E0:78:92
inet addr:192.168.0.101 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:75127 errors:0 dropped:0 overruns:0 frame:0
TX packets:42144 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:109669197 (104.5 MiB) TX bytes:3393369 (3.2 MiB)
Interrupt:169 Base address:0x1400
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:56 errors:0 dropped:0 overruns:0 frame:0
TX packets:56 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:3888 (3.7 KiB) TX bytes:3888 (3.7 KiB)
To make sure Heartbeat start up correctly during boot:
chkconfig --level 35 heartbeat on
lb1/lb2:
/etc/init.d/haproxy start
You can now make HTTP requests to the virtual IP address 192.168.0.99 (or to any domain/hostname that is pointing to the virtual IP address), and you should get content from the backend web servers.
You can test its high-availability/failover capabilities by switching off one backend web server - the load balancer should then redirect all requests to the remaining backend web server. Afterwards, switch off the active load balancer (lb1) - lb2 should take over immediately. You can check that by running:
lb2:
ip addr sh eth0
You should now see the virtual IP address in the output on lb2:
lb2:~# ip addr sh eth0
2: eth0: <BROADCAST,MULTICAST,UP,10000> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:e0:78:92 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.101/24 brd 192.168.0.255 scope global eth0
inet 192.168.0.99/24 brd 192.168.0.255 scope global secondary eth0:0
lb2:~#
The same goes for the output of
ifconfig
When lb1 comes up again, it will take over the master role again.