2 Setting Up Load Balancing by Using HAProxy

This chapter describes how to configure load balancing by using HAProxy. The chapter also includes configuration scenarios and examples.

Installing and Configuring HAProxy

Before you can set up load balancing by using HAProxy, you must first install and configure the feature.

  1. Install the haproxy package on each front-end server:

    sudo dnf install haproxy
  2. Edit the /etc/haproxy/haproxy.cfg file to configure HAProxy on each server.

    See HAProxy Configuration Directives.

  3. Enable access to the services or ports that you want HAProxy to handle.

    To accept incoming TCP requests on port 80, use the following command:

    sudo firewall-cmd --zone=zone --add-port=80/tcp
    sudo firewall-cmd --permanent --zone=zone --add-port=80/tcp
  4. Enable and start the haproxy service on each server:

    sudo systemctl enable --now haproxy

    If you change the HAProxy configuration, reload the haproxy service:

    sudo systemctl reload haproxy

HAProxy Configuration Directives

The /etc/haproxy/haproxy.cfg configuration file is divided into the following sections:

global

Defines global settings, such as the syslog facility and level to use for logging, the maximum number of concurrent connections that are allowed, and how many processes to start in daemon mode.

defaults

Defines the default settings for the other sections.

listen

Defines a complete proxy, which implicitly includes the frontend and backend components.

frontend

Defines the ports that accept client connections.

backend

Defines the servers to which the proxy forwards client connections.

Configuring Round Robin Load Balancing by Using HAProxy

The following example uses HAProxy to implement a front-end server that balances incoming requests between two backend web servers, and which also handles service outages on the backend servers.

The following figure shows an HAProxy server (10.0.0.10), which is connected to an externally facing network (10.0.0.0/24) and to an internal network (192.168.1.0/24). Two web servers, websrv1 (192.168.1.71) and websrv2 (192.168.1.72), are accessible on the internal network. The IP address 10.0.0.10 is in the private address range 10.0.0.0/24, which can't be routed on the Internet. An upstream Network Address Translation (NAT) gateway or a proxy server provides access to and from the Internet.

Figure 2-1 Example HAProxy Configuration for Load Balancing


The diagram shows an HAProxy server (10.0.0.10), which is connected to an externally facing network (10.0.0.0/24) and to an internal network (192.168.1.0/24). Two web servers, websrv1 (192.168.1.71) and websrv2 (192.168.1.72), are accessible on the internal network. The IP address 10.0.0.10 is in the private address range 10.0.0.0/24, which can't be routed on the Internet. An upstream NAT gateway or a proxy server provides access to and from the Internet.

The following is an example configuration in /etc/haproxy/haproxy.cfg on the server:

global
    daemon
    log 127.0.0.1 local0 debug
    maxconn 50000
    nbproc 1

defaults
    mode http
    timeout connect 5s
    timeout client 25s
    timeout server 25s
    timeout queue 10s

# Handle Incoming HTTP Connection Requests
listen  http-incoming
    mode http
    bind 10.0.0.10:80
# Use each server in turn, according to its weight value
    balance roundrobin
# Verify that service is available
    option httpchk HEAD / HTTP/1.1\r\nHost:\ www
# Insert X-Forwarded-For header
    option forwardfor
# Define the back-end servers, which can handle up to 512 concurrent connections each
    server websrv1 192.168.1.71:80 maxconn 512 check
    server websrv2 192.168.1.72:80 maxconn 512 check

This configuration balances HTTP traffic between the two backend web servers websrv1 and websrv2, whose firewalls are configured to accept incoming TCP requests on port 80. The traffic is distributed equally between the servers and each server can handle a maximum of 512 concurrent connections. A health-check is also configured and this performs a request for the HTTP headers on a request for the web root on each backend server.

After implementing basic /var/www/html/index.html files on the web servers and using curl to test connectivity, the following output shows how HAProxy balances the traffic between the servers and how it handles the httpd service stopping on websrv1:

