HAProxy is one of very few pieces of software that are truly a joy to use. It's written in C, it's reliable, fast, secure and versatile.
I've been using it for a number of years now for all kinds of purposes and its ability to inspect and manipulate HTTP requests is one of the features I use most often.
In this tutorial, I won't explain how to install and configure HAProxy, but rather how to use HAProxy for load balancing or simply redirecting HTTP traffic to desired backends by matching specific cookies or request headers. The cherry on the top will be dynamic HAProxy backends defined in map files, for easier automation and cleaner configuration file.
For this scenario, lets assume that we have an frontend named fe_main and a couple of backends - be_alpha, be_bravo, be_charlie and be_delta as default backend.
Our fe_main frontend handles a bunch of RESTful API requests and based on X-API request header value, we'll let HAProxy decide where to pass those requests. If the request doesn't have such header, it'll be forwarded to default HAProxy backend (be_delta).
To keep haproxy.cfg configuration file as clean as possible, we'll map X-API values with backends in separate, map file. Thanks to HAProxy's powerful, but still easy to read configuration language, we can achieve all of the above in just a few lines.
frontend fe_main bind 192.168.1.100:80 mode http # check the presence of X-API request header acl xapi_header req.hdr(X-API) -m found # if X-API header exists, check the backend to which the request needs to be forwarded # use be_delta if there's no match use_backend %[req.hdr(X-API),lower,map(/etc/haproxy/api.map,be_delta)] if xapi_header # if X-API header doesn't exist use default backend default_backend be_delta # backends backend be_alpha server alpha_server 192.168.1.200:80 backend be_bravo server bravo_server 192.168.1.201:80 backend be_charlie server charlie_server 192.168.1.202:80 backend be_delta server delta_server 192.168.1.203:80
The /etc/haproxy/api.map map file looks very simple. It contains space-delimited key-value pairs. The value of X-API header is key and the HAProxy backend which should handle the request is the value.
# X-API value | HAProxy backend v1.0 be_alpha v1.1 be_bravo v2.5 be_charlie v2.2 be_alpha v1.1 be_delta
So, if the request has X-API: v1.1 header, that request will be forwarded to be_bravo backend. The request with X-API: v2.2 will end up at be_alpha backend and so on. If there's no match in the map file, be_delta backend will be used.
If we wanted to inspect an cookie, instead of request header, pretty much the whole configuration remains the same:
frontend fe_main bind 192.168.1.100:80 mode http # check the presence of APIcookie request header acl api_cookie hdr_sub(cookie) -i APIcookie -m found # if APIcookie cookie exists, check which backend should handle the request # use be_delta if there's no match use_backend %[req.cook(APIcookie),lower,map(/etc/haproxy/api.map,be_delta)] if api_cookie # if APIcookie doesn't exist, use default backend default_backend be_delta # backends backend be_alpha server alpha_server 192.168.1.200:80 backend be_bravo server bravo_server 192.168.1.201:80 backend be_charlie server charlie_server 192.168.1.202:80 backend be_delta server delta_server 192.168.1.203:80
Pretty simple, right? Just make sure that you restart/reload HAProxy when you modify the map file, as HAProxy won't re-read it automatically.
For detailed information about configuration directives used in these examples, take a peek at official HAProxy documentation.