Wednesday, April 4, 2012

Load balancing WSO2 IS cluster using Apache HTTP Server

First let's create IS cluster. WSO2 IS 3.2.3 can be downloaded from here.

1) Create two directories named node1 and node2.

2) Unzip wso2is-­‐3.2.3.zip distribution in to each of the node directories created above.


In this scenario, I'm going to change the host name of the servers. Let's first consider node1.


3) Change the host name to abc.com. This blog explains all the necessary steps.

Note: While creating the keystore, make sure to provide the new host name as the Common Name (CN).

What is your first and last name?
[Unknown]: abc.com

And the public certificate will look like


Owner: CN=abc.com, OU=qa, O=wso2, L=colombo, ST=western, C=sl
Issuer: CN=abc.com, OU=qa, O=wso2, L=colombo, ST=western, C=sl
Serial number: 4f788014
Valid from: Sun Apr 01 21:49:32 IST 2012 until: Sat Jun 30 21:49:32 IST 2012
Certificate fingerprints:
MD5: B3:B7:F0:73:8E:21:0C:FE:75:5C:9E:4C:5E:51:57:6E
SHA1: 7E:05:40:3C:07:89:CD:AC:3F:5B:F2:7E:E6:D5:A8:8E:85:6D:7D:C9
Signature algorithm name: SHA1withRSA
Version: 3


4) Enable clustering in axis2.xml and change domain name as "wso2.is.domain". After all the modifications, cluster related configuration in axis2.xml should look like this.

<clustering class="org.apache.axis2.clustering.tribes.TribesClusteringAgent" enable="true">
<parameter name="AvoidInitiation">true</parameter>
<parameter name="membershipScheme">multicast</parameter>
<parameter name="domain">wso2.is.domain</parameter>
<parameter name="synchronizeAll">true</parameter>
<parameter name="maxRetries">10</parameter>
<parameter name="mcastAddress">228.0.0.4</parameter>
<parameter name="mcastPort">45564</parameter>
<parameter name="mcastFrequency">500</parameter>
<parameter name="memberDropTime">3000</parameter>
<parameter name="mcastBindAddress">127.0.0.1</parameter>
<parameter name="localMemberHost">abc.com</parameter>
<parameter name="localMemberPort">4000</parameter>
<parameter name="preserveMessageOrder">true</parameter>
<parameter name="atmostOnceMessageSemantics">true</parameter>
<parameter name="properties">
<property name="backendServerURL" value="https://${hostName}:${httpsPort}/services/"/>
<property name="mgtConsoleURL" value="https://${hostName}:${httpsPort}/"/>
</parameter>
<!--
The list of static or well-known members. These entries will only be valid if the
"membershipScheme" above is set to "wka"
-->
<members>
<member>
<hostName>127.0.0.1</hostName>
<port>4000</port>
</member>
<member>
<hostName>127.0.0.1</hostName>
<port>4001</port>
</member>
</members>
<!--
Enable the groupManagement entry if you need to run this node as a cluster manager.
Multiple application domains with different GroupManagementAgent implementations
can be defined in this section.
-->
<groupManagement enable="false">
<applicationDomain name="apache.axis2.application.domain"
description="Axis2 group"
agent="org.apache.axis2.clustering.management.DefaultGroupManagementAgent"/>
</groupManagement>
<!--
This interface is responsible for handling management of a specific node in the cluster
The "enable" attribute indicates whether Node management has been enabled
-->
<nodeManager class="org.apache.axis2.clustering.management.DefaultNodeManager"
enable="false"/>
<!--
This interface is responsible for handling state replication. The property changes in
the Axis2 context hierarchy in this node, are propagated to all other nodes in the cluster.
The "excludes" patterns can be used to specify the prefixes (e.g. local_*) or
suffixes (e.g. *_local) of the properties to be excluded from replication. The pattern
"*" indicates that all properties in a particular context should not be replicated.
The "enable" attribute indicates whether context replication has been enabled
-->
<stateManager class="org.apache.axis2.clustering.state.DefaultStateManager"
enable="false">
<replication>
<defaults>
<exclude name="local_*"/>
<exclude name="LOCAL_*"/>
</defaults>
<context class="org.apache.axis2.context.ConfigurationContext">
<exclude name="local_*"/>
</context>
<context class="org.apache.axis2.context.ServiceGroupContext">
<exclude name="local_*"/>
</context>
<context class="org.apache.axis2.context.ServiceContext">
<exclude name="local_*"/>
</context>
</replication>
</stateManager>
</clustering>
view raw clustering hosted with ❤ by GitHub


5) Apache HTTP server is going to be used as the load balancer. To enable Apache2 mod_proxy, uncomment following properties in mgt-transports.xml

<parameter name="proxyPort">80</parameter>
<parameter name="proxyPort">443</parameter>
view raw apache ports hosted with ❤ by GitHub


With above changes, configuration of one of the instances is complete. Now let's change node2 IS instances.

6) To change the host name, enable clustering and apache mod_proxy please follow steps 3), 4) and 5). I'm using xyz. com as the host name of second IS instance. Therefore change carbon.xml, identity.xml and axis2.xml accordingly to reflect this new host name.

