HOWTO: Set Up JSessionID-Based Persistence on a BigIP

Introduction

When using mod_jk to load-balance your application servers, you maintain persistence utilizing a special configuration parameter called a jvmRoute. If you have two app servers, you can set a jvmRoute parameter, for example, on Server A named appserver1 and on Server B you set the jvmRoute to appserver2. When the initial request for a new user session hits Server A, this app server will generate a JSESSIONID using the standard number of random characters but it will append the appserver1jvmRoute at the end of the JSESSIONID. When configured correctly, mod_jk will look for the jvmRoute in the JSESSIONID and will persist requests to the same application server for the remainder of that session.

If you want to utilize a BigIP LTM instead to load-balance your app servers and take advantage of many of the pre-written iRules out there that maintain persistence based off of a JSESSIONID, the first thing to keep in mind is that these irules do not maintain persistence in the same manner that mod_jk does. Instead, the BigIP creates an internal map of JSESSIONID's and the application server that the ID came from. There are several things that you need to do when setting up any virtual server with a JSESSIONID-based persistence irule, which is the scope of this article.

In order to attach an iRule to an HTTP virtual server, that virtual server must have an HTTP Profile attached to it. If you try to attach an iRule to a virtual server that does not have an HTTP Profile attached to it, the action will fail.

Step One: iRule Changes

The following iRule is one of the more basic ones, which maintains JSESSIONID-based persistence based off of the session cookie that tomcat and jboss create and manage:

when HTTP_REQUEST { 
if { [HTTP::cookie exists "JSESSIONID"] } {
persist uie [HTTP::cookie "JSESSIONID"] 1800
} else {
set jsess [findstr [HTTP::uri] "JSESSIONID" 11 ";"]
if { $jsess != "" } {
persist uie $jsess 1800
}
}
}
when HTTP_RESPONSE {
if { [HTTP::cookie exists "JSESSIONID"] } {
persist add uie [HTTP::cookie "JSESSIONID"] 1800
}
}

Please note that although utilizing a session cookie stored in app server memory may not be a best practice way of managing state, it is most likely the most popular state management mechanism in use. The 1800 second value above is an idle timeout and this should be higher or lower based upon the values of your web application.

This irule, or something similar, will be uploaded into your BigIP inside the irules section of the LTM's web-based administration screen

Step Two: Attach iRule to Virtual Server

Navigate to the VIP for your application server, (not for your web server). The persistence rule only needs to be applied to your application server VIP/VIPs, which is different from mod_jk which resides as a plug in on your web servers. Keep in mind that, as mentioned towards the beginning of this article, your VIP needs an HTTP Profile attached to it.

Step Three: Assign a Persistence Profile to the Virtual Server

This is the step that I commonly forget about, which is attaching the "Universal" (or a new one derived from "Universal") Persistence Profile to the Virtual Server. The Universal Persistence Profile is what makes it possible for the iRule to create that map of JSESSIONID's to app server nodes. Skipping this step will result in persistence failing to work as expected.You assign it by selecting it from a drop-down list of profiles labeled "Default Persistence Profile".

Step Four:  Enable oneconnect on Your App Server Virtual Server

Oneconnect allows the BigIP to look at individual tcp streams in order to distinguish one client connection from another.   I've found that if oneconnect is not enabled on the virtual server, the BigIP sometimes persists the sessions incorrectly.

Other Important Considerations

Switching to an HTTP-based load-balanced pool of application servers means that mod_jk is no longer required to be resident on your web servers but you still need some mechanism for funneling that request over to the BigIP-managed virtual server. If you are using apache web servers as your front-end, you can easily accomplish this task with mod_proxy_http and ProxyPassReverse to the VIP but the one thing to keep in mind is that some servlets and JSPs utilize convenience methods for generation of URLs back to the browser. Although the BigIP does not modify the Host HTTP Header in the HTTP Request from the web browser, mod_proxy_http does and I have seen some web applications break because the URL that the web application generates utilizes the VIP of the app server instead of the VIP of the web server. If this occurs, you can force mod_proxy_http to maintain the Host header by setting the ProxyPreserveHost configuration directive to On.

Another issue that I've run into with mod_proxy is that during periods of low activity, mod_proxy sometimes forwards a request to a connection that is no longer active.  Intermittently, my monitors report failures on the initial connect to the upstream appserver virtual server and a 502 Proxy Error is logged in the apache error log.  I've added the proxy-initial-not-pooled environment variable to the apache configuration in order to prevent mod_proxy from using an existing tcp connection when it is proxying a user's initial connection.  This has prevented those 502 Proxy Errors where a connection reset was logged.

IIS7 users should be able to utilize the Application Request Routing extension to accomplish the same thing that mod_proxy_http does for Apache but I (still) do not have any IIS7 servers on my network to play with.


There is a new eBook available on Amazon if you are looking to become more acquainted with iRules. Check out: An Introduction to F5 Networks LTM iRules (All Things F5 Networks, BIG-IP, TMOS and LTM v11). It is only $10!


Creative Commons Attribution-ShareAlike 3.0 Unported