Load Balancing Exchange 2010 CAS Array with HAProxy (Quick Guide)

Application Management Published on 5 mins Last updated

This Blog is for anyone wanting to load balance the Exchange 2010 CAS role using only open source software. In my example I will be starting with a simple Debian net-install and building the HAProxy package from source because I wanted the latest feature set available. I would definitely recommend using a recent 1.5-dev build if following this guide or parts of the HAProxy configuration may be incompatible.

Update the system and install dependencies :

  1. Update
root@localhost:~# apt-get update
  1. Install dependencies
root@localhost:~# apt-get install build-essential make libpcre3 libpcre3-dev

Downloading/Building the HAProxy package:

  1. Download the HAProxy Package available from http://haproxy.1wt.eu/
root@localhost:~# wget http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev11.tar.gz
  1. Extract the package
root@localhost:~# tar -xvzf haproxy-1.5-dev11.tar.gz
  1. Change directory and make the package
root@localhost:~# cd haproxy-1.5-dev11

root@localhost:~/haproxy-1.5-dev11# make TARGET=linux2628 ARCH=x86_64 USE_PCRE=1
  1. Install the newly compiled package and confirm it is installed.
root@localhost:~/haproxy-1.5-dev11# make install
root@localhost:~/haproxy-1.5-dev11# /usr/local/sbin/haproxy -vv

Assuming you didn't run into any errors with the previous commands you should now have HAProxy installed.

Configuring startup script:

  1. Create the startup script
root@localhost:~/haproxy-1.5-dev11# nano -w /etc/init.d/haproxy
  1. Paste the following into the new file and save it(with Ctrl+X)
#!/bin/sh
# /etc/init.d/haproxy
PATH=/bin:/usr/bin:/sbin:/usr/sbin
pidfile=/var/run/haproxy.pid
binpath=/usr/local/sbin/haproxy
options="-f /etc/haproxy/haproxy.cfg"
test -x $binpath || exit 0
case "$1" in
  start)
    echo -n "Starting HAproxy"
        $binpath $options
    #start-stop-daemon --start --quiet --exec $binpath -- $options
    echo "."
    ;;
  stop)
    echo -n "Stopping HAproxy"
    kill `cat $pidfile`
        #start-stop-daemon --stop --quiet --exec $binpath --pidfile $pidfile
    echo "."
    ;;
  restart)
    echo -n "Restarting HAproxy"
    #start-stop-daemon --stop --quiet --exec $binpath --pidfile $pidfile
    kill `cat $pidfile`
        sleep 1
    $binpath $options
    echo "."
    ;;
  *)
    echo "Usage: /etc/init.d/haproxy {start|stop|restart}"
    exit 1
esac
exit 0</code>
  1. Change permissions and register the startup script
root@localhost:~/haproxy-1.5-dev11# cd /etc/init.d
root@localhost:~/etc/init.d# chmod +x haproxy
root@localhost:~/etc/init.d# update-rc.d haproxy defaults

You should now be able to start and stop haproxy with “service haproxy <action>” where the action is start/stop/restart.

Creating the HAProxy configuration file:

  1. Create folder structure and open the config file for editing
root@localhost:~/etc/init.d# mkdir /etc/haproxy
root@localhost:~/etc/init.d# nano -w /etc/haproxy/haproxy.cfg
  1. Paste in the example configuration and adapt for your settings.

N.B. The bind lines below can be adapted to listen on a specific IP address, simply add your desired local IP: bind 192.168.72.120:135,192.168.72.120:60200,192.168.72.120:60201

