Tuesday, October 27th, 2009
Easy HTTP load balancing with Apache
Usually a single AMP system is enough to serve - let's say - around 500 concurrent users. Sometimes more, sometimes less, strongly depending on the particular web application, the overall architecture of your system, of course the hardware itself, and how you define "concurrent users".
Nevertheless, if your server gets too slow, you'll need to take actions. You may upgrade your server up to the maximum (aka vertical scaling), optimize your software (aka refactoring), and finally add more servers (aka horizontal scaling). The whole process of horizontal scaling is quite complex and far too much for a single blog post, but here's a first shot. Others will follow.
Today I'll focus on one single aspect of horizontal scaling: an HTTP load balancer.
On the left: a whole crowd of people ready to visit our web site. On the right: our server farm (called workers). And in the middle: our current hero, the load balancer. The purpose of the load balancer (in this case an HTTP load balancer) is to distribute all incoming requests to our backend web servers. The load balancer hides all our backend servers to the public, and from the outside it looks like a single server doing all of the work.
Okay, let's start. Step by step.
Since version 2.2 the Apache web server ships a load balancer module called mod_proxy_balancer. All you need to do is to enable this module and the modules mod_proxy and mod_proxy_http:
LoadModule proxy_module mod_proxy.so LoadModule proxy_http_module mod_proxy_http.so LoadModule proxy_balancer_module mod_proxy_balancer.so
Please don't forget to load mod_proxy_http, because you wouldn't get any error messages if it's not loaded. The balancer just won't work.
Because mod_proxy makes Apache become an (open) proxy server, and open proxy servers are dangerous both to your network and to the Internet at large, I completely disable this feature:
ProxyRequests Off <Proxy *> Order deny,allow Deny from all </Proxy>
The load balancer doesn't need this feature at all.
Now I need to make sure all my backend web servers have the same content:
serverA htdocs% cat index.html This is A.
serverB htdocs% cat index.html This is B.
serverC htdocs% cat index.html This is C.
serverD htdocs% cat index.html This is D.
Okay, in this case the content differs, but I need this to show how the load balancer works.
And here's the actual load balancer configuration:
<Proxy balancer://clusterABCD> BalancerMember http://serverA BalancerMember http://serverB BalancerMember http://serverC BalancerMember http://serverD Order allow,deny Allow from all </Proxy> ProxyPass / balancer://clusterABCD/
The <Proxy>...</Proxy> container defines which backend servers belong to my balancer. I chose the name clusterABCD for this server group, but you are free to choose any name you want.
And the ProxyPass directive instructs the Apache to forward all incoming requests to this group of backend servers.
That's all? Yes, that's all. Here's the prove:
# repeat 12 lynx -source http://loadbalancer This is A. This is B. This is C. This is D. This is A. This is B. This is C. This is D. This is A. This is B. This is C. This is D.
Each request to the load balancer is forwarded to one of the backend servers. By default Apache simply counts the number of requests and makes sure every backend server gets the same amount of requests forwarded.
If you want to know more about available balancing algorithms please refer to Apache's mod_proxy_balancer manual.
Did you ever imagine setting up a load balancer would be this easy? Of course, there is more to say about (HTTP) load balancing and much more about vertical scaling too, but this is only a blog posting and not a place for such an expansive reference. If time and space allows I'll go into further details on this in the near future.