For this setup, a central user store is needed. The embedded LDAP shipped with WSO2 IS will be used in this scenario.

7) To disable the default LDAP of node2 change following property in embedded-ldap.xml

<Property name="enable">false</Property>
view raw embedded-ldap hosted with ❤ by GitHub


8) Then change user-mgt.xml of node2 IS instance so that it uses embedded LDAP of node1 as the user store.

<UserStoreManager class="org.wso2.carbon.user.core.ldap.ApacheDSUserStoreManager">
<Property name="ReadOnly">false</Property>
<Property name="ConnectionURL">ldap://localhost:10389</Property>
<Property name="ConnectionName">uid=admin,ou=system</Property>
<Property name="ConnectionPassword">admin</Property>
<Property name="passwordHashMethod">SHA</Property>
<Property name="UserNameListFilter">(objectClass=person)</Property>
<Property name="UserEntryObjectClass">inetOrgPerson</Property>
<Property name="UserSearchBase">ou=Users,dc=wso2,dc=org</Property>
<Property name="UserNameSearchFilter">(&amp;(objectClass=person)(uid=?))</Property>
<Property name="UserNameAttribute">uid</Property>
<Property name="PasswordJavaScriptRegEx">[\\S]{5,30}</Property>
<Property name="ReadLDAPGroups">true</Property>
<Property name="WriteLDAPGroups">true</Property>
<Property name="EmptyRolesAllowed">true</Property>
<Property name="GroupSearchBase">ou=Groups,dc=wso2,dc=org</Property>
<Property name="GroupNameListFilter">(objectClass=groupOfNames)</Property>
<Property name="GroupEntryObjectClass">groupOfNames</Property>
<Property name="GroupNameSearchFilter">(&amp;(objectClass=groupOfNames)(cn=?))</Property>
<Property name="GroupNameAttribute">cn</Property>
<Property name="MembershipAttribute">member</Property>
<Property name="UserRolesCacheEnabled">true</Property>
<Property name="ReplaceEscapeCharactersAtUserLogin">true</Property>
</UserStoreManager>
view raw user-mgt.xml hosted with ❤ by GitHub


9) Change the ports of the server by changing the off-set value in carbon.xml. I've changed offset value from 0 to 1. Now https port will be 9444 and http port will be 9764.

Now let's consider about configuring Apache http server as a load balancer.

10) If you've not already installed Apache server, install it using following command. (you have to be root to perform following actions)

apt-get install apache2

11) To enable necessary modules, go to /etc/apache2/mods-available directory and run following commands.

a2enmod proxy_http
a2enmod ssl
a2enmod proxy_balancer
a2enmod headers

12) Add following entry in httpd.conf (/etc/apach2)


LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule status_module modules/mod_status.so

12) Add a virtual host containing following content. You have to create it inside /etc/apache2/sites-available directory

<IfModule mod_proxy.c>
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<VirtualHost *:443>
ServerAdmin techops@wso2.com
ServerName wso2.com
ServerAlias wso2.com
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
SSLEngine On
SSLProxyEngine On
SSLCertificateFile /home/pavithra/Documents/IS/3.2.3/released/load_balance/certs/new/server.crt
SSLCertificateKeyFile /home/pavithra/Documents/IS/3.2.3/released/load_balance/certs/new/server.key
SSLCACertificateFile /home/pavithra/Documents/IS/3.2.3/released/load_balance/certs/new/ca.crt
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy balancer://wso2.is.domain>
BalancerMember https://abc.com:9443 route=1
BalancerMember https://xyz.com:9444 route=2
Order Deny,Allow
Deny from none
Allow from all
ProxySet lbmethod=byrequests stickysession=ROUTEID
ProxySet lbmethod=byrequests
</Proxy>
<Location /balancer-manager>
SetHandler balancer-manager
</Location>
ProxyPass /balancer-manager !
ProxyPass / balancer://wso2.is.domain/
ProxyPassReverse / https://abc.com:9443/
ProxyPassReverse / https://xyz.com:9444/
</VirtualHost>
</ifModule>
view raw gistfile1.txt hosted with ❤ by GitHub

Note : Since ssl is enabled you have to specify location of the certificates.

Load balancing configuration is done to be session aware.

13) Add above created virtual host to sites-enabled directory using following command.

a2dissite default-ssl
a2ensite wso2.com

Note : "wso2.com" is the name of the virtual host.

14) After all these configuration start apache server

sudo /etc/init.d/apach2 start

15) I've changed the host name of the server/load balancer to wso2.com. Therefore add following entry to /etc/hosts file

127.0.0.1 wso2.com

I've created load balancer certificates with Common name matching the server host name (wso2.com)

Final step is to import this certificate into the keystore and trust-store of two IS instances. For that use following commands.

16) keytool -import -alias servercert -file servercert.pem -keystore iskeystore.jks -storepass ispassword

Note : ispassword is the password of new keystore used by IS

17) keytool -import -alias servercert -file servercert.pem -keystore client-truststore.jks -storepass wso2carbon

18) Now start two IS instances.

19) If you type https://wso2.com/carbon or https://wso2.com:443/carbon you'll be redirected to one of the IS instances. If one server is down, you can observe that the traffic is routed to other server.