global
daemon
stats socket /var/run/haproxy.stat mode 600 level admin
pidfile /var/run/haproxy.pid
maxconn 40000
ulimit-n 81000
defaults
mode http
balance roundrobin
timeout connect 4000
timeout client 86400000
timeout server 86400000
frontend CAS-RPC
bind :135,:60200,:60201
mode tcp
maxconn 40000
default_backend CAS-RPC-SERVERS
frontend CAS-WEB
bind :80,:443
mode tcp
maxconn 40000
default_backend CAS-WEB-SERVERS
frontend HT-SMTP
bind :25
mode tcp
maxconn 40000
default_backend HT-SERVERS
backend CAS-RPC-SERVERS
stick-table type ip size 10240k expire 60m
stick on src
option redispatch
option abortonclose
balance leastconn
server EXCH01 192.168.72.222 weight 1 check port 135 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
server EXCH02 192.168.72.223 weight 1 check port 135 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
backend CAS-WEB-SERVERS
stick-table type ip size 10240k expire 60m
stick on src
option redispatch
option abortonclose
balance leastconn
server EXCH01 192.168.72.222 weight 1 check port 443 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
server EXCH02 192.168.72.223 weight 1 check port 443 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
backend HT-SERVERS
option redispatch
option abortonclose
balance leastconn
server EXCH01 192.168.72.222 weight 1 check port 25 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
server EXCH02 192.168.72.223 weight 1 check port 25 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
listen stats :7777
stats enable
stats uri /
option httpclose
stats admin if TRUE
stats auth admin:password
  1. Start HAProxy with your new configuration
root@localhost:~/etc/init.d# service haproxy start

N.B. At this stage if you receive errors like below please check that something else is not listening on any of the required ports.

[ALERT] 205/123152 (2839) : Starting proxy CAS: cannot bind socket [192.168.72.120:135]

You now also have a WUI in the form of the HAProxy stats page which includes useful options such as taking a server offline etc.

http://<IP-ADDRESS>:7777/

Configuring the Exchange 2010 CAS role :

  1. Either configure the ports manually or using the following Registry file (user beware)

Link to the Registry file = https://downloads.loadbalancer.org/support/exchange/RPCPorts.reg

Manual Static Port Configuration

To set a static port for the RPC Client Access Service, open the registry on each CAS and navigate to:

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetservicesMSExchangeRPC

Here, you need to create a new key named ParametersSystem, and under this key create a new DWORD(32-bit) Value named TCP/IP Port as shown below. The Value for the DWORD should be the port number you want to use. Microsoft recommends you set this to a unique value between 59531 and 60554 and use the same value on all CAS. In this Blog the port used is 60200.

N.B. Make sure you use a DWORD Value for this key

To set a static port for the Address Book Service, open the registry on each CAS and navigate to:

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetservicesMSExchangeAB

Here, you need to create a new key named Parameters, and under this key create a new String Value named RpcTcpPort as shown below. Microsoft recommends you set this to a unique value between 59531 and 60554 and use the same value on all CAS. In this Blog the port used is 60201.

N.B. Make sure you use a STRING Value for this key

  1. Creating the DNS entry

Create a DNS record for the CAS Array, this should be the same as the load balancer's IP address(bind address if used earlier), e.g. cas.domain.com

  1. Configure the CAS array object

Use the following command from the Exchange 2010 management shell to create the object:

New-ClientAccessArray –Name “CAS-array” –FQDN “cas.domain.com” -Site “default-first-site-name”

N.B. change “default-first-site-name” to the AD site appropriate for your Client Access Servers
N.B. change “cas.domain.com” to the FQDN of the CAS array(same as the DNS entry)

If the mail database already existed before creating the array, you'll also need to run the following command to relate the new CAS array to the database:

Set-MailboxDatabase "NameofDatabase" -RpcClientAccessServer “cas.domain.com”

To verify the configuration of the CAS array, use the following commands from the Exchange Shell :

get-ClientAccessServer

lists the available Client Access Servers

get-ClientAccessArray

lists the Client Access Array and its members

Finished

Once you've completed all the previous steps you can now access your CAS services via your load balancer IP, it should also be correctly load balancing connections for better performance and real server resilience. There are still many ways you could build further resilience or add more features to this solution such as HA, DAG’s and SSL Termination but this will still give you perfectly adequate load balancing of the CAS and HT roles.