Routing traffic by URI using iRule

The DevCentral team recently developed a solution for routing traffic to a specific server based on the URI path.  A BIG-IP Local Traffic Manager (LTM) sits between the client and two servers to load balance the traffic for those servers.

 

The Challenge:

When a user conducts a search on a website and is directed to one of the servers, the search information is cached on that server.  If another user searches for that same data but the LTM load balances to the other server, the cached data from the first server does him no good.  So to solve this caching problem, the customer wants traffic that contains a specific search parameter to be routed to the second server (as long as the server is available).  Specifically in this case, when a user loads a page and the URI starts with /path/* that traffic should be sent to Server_2.

The picture below shows a representation of what the customer wants to accomplish:

 

 

The Solution:


So, the question becomes: How does the customer ensure all /path/* traffic is sent to a specific server?  Well, you guessed it...the ubiquitous and loveable iRule!  Everyone had a pretty good idea an iRule would be used to solve this problem, but what does that iRule look like?  Well, here it is!!

when HTTP_REQUEST {

if { [string tolower [HTTP::path]] starts_with "/path/" } {

persist none

set pm [lsearch -inline [active_members -list <pool name>] x.x.x.x*]

catch { pool <pool name> member [lindex $pm 0] [lindex $pm 1] }

}

}

 

Let's talk through the specifics of this solution...

For efficiency, start by checking the least likely condition.  If an HTTP_REQUEST comes in, immediately check for the "/path/" string. Keep in mind the "string tolower" command on the HTTP::path before the comparison to "/path/" to ensure the cases match correctly. Also, notice the use of HTTP::path instead of the full URI for the comparison...there's no need to use the full URI for this check.

Next, turn off persistence just in case another profile or iRule is forcing the connection to persist to a place other than the beloved Server_2.

Then, search all active members in the pool for the Server_2 IP address and port. The "lsearch -inline" ensures the matching value is returned instead of just the index. The "active_members -list" is used to ensure we get a list of IP addresses and ports, not just the number of active members. Note the asterisk behind the IP address in the search command...this is needed to ensure the port number is included in the search. Based on the searches, the resulting values are set in a variable called "pm".

Next, use the catch command to stop any TCL errors from causing problems. Because we are getting the active members list, it's possible that the pool member we are trying to match is NOT active and therefore the pool member listed in the pool command may not be there...this is what might cause that TCL error. Then send the traffic to the correct pool member, which requires the IP and port. The astute observer and especially the one familiar with the output of "active_members -list" will notice that each pool member returned in the list is already pre-formatted in "ip port" format. However, just using the pm variable in the pool command returns a TCL error, likely because the pm variable is a single object instead of two unique objects. So, the lindex is used to pull out each element individually to avoid the TCL error.

 

Testing:


Our team tested the iRule by adding it to a development site and then accessing several pages on that site.  We made sure the pages included "/path/" in the URIs!  We used tcpdump on the BIG-IP to capture the transactions (tcpdump -ni 0.0 -w/var/tmp/capture1.pcap tcp port 80 -s0) and then downloaded them locally and used Wireshark for analysis. Using these tools, we determined that all the "/path/" traffic routed to Server_2 and all other traffic was still balanced between Server_1 and Server_2.  So, the iRule worked correctly and it was ready for prime time!

 

 

Special thanks to Jason Rahm and Joe Pruitt for their outstanding technical expertise and support in solving this challenge!

 

For more information on this iRule or any other LTM issues, you can submit questions/comments in the "Comments" section below, or you can contact the DevCentral team at https://devcentral.f5.com/s/community/contact-us.

Published May 01, 2013
Version 1.0
  • a couple reasons we didn't use httpclass.

     

     

    1. we already have other traffic steering in iRules and can set the priority of this decision more precisely as well as manage persistence needs more granularly

     

    2. httpclass goes away in 11.4
  • lkchen's avatar
    lkchen
    Icon for Nimbostratus rankNimbostratus
    Hmmm, a couple years ago I was faced with a similar need -- content was developed using capabilities not present on the main production website, but is then unexpectedly announced to the world as a URI on the main site - and it wasn't a one off occurrence. Historically two competing web units...the older basic web running the main website (www.example.com), and the newer applications unit doing dynamic web development (app.example.com). But, recently (at the time) combined ... they had presented an idea that they would put a reverse proxy in front of the main site so that some URI's would direct to the dynamic web servers, which hasn't happened yet.

     

     

    So back then, I was rushed to come up with a solution, and I came up with my own 'proxy' iRule....

     

     

    when HTTP_REQUEST priority 500 {

     

    switch -glob [HTTP::uri] {

     

    "/path1" -

     

    "/path1/*" {

     

    if { $usessl == 1 } {

     

    set usepool pool1_ssl

     

    } else {

     

    set usepool pool1

     

    }

     

    }

     

    "/path2" -

     

    "/path2/*" {

     

    ...

     

    }

     

    }

     

     

    Where there are another iRule with when HTTP_REQUEST with priorities before (set usessl and set usepool to [LB::server pool]), and after (check if [active_members $usepool] < 1) - display apology page, else pool $usepool).

     

     

    Problem...you use string tolower on URI, but URI is case sensitive. (generally people are told to keep with lower case, but not everybody does, plus others with spaces.... Also some people do it intentionally, some with the intent that to hide/obscure private/internal only content (and getting the URI with case into robots.txt)....

     

     

    Wonder if I need to worry about persistence or use catch, etc. since the temporary irule is starting to get old.... ;)
  • How does this differ from the ProxyPass functionality added in 11.4 (previously implemented as an iRule)? And does it work with CPM (the replacement for HTTP Class in 11.4)?
  • CPM is a possibility for replacing the iRule. Proxypass primary functionality is handling internal/external host/uri mapping.