while true; do curl http://10.0.0.10; sleep 1; done
This is HTTP server websrv1 (192.168.1.71).
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv1 (192.168.1.71).
This is HTTP server websrv2 (192.168.1.72).
...
This is HTTP server websrv2 (192.168.1.72).
<html><body><h1>503 Service Unavailable</h1>
No server is available to handle this request.
</body></html>
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv2 (192.168.1.72).
...
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv1 (192.168.1.71).
This is HTTP server websrv2 (192.168.1.72).
This is HTTP server websrv1 (192.168.1.71).
...

In this example, HAProxy detected that the httpd service had restarted on websrv1 and resumed using that server in addition to websrv2.

By combining the load balancing capability of HAProxy with the high-availability capability of Keepalived, you can configure a backup load balancer that ensures continuity of service if the primary load balancer fails. See Enhancing Load Balancing by Using Keepalived With HAProxy for more information on how this configuration can be extended.

Using Weighted Round Robin Load Balancing with HAProxy

HAProxy can also be configured to use the weighted round-robin algorithm to distribute traffic. This algorithm selects servers in turns, according to their weights, and distributes the server load without implementing certain other factors such as server response time. With weighted round-robin, you can balance traffic proportionally between servers based on processing power and resources available to a server.

To implement weighted round-robin, append weight values to each server in the configuration. For example, to distribute twice the amount of traffic to websrv1, change the configuration to include different weight ratios, as follows:

server websrv1 192.168.1.71:80 weight 2 maxconn 512 check
server websrv2 192.168.1.72:80 weight 1 maxconn 512 check

Adding Session Persistence for HAProxy

HAProxy provides a multitude of load balancing algorithms, some of which provide features that automatically ensure that web sessions have persistent connections to the same backend server. You can configure a balance algorithm such as hdr, rdp-cookie, source, uri, or url_param to ensure that traffic is always routed to the same web server for a particular incoming connection during the session. For example, the source algorithm creates a hash of the source IP address and maps it to a particular backend server. If you use the rdp-cookie, or url_param algorithms, you might need to configure the backend web servers or the web applications for these mechanisms to run efficiently.

If the implementation requires the use of the leastconn, roundrobin, or static-rr algorithm, you can achieve session persistence by using server-dependent cookies.

To enable session persistence for all pages on a web server, use the cookie directive to define the name of the cookie to be inserted and add the cookie option and server name to the server lines, for example:

cookie WEBSVR insert
server websrv1 192.168.1.71:80 weight 1 maxconn 512 cookie 1 check
server websrv2 192.168.1.72:80 weight 1 maxconn 512 cookie 2 check

HAProxy includes the Set-Cookie: header that identifies the web server in its response to the client, for example: Set-Cookie: WEBSVR=N; path=page_path . If a client specifies the WEBSVR cookie in a request, HAProxy forwards the request to the web server whose server cookievalue matches the value of WEBSVR.

To enable persistence selectively on a web server, use the cookie directive to configure the HAProxy to expect the specified cookie, typically a session ID cookie or other existing cookie, to be prefixed with the server cookie value and a ~ delimiter, for example:

cookie SESSIONID prefix
server websrv1 192.168.1.71:80 weight 1 maxconn 512 cookie 1 check
server websrv2 192.168.1.72:80 weight 1 maxconn 512 cookie 2 check

If the value of SESSIONID is prefixed with a server cookie value, for example: Set-Cookie: SESSIONID=N~Session_ID;, HAProxy strips the prefix and delimiter from the SESSIONID cookie before forwarding the request to the web server whose server cookie value matches the prefix.

The following example shows how to configure session persistence by using a prefixed cookie:

while true; do curl http://10.0.0.10 cookie "SESSIONID=1~1234;"; sleep 1; done
This is HTTP server websrv1 (192.168.1.71).
This is HTTP server websrv1 (192.168.1.71).
This is HTTP server websrv1 (192.168.1.71).
...

A real web application would typically set the session ID on the server side, in which case the first HAProxy response would include the prefixed cookie in the Set-Cookie: header.