Chapter 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.

2.1 Installing and Configuring HAProxy

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

To install HAProxy:

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

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

    See Section 2.2, “HAProxy Configuration Directives”.

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

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

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

    # systemctl enable --now haproxy

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

    # systemctl reload haproxy

2.2 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 subsequent 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.

2.3 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 back-end web servers, and which also handles service outages on the back-end 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 cannot 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 cannot 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 back-end 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 demonstrate 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 in the event that the primary load balancer fails. See Section 3.4, “Setting Up Load Balancing by Using Keepalived With HAProxy” for more information on how this configuration can be extended.

2.4 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. Weighted round-robin allows you to balance traffic proportionally between servers based on processing power and resources available to a server.

To implement weighted round-robin, simply append weight values to each server in your 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

2.5 Adding Session Persistence for HAProxy

HAProxy provides a multitude of load balancing algorithms, some of which provide features that automatically help to make sure that web sessions have persistent connections to the same back-end server. For instance, 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 back-end server. If you use the rdp-cookie, or url_param algorithms, you may need to configure your back-end web servers or your web applications to facilitate these mechanisms.

If your 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 an additional 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 subsequently 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 specify that HAProxy should expect the specified cookie, usually 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 demonstrates how using a prefixed cookie enables session persistence:

$ 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 usually 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.