How to make an open source load balancer (stunnel + haproxy)

February 23rd, 2010

Our current setup we have multiple application and static content servers serving our ecommerce shopping software Veracart. Instead of spending thousands of dollars on an expensive load balancer appliance we decide to go the open source route. After looking at many different solutions what we finally ended up going with for our load balancer machine is stunnel for the ssl decryption and haproxy for the actual load balancing. stunnel is need because haproxy doesn’t support ssl in http mode.

Load balancer hardware: 1U, dual Intel Xeon 2.80GHz cpu’s, 4gigs of ram, sata drives.
Software: freebsd (or linux), stunnel 4.32 with xforwarded-for patch, haproxy 1.4.8

Stunnel Installation

First off if you want your web servers in your clusters to be able to know the connecting clients real ip address instead of the load balancers then you need to patch stunnel with xforwarded-for patch.

wget http://www.stunnel.org/download/stunnel/src/stunnel-4.32.tar.gz
wget http://haproxy.1wt.eu/download/patches/stunnel-4.32-xforwarded-for.diff
tar -zxvf stunnel-4.32.tar.gz
cd stunnel-4.32
patch -p1 < ../stunnel-4.32-xforwarded-for.diff
./configure
make && make install

Haproxy Installation

download: http://haproxy.1wt.eu/#down
or
cd /usr/ports/net/haproxy (from freebsd ports)
make && make install

Stunnel Configuration

Stunnel will receive all the https connections on port 443 and forward as http requests to haproxy on port 81 (or any port you want). From there haproxy will send the http request to the webserver cluster. Below the ip 1.2.3.4 represents your public ip that will goto your cluster. You can have as many [secure.domain.com] sections as you want or have domains with ssl certs for.

stunnel.conf

sslVersion = all
options = NO_SSLv2
setuid = root
setgid = stunnel
pid = /var/run/stunnel.pid
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
output = /var/log/stunnel.log

[secure.domain.com]
cert = /usr/local/openssl/certs/secure.domain.com.crt
key = /usr/local/openssl/private/secure.domain.com.key
accept  = 1.2.3.4:443
connect = 1.2.3.4:81
xforwardedfor = yes
TIMEOUTclose = 0

Haproxy Configuration

Haproxy will receive the decrypted https request, now just an http request, from stunnel on port 81. It will then load balance those requests across however many servers you have listed. I forward the request to port 81 on our apache webservers.

haproxy.conf

global
	daemon
	maxconn 10000
	nbproc 2
	log 127.0.0.1 syslog

defaults
	log global
	clitimeout 60000
	srvtimeout 30000
	contimeout 4000
	retries 3
	option redispatch
	option httpclose
	option abortonclose

listen domain_cluster_http 1.2.3.4:80
	mode http
	balance roundrobin
	cookie SERVERID insert nocache
	option forwardfor except 1.2.3.4
	reqadd X-Forwarded-Proto:\ http
	server server1 10.1.1.2:80 cookie s1 weight 1 maxconn 5000 check
	server server2 10.1.1.3:80 cookie s2 weight 1 maxconn 5000 check

listen domain_cluster_https 1.2.3.4:81
	mode http
	balance roundrobin
	cookie SERVERID insert nocache
	option forwardfor except 1.2.3.4
	reqadd X-Forwarded-Proto:\ https
	server server1 10.1.1.2:81 cookie s1 weight 1 maxconn 5000 check
	server server2 10.1.1.3:81 cookie s2 weight 1 maxconn 5000 check	

