Recently, I have been looking into HAProxy as an alternative load balancer to NGINX. NGINX’ free version lacks features vital to a multi-container environment. The most important features we need are upstream health checks and sticky sessions.
So far, least_conn or ip_hash have worked well enough, but cracks are beginning to show. Two reasons forced the cracks to show: * Bad session handlers built into applications, forcing us away from least_conn to ip_hash * Heavy use from one IP address killing one container, and leaving others idle.
Being frugal is a good thing, so I started playing with HAProxy, discovering its features.
HAProxy supports the features mentioned above, but getting up and running took some experimenting. The docs for HAProxy are vast, and I felt overwhelmed. Finally, I figured out routing HTTP requests based on domain names, is possible in several ways, using ACLs.
Direct ACL in haproxy.cfg
First, you define an ACL with
acl <name-of-acl> hdr(host) -i <fqdn>. The
-i flag, tells
hdr(host) to perform a case-insensitive check on
Then, you can route the requests to their backends, using the
use_backend <backend-name> if <name-of-acl>
Here is a cleaned up snippet:
frontend fe-http-incoming bind :80 # Decide which ACL to set by reading host header acl goto-app-ghost hdr(host) -i eng.eelcowesemann.nl acl goto-app-wordpress hdr(host) -i www.eelcowesemann.nl # Use wanted backend, based on ALC use_backend app-ghost if goto-app-ghost use_backend app-wordpress if goto-app-wordpress
Another way to route requests is to use a mapping file. In this file, you have two columns like so:
cat /etc/haproxy/appservers.map # hostname backend eng.eelcowesemann.nl app-ghost-eng zelda.eelcowesemann.nl app-ghost-zelda www.eelcowesemann.nl app-ghost-www iddqd.nl static-iddqd
You can then tell HAProxy, you use this file to decide which backend to use based on the
frontend fe-http-incoming mode http bind :80 use_backend %[req.hdr(host),lower,map_dom(/etc/haproxy/appservers.map,app-ghost-eng)]
use_backend line explained:
req.hdr(host): use the Host request header
lower: translate the Host header to lowercase
map_dom(/etc/haproxy/appservers.map): the path to the mapping file
app-ghost-eng: the default backend to use if none provided.
Which method to use
You can choose whichever method you want, and whether you use a configuration management tool like Puppet. It is easier to generate a separate mapping file, and to point
haproxy.cfg towards that.
When not using configuration management, I would choose use the mapping file when the amount of host names you want to route exceeds ten entries, this way the main configuration file stays nice and clean.