|
| 1 | +--- |
| 2 | +slug: how-to-configure-haproxy-http-load-balancing-and-health-checks |
| 3 | +title: "How to Configure HAProxy HTTP Load Balancing and Health Checks" |
| 4 | +description: "Learn how to configure HAProxy for HTTP load balancing, with instructions on updating frontend and backend settings, path-based routing, and health checks." |
| 5 | +authors: ["Tom Henderson"] |
| 6 | +contributors: ["Tom Henderson"] |
| 7 | +published: 2024-09-18 |
| 8 | +keywords: ['haproxy','http load balancing','http health checks','haproxy acl'] |
| 9 | +license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' |
| 10 | +--- |
| 11 | + |
| 12 | +[HAProxy](http://www.haproxy.org) is an intermediary gateway application that manages traffic between frontend clients and backend server resources. HAProxy can be configured to load balance traffic between a set of backend servers, and it can be configured to route HTTP requests according to the URL path of the request. |
| 13 | + |
| 14 | +This guide is the second part in a series on HAProxy. The first guide, [Getting Started with HAProxy TCP Load Balancing and Health Checks](/docs/guides/getting-started-with-haproxy-tcp-load-balancing-and-health-checks/), provided steps to build a minimally configured network of Nanodes: |
| 15 | + |
| 16 | +- An HAProxy node was set up as a TCP load balancer |
| 17 | +- Three Linode Marketplace WordPress servers served as the backends |
| 18 | +- TCP health checks were configured on the HAProxy node |
| 19 | + |
| 20 | +This second guide presents another configuration for this server cluster that demonstrates how to use path-based routing and HTTP health checks. |
| 21 | + |
| 22 | +## Before You Begin |
| 23 | + |
| 24 | +Follow the [Before You Begin](/docs/guides/getting-started-with-haproxy-tcp-load-balancing-and-health-checks/#before-you-begin) and [Install HAProxy](/docs/guides/getting-started-with-haproxy-tcp-load-balancing-and-health-checks/#install-haproxy) sections of the [Getting Started with HAProxy TCP Load Balancing and Health Checks](/docs/guides/getting-started-with-haproxy-tcp-load-balancing-and-health-checks/) guide. |
| 25 | + |
| 26 | +{{< note >}} |
| 27 | +This guide is written for a non-root user. Commands that require elevated privileges are prefixed with `sudo`. If you’re not familiar with the `sudo` command, see the [Users and Groups](/docs/guides/linux-users-and-groups/) guide. |
| 28 | +{{< /note >}} |
| 29 | + |
| 30 | +## HTTP Path-based Routing |
| 31 | + |
| 32 | +By using the `acl` and `use_backend` directives, HAProxy can direct URL requests to specific backend servers based on the incoming HTTP request string. |
| 33 | + |
| 34 | +### The `acl` Directive |
| 35 | + |
| 36 | +An [*ACL* (Access Control List)](https://www.haproxy.com/documentation/haproxy-configuration-tutorials/core-concepts/acls/#acl-syntax) directive can be used to match a specific URL path. The syntax for an ACL is: |
| 37 | + |
| 38 | +```file {title="/etc/haproxy/haproxy.conf"} |
| 39 | +acl {{< placeholder "ACL_NAME" >}} {{< placeholder "ACL_FETCH" >}} {{< placeholder "ACL_URL_MATCH_STRING" >}} |
| 40 | +``` |
| 41 | + |
| 42 | +An ACL *fetch* method is used to retrieve information from the HTTP request. The `path` fetch method gets the URL for the request. For example, this directive is named `match_foo` and evaluates to true if the URL path for the request is `/foo`: |
| 43 | + |
| 44 | +```file {title="/etc/haproxy/haproxy.conf"} |
| 45 | +acl match_foo path /foo |
| 46 | +``` |
| 47 | + |
| 48 | +There are [variants of the `path` method](https://docs.haproxy.org/2.4/configuration.html#7.3.6-path) that provide similar functionality. For example, `path_beg` checks that the beginning of the path matches the URL string provided. As well, there are fetch methods for other request information, like the source IP of the address or any URL parameters that were present. |
| 49 | + |
| 50 | +When you define an ACL, you specify a unique name for it. That name is later referenced in your configuration (demonstrated in the next [`use_backend` Directive](#the-use_backend-directive) section) to direct requests to specific backends. |
| 51 | + |
| 52 | +### The `use_backend` Directive |
| 53 | + |
| 54 | +A `use_backend` HAProxy directive routes requests to specific backend servers. This directive references a previously-declared `acl` directive by the unique name associated with it. If that ACL evaluated to true for the request, then the request is routed to that specific backend. The syntax for this is: |
| 55 | + |
| 56 | +```file {title="/etc/haproxy/haproxy.conf"} |
| 57 | +use_backend {{< placeholder "BACKEND_NAME" >}} if {{< placeholder "ACL_NAME" >}} |
| 58 | +``` |
| 59 | + |
| 60 | +### Path-based Routing Example |
| 61 | + |
| 62 | +To demonstrate how path-based routing works in practice, follow these steps for the HAProxy and WordPress cluster: |
| 63 | + |
| 64 | +1. Edit the HAProxy configuration to include this `frontend` section: |
| 65 | + |
| 66 | + ```file {title="/etc/haproxy/haproxy.cfg"} |
| 67 | + frontend http |
| 68 | + bind *:80 |
| 69 | + mode http |
| 70 | + acl sample-page path_beg /index.php/sample-page/ |
| 71 | + acl author-archive path_beg /index.php/author/admin/ |
| 72 | + use_backend backend1 if sample-page |
| 73 | + use_backend backend2 if author-archive |
| 74 | + default_backend mybackend |
| 75 | + ``` |
| 76 | +
|
| 77 | + Here's an explanation for each line: |
| 78 | +
|
| 79 | + - `frontend http`: Defines a frontend named `http` that handles incoming HTTP connections. |
| 80 | + - `bind *:80`: Binds the frontend to all available IP addresses on port `80`, listening for incoming traffic. |
| 81 | + - `mode http`: Sets the mode to HTTP, allowing traffic to be processed at the application layer. |
| 82 | + - `acl sample-page path_beg /index.php/sample-page/`: Defines an ACL named `sample-page` to match requests starting with `/index.php/sample-page/`. |
| 83 | + - `acl author-archive path_beg /index.php/author/admin/`: Defines an ACL named `author-archive` to match requests starting with `/index.php/author/admin/`. |
| 84 | + - `use_backend backend1 if sample-page`: Routes requests to `backend1` if the `sample-page` ACL is matched. |
| 85 | + - `use_backend backend2 if author-archive`: Routes requests to `backend2` if the `author-archive` ACL is matched. |
| 86 | + - `default_backend mybackend`: Routes all other requests to the default backend, `mybackend`, if no ACL matches. |
| 87 | +
|
| 88 | +1. The HAProxy configuration must also include two additional `backend` sections. These direct incoming frontend requests to the correct backend server. Update the configuration file to include these sections. Replace the placeholder IP addresses with the addresses from the WordPress servers: |
| 89 | +
|
| 90 | + ```file {title="/etc/haproxy/haproxy.cfg"} |
| 91 | + backend mybackend |
| 92 | + mode http |
| 93 | + balance roundrobin |
| 94 | + server backend1 {{< placeholder "backend1_VLAN_IP_ADDRESS" >}}:80 |
| 95 | + server backend2 {{< placeholder "backend2_VLAN_IP_ADDRESS" >}}:80 |
| 96 | + server backend3 {{< placeholder "backend3_VLAN_IP_ADDRESS" >}}:80 |
| 97 | +
|
| 98 | + backend backend1 |
| 99 | + mode http |
| 100 | + server backend1 {{< placeholder "backend1_VLAN_IP_ADDRESS" >}}:80 |
| 101 | +
|
| 102 | + backend backend2 |
| 103 | + mode http |
| 104 | + server backend2 {{< placeholder "backend2_VLAN_IP_ADDRESS" >}}:80 |
| 105 | + ``` |
| 106 | +
|
| 107 | + Here's an explanation of each line: |
| 108 | +
|
| 109 | + - `backend mybackend`: Defines a backend pool called `mybackend` that handles client requests directed to it. |
| 110 | + - `mode http`: Sets the backend mode to HTTP, processing traffic at the application layer. |
| 111 | + - `balance roundrobin`: Distributes incoming requests evenly across the servers in the `mybackend` pool. |
| 112 | + - `server backend1/backend2/backend3`: Lists three servers (`backend1`, `backend2`, `backend3`) in the `mybackend` pool, each assigned their respective VLAN IP addresses and listening on port `80`. |
| 113 | + - `backend backend1`: Defines an individual backend for `backend1`, allowing specific configuration. |
| 114 | + - `server backend1`: Adds `backend1` with its VLAN IP address and port `80` to this backend. |
| 115 | + - `backend backend2`: Defines an individual backend for `backend2`, similar to the setup for `backend1`. |
| 116 | + - `server backend2`: Adds `backend2` with its VLAN IP address and port `80`. |
| 117 | +
|
| 118 | +## HTTP Health Checks |
| 119 | +
|
| 120 | +In [Getting Started with HAProxy TCP Load Balancing and Health Checks](/docs/guides/getting-started-with-haproxy-tcp-load-balancing-and-health-checks/), the HAProxy gateway was configured to test TCP and Layer 4 connectivity, marking servers as down when errors accumulate over time. HTTP health checks work similarly, but use standard HTTP response codes to mark servers as "down" based on their performance over a specified interval. |
| 121 | +
|
| 122 | +Like TCP health checks, HTTP health checks continue to test a server marked as "down" to see if it begins to respond. If it starts responding correctly, the server is added back to the pool of active servers. |
| 123 | +
|
| 124 | +You can also configure HTTP health checks to look for specific values rather than standard HTTP response codes. This can be done using *REGEX* (regular expressions) or simple text matches within the first 16 KB of the backend server's response. For example, a string could match phrases like "Alive" or "Logged In" within a session. |
| 125 | +
|
| 126 | +### Basic HTTP Health Checks |
| 127 | +
|
| 128 | +Like with TCP health checks, adding the `check` directive to the `server` lines within the `backend` section of your HAProxy configuration file monitors the health of backend servers. However, this is only a TCP health check. In order to activate an HTTP health check, you also need to add the `option httpchk` to the `backend` section. |
| 129 | +
|
| 130 | +To demonstrate this with the example HAProxy and WordPress cluster, add these new directives to the `backend mybackend` section of your HAProxy configuration: |
| 131 | +
|
| 132 | +```file {title="/etc/haproxy/haproxy.cfg"} |
| 133 | +backend mybackend |
| 134 | + option httpchk |
| 135 | + server backend1 {{< placeholder "backend1_IP_ADDRESS" >}}:80 check |
| 136 | + server backend2 {{< placeholder "backend2_IP_ADDRESS" >}}:80 check |
| 137 | + server backend3 {{< placeholder "backend3_IP_ADDRESS" >}}:80 check |
| 138 | +``` |
| 139 | + |
| 140 | +- `check`: Appending this keyword to each `server` entry activates the health check on that server. |
| 141 | +- `option httpchk`: Adding this option allows HAProxy to perform HTTP health checks. By default, standard responses from the server in the `200`-`399` range indicate that the server is healthy. Meanwhile, responses in the `500` range mark the server as down and remove it from the active pool until it recovers. |
| 142 | + |
| 143 | +{{< note >}} |
| 144 | +By default, the `option httpchk` directive sends a `GET` request to the `/` endpoint to determine server health. However, this can be customized for a specific endpoint, such as `/health`: |
| 145 | + |
| 146 | +```file {title="/etc/haproxy/haproxy.cfg"} |
| 147 | +backend mybackend |
| 148 | + option httpchk GET /health |
| 149 | +``` |
| 150 | +{{< /note >}} |
| 151 | + |
| 152 | +### String-based HTTP Health Checks |
| 153 | + |
| 154 | +With the `http-check expect string` directive, HTTP health checks can also query and match specific strings in the backend server's responses. However, only the first 64 KB of the response is examined. If headers are lengthy or include large elements like inline graphics, the desired string may not be within this limit, and it is then missed by the health check. |
| 155 | + |
| 156 | +To demonstrate this with the example HAProxy and WordPress cluster, add this new directive to the `backend mybackend` section of your HAProxy configuration: |
| 157 | + |
| 158 | +```file {title="/etc/haproxy/haproxy.cfg"} |
| 159 | +backend mybackend |
| 160 | + option httpchk |
| 161 | + http-check expect string OK |
| 162 | + server backend1 {{< placeholder "backend1_IP_ADDRESS" >}}:80 check |
| 163 | + server backend2 {{< placeholder "backend2_IP_ADDRESS" >}}:80 check |
| 164 | + server backend3 {{< placeholder "backend3_IP_ADDRESS" >}}:80 check |
| 165 | +``` |
| 166 | + |
| 167 | +- `http-check`: This directive checks a server response three times before marking the server as "down". The code above configures HAProxy to check that a response was returned the string `OK`. You can adjust this string to match any other server response that indicates a healthy state. |
| 168 | + |
| 169 | +### The HAProxy Stats Page |
| 170 | + |
| 171 | +HTTP health checks offer a more approachable way to monitor the health of your servers than TCP health checks. While HTTP health checks can still be viewed from log files, they can also be viewed from HAProxy's web-based graphical stats page. |
| 172 | + |
| 173 | +Enable the stats page by adding a `listen` section to the HAProxy configuration file:: |
| 174 | + |
| 175 | +```file {title="/etc/haproxy/haproxy.cfg"} |
| 176 | +listen stats |
| 177 | + bind *:8404 |
| 178 | + mode http |
| 179 | + stats enable |
| 180 | + stats uri /stats |
| 181 | + stats refresh 10s |
| 182 | + stats auth admin:password # Set a username and password for access |
| 183 | +``` |
| 184 | + |
| 185 | +- `listen` creates a new listen section named `stats`. |
| 186 | +- `bind` tells HAProxy to listen on all available network interfaces (`8`) on port `8404`. |
| 187 | +- `mode` is set to HTTP. |
| 188 | +- `stats enable` enables the stats function. |
| 189 | +- `stat uri/stats` defines the URL where the stats page can be accessed (`http://{{< placeholder "HAProxy_IP_ADDRESS" >}}:8404/stats`) |
| 190 | +- `stats refresh 10s` sets the auto-refresh interval at 10 seconds. |
| 191 | +- `stats auth admin:password` configures basic credentials for the stats page. Replace `admin` and `password` with the username and password of your choice. |
| 192 | + |
| 193 | +## Test HTTP Load Balancing with Health Checks |
| 194 | + |
| 195 | +Follow these steps to test the concepts from the previous sections: |
| 196 | + |
| 197 | +1. After including all of the directives described in the previous sections, your HAProxy configuration should now resemble the following. Verify that your configuration matches this: |
| 198 | + |
| 199 | + ```file {title="/etc/haproxy/haproxy.cfg"} |
| 200 | + listen stats |
| 201 | + bind *:8404 |
| 202 | + mode http |
| 203 | + stats enable |
| 204 | + stats uri /stats |
| 205 | + stats refresh 10s |
| 206 | + stats auth admin:password # Set a username and password for access |
| 207 | +
|
| 208 | + frontend http |
| 209 | + bind *:80 |
| 210 | + mode http |
| 211 | + acl sample-page path_beg /index.php/sample-page/ |
| 212 | + acl author-archive path_beg /index.php/author/admin/ |
| 213 | + use_backend backend1 if sample-page |
| 214 | + use_backend backend2 if author-archive |
| 215 | + default_backend mybackend |
| 216 | +
|
| 217 | + backend mybackend |
| 218 | + mode http |
| 219 | + balance roundrobin |
| 220 | + option httpchk |
| 221 | + server backend1 {{< placeholder "backend1_VLAN_IP_ADDRESS" >}}:80 check |
| 222 | + server backend2 {{< placeholder "backend2_VLAN_IP_ADDRESS" >}}:80 check |
| 223 | + server backend3 {{< placeholder "backend3_VLAN_IP_ADDRESS" >}}:80 check |
| 224 | +
|
| 225 | + backend backend1 |
| 226 | + mode http |
| 227 | + option httpchk |
| 228 | + server backend1 {{< placeholder "backend1_VLAN_IP_ADDRESS" >}}:80 check |
| 229 | +
|
| 230 | + backend backend2 |
| 231 | + mode http |
| 232 | + option httpchk |
| 233 | + server backend2 {{< placeholder "backend2_VLAN_IP_ADDRESS" >}}:80 check |
| 234 | + ``` |
| 235 | +
|
| 236 | +1. Reload HAProxy for the changes to take effect: |
| 237 | +
|
| 238 | + ```command |
| 239 | + sudo systemctl reload haproxy |
| 240 | + ``` |
| 241 | +
|
| 242 | + {{< note >}} |
| 243 | + If you encounter any errors after reloading HAProxy, run the following command to check for syntax errors in your `haproxy.cfg` file: |
| 244 | +
|
| 245 | + ```command |
| 246 | + sudo haproxy -c -f /etc/haproxy/haproxy.cfg |
| 247 | + ``` |
| 248 | +
|
| 249 | + An error message is returned if the configuration file has logical or syntax errors. When the check is complete, each error is listed one per line. This command only verifies the syntax and basic logic of the configuration, it does not guarantee that the configuration works as intended when running. |
| 250 | + {{< /note >}} |
| 251 | +
|
| 252 | +1. Open a web browser and access the HAProxy server’s IP address: |
| 253 | +
|
| 254 | + ```command |
| 255 | + http://{{< placeholder "HAPROXY_SERVER_IP_ADDRESS" >}} |
| 256 | + ``` |
| 257 | +
|
| 258 | + {{< note >}} |
| 259 | + If your browser warns of no HTTPS/TLS certificate, ignore the warning or use the advanced settings to reach the site. |
| 260 | + {{< /note >}} |
| 261 | +
|
| 262 | + This page is served by the `mybackend` pool that consists of all three WordPress instances: |
| 263 | +
|
| 264 | +  |
| 265 | +
|
| 266 | +1. Open the default WordPress Sample Page: |
| 267 | +
|
| 268 | + ```command |
| 269 | + http://{{< placeholder "HAPROXY_SERVER_IP_ADDRESS" >}}/index.php/sample-page/ |
| 270 | + ``` |
| 271 | +
|
| 272 | + This URL should only be served by `backend1`: |
| 273 | +
|
| 274 | +  |
| 275 | +
|
| 276 | +1. Navigate to the default WordPress author archive for `admin`: |
| 277 | +
|
| 278 | + ```command |
| 279 | + http://{{< placeholder "HAPROXY_SERVER_IP_ADDRESS" >}}/index.php/author/admin/ |
| 280 | + ``` |
| 281 | +
|
| 282 | + This URL should only be served by `backend2`: |
| 283 | +
|
| 284 | +  |
| 285 | +
|
| 286 | +1. Open a web browser and navigate the HAProxy server's public IP address with `:8404/stats` appended to the URL: |
| 287 | +
|
| 288 | + ```command |
| 289 | + http://{{< placeholder "HAPROXY_SERVER_IP_ADDRESS">}}:8404/stats |
| 290 | + ``` |
| 291 | +
|
| 292 | +1. When prompted, enter your username and password from the `listen` section of the HAProxy configuration file. |
| 293 | +
|
| 294 | +1. You should now see the HAProxy stats page showing `mybackend`, `backend1`, and `backend2`: |
| 295 | +
|
| 296 | +  |
| 297 | +
|
| 298 | + This page holds a wealth of information regarding the health of your servers, including status, sessions, bytes in/out, errors, warnings, and more. |
0 commit comments