listen  lb1_stats [load balancer's public ip]:80
	mode http
	stats uri /
	stats auth username:password
	#stats refresh 5s

Apache Configuration

Lastly there is some things you will want to do to apache so it will know the real clients ip address. What I use is mod_rpaf, which changes the remote address of the client visible to other Apache modules from the X-Forwarded-For header. On freebsd there is a port for this.

cd /usr/ports/www/mod_rpaf2
make && make install

This will install the Apache2 version. After its installed open up your Apache’s http.conf file and add the following lines.

httpd.conf

# if DSO load module first:
LoadModule rpaf_module libexec/apache2/mod_rpaf-2.0.so
RPAFenable On
RPAFsethostname On
RPAFproxy_ips [load balancer's ip]
RPAFheader X-Forwarded-For

Next since we are forwarding https request to our webserver cluster nodes on port 81 we need to add the following line to http.conf

httpd.conf

Listen 81

Conclusion

We don’t have a huge amount of traffic about a steady 5Mbps, and the load balancer is always idling along with barely any load at all. This has worked out great for us. All the ssl decrypting is off loaded from the web servers and haproxy is super fast and has a great stats page to see what’s going on with your cluster’s.

Setting up Trax with EasyPHP (Windows)

December 10th, 2009

I soon found out that EasyPHP isn’t so easy to get PEAR and commandline php working. Hopefully this will help people that are unfortunately stuck on windoze and want to use EasyPHP to develop their php apps. I may look at WAMP next to see if it’s any easier. As well will make a guide for setting up OSX and Trax which is what I use personally.

1. Download and install easyPHP 5.3.0.

2. EasyPHP’s version of PEAR is jacked. To fix it, right click here on go-pear.phar and save it to “C:\Program Files\EasyPHP5.3.0\php\PEAR”. Just overwriting the existing go-pear.phar file.

3.  Open a command window (Start -> Run, type cmd) and

cd C:\Program Files\EasyPHP5.3.0\php

4. Install PEAR. In command window type go-pear.

5.   Now edit C:\Program Files\EasyPHP5.3.0\php\pear.bat file and change the line toward the top from this:

IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=.\php.exe"

to this:

IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=C:\Program Files\EasyPHP5.3.0\php\php.exe"

6. Edit your system path so you can run commandline commands without putting in the complete path.

  • Right-click My Computer and click Properties.
  • In the System Properties window, click on the Advanced tab.
  • In the Advanced section, click the Environment Variables button.
  • In the Environment Variables window, highlight the Path variable in the Systems Variable section and click the Edit button. Add the path “;C:\Program Files\EasyPHP5.3.0\php” to the end of whatever is in there already but be sure to have the “;” separating the addition from the old path.

7.  Open a new command window.  If you type in now “php -v“  you should show the version of php you are running in my case 5.3.0. Next type “pear” and you should see a bunch of output about pear options.

Now everything up until this point was just to get pear and php installed working from the commandline. :(  Now lets install PHP on Trax.

8.  Install Trax.  Instructions can be found here.  Following the instructions on that page you do the following commands.

First add the Trax channel server:

pear channel-discover pear.phpontrax.com

Next install Trax:

pear install trax/PHPonTrax

Lastly if you are using mysql you can run:

pear install -f pear/MDB2#mysql

9. Last thing to do is make a trax.bat file and place it in the folder C:\Program Files\EasyPHP5.3.0\php. The file contents will be the one line below.

php "C:\Program Files\EasyPHP5.3.0\php\PEAR\PHPonTrax\trax.php" %1

To verify your Trax install is working at the commandline type

trax -v

You should see the output Trax 0.16.0

If you want to start making an app cd into the directory for your project and type:

trax .

This will generate all the basic files needed to make a new Trax app.

That’s it now you should be ready to start making Trax apps!

Edit: Also need to change some Apache/PHP configurations. Right click the e icon next to the time. Then goto Configuration => Apache. This will open up the httpd.conf file for Apache.

1. uncomment the line:

LoadModule rewrite_module modules/mod_rewrite.so

2. Scroll to the bottom and add the following replacing “c:/projects/traxtest/public” with the correct path to your trax public folder for your app:

NameVirtualHost *:80
<VirtualHost *:80>
    DocumentRoot "c:/projects/traxtest/public"
    ServerName localhost
	<Directory "c:/projects/traxtest/public">
		Options FollowSymLinks Indexes
		AllowOverride All
		Order deny,allow
		Allow from 127.0.0.1
		deny from all
	</Directory>
</VirtualHost>

Next edit PHP Configuration. Right click the e and go to Configuration => PHP. Change the following lines:

short_open_tag = On
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED

In Trax Edit two files.
1. In your app’s public folder the .htaccess file make sure that the seperator on the include_path is a “;” and not a “:” and that the path to your app’s config folder is correct.

php_value include_path .;C:\projects\traxtest\config

2. In your app’s config folder edit the environment.php file and uncomment and set the PHP_LIB_ROOT constant.

define("PHP_LIB_ROOT",    "C:\Program Files\EasyPHP5.3.0\php\PEAR");

Then in your browser you should be able to goto the url: http://localhost and see the default trax welcome page.