by Bhattman at gmail.com

 

The title sounds like a movie title. Actually its more related to the next scenario which I thought was unique enough to write about.  Recently a client came to me to discuss a problem she wanted solved by an F5 Load balancer (yes she asked for it by name)

Here is a bit how the story goes

“ I have an application that needs to shape traffic on 3 sets of criteria.  I need to direct traffic based on variable in the URI ala query which the server will redirect the consumer.  If the query doesn’t exist I want to load balance on header information (user-name), if the header doesn’t exist I want to load balance using generic default settings.  The consumer will initially come without a query but with user-name header, however, it’s possible that they might not have both sets of information.

 

Without boring you of what comes after, I can tell you that the client wants to load balance based on the URL example:

http://something.blah.com/file.html?po=03

 

The client has made a distinction that the number, “03” in this example, indicates the load balancer member node.  There is no real logic to which number is associated to which server.  It’s simply a number.  So if she has 5 web server the combination of numbers is going to be 01 to 05.  Of course this leads to our very first question. Why would you want the client to use number contained in the URI to determine which member node it must use?   This is a very good question.  The short answer is that the URI is the final form of which the client will use to persist on a particular member.   So the iRule she wants is to make sure they driven towards the correct member of the pool. How can we do this?

Suppose there are 5 web servers.  You would create 5 pools (each server in each pool) + 1 pool that contained all 5 servers. For example

 

pool_something_lb

pool_something_01

pool_something_02

.

.

.

pool_something_05

 

Based on the naming convention above the iRule would something like this:

  1: when HTTP_REQUEST {
  2:   if { [HTTP::query] contains "P0" } { 
  3:     set serverid [substr [HTTP::query] 5 2]
  4:        if { $serverid <= [active_members pool_something_lb] }
  5:           pool pool_something_$serverid
  6:           }
  7:   }
  8: }

 

So how do we handle the header information.  If you recall we created 5 pools with numbers (i.e, pool_something_01).   So we need is to take the header “user-name” and converted so that it would equal either 1 or 2 or 3 or 4 or 5.    How do we do this?

We use crc32 to convert the value in the header into the an numeric equivalent and store into a variable called NODENUM and then divide it using MOD against the active nodes in pool_something_lb which totals 5 active servers. This would provide you reminder which would be 0, 1, 2, 3, 4.  Since we are looking for 1,2,3,4,or 5 we increment the number.  The next step is to send it to one of the 5 servers in “pool_something_lb”.   This is where “lindex” and “active_member –list” command comes in handy.  With “active-member –list” against pool_something_lb we determine the IP address and port of all servers in the pool.  Using Lindex and NODENUM we can select one of the servers and thus the request goes to a server which then tells the client which members server to return to by redirect the client using the URI.

Enough of describing it what would the code look like?

 

  1: when HTTP_REQUEST {
  2:   if { [HTTP::query] contains "P0" } { 
  3:     set serverid [substr [HTTP::query] 5 2]
  4:        if { $serverid <= [active_members pool_something_lb] }
  5:           pool pool_something_$serverid
  6:           }
  7:   }
  8:   elseif { [HTTP::header exists "user-name"] } {
  9:    set NODENUM [crc32 [HTTP::header "user-name"]]
 10:    set NODENUM [expr $NODENUM % [active_members pool_something_lb] ]
 11:    incr NODENUM
 12:    pool pool_something_lb member [lindex [active_member -list pool_something_lb] $NODENUM]
 13:   }
 14: }

 

However, this is not the end of our story.  What happens when server dies while being load balanced?  In otherwords what if the server fails after the node member is selected in a pool.  This is where LB_FAILED event comes into play.  We have to assume that if it’s failed then http query information is invalid, BUT the header “user-name” is still valid.  We simply need to re-evaluate based on what is active in a pool.   You can do this by adjusting your code to do the following:

 

  1: when HTTP_REQUEST {
  2:   if { [HTTP::query] contains "P0" } { 
  3:     set serverid [substr [HTTP::query] 5 2]
  4:        if { $serverid <= [active_members pool_something_lb] }
  5:           pool pool_something_$serverid
  6:           }
  7:   }
  8:   elseif { [HTTP::header exists "user-name"] } {
  9:    set NODENUM [crc32 [HTTP::header "user-name"]]
 10:    set NODENUM [expr $NODENUM % [active_members pool_pool_something_lb] ]
 11:    incr NODENUM
 12:    pool pool_something_lb member [lindex [active_member -list pool_pool_something_lb] $NODENUM]
 13:   }
 14: }
 15: 
 16: when LB_FAILED { 
 17:  if { [HTTP::header exists "user-name"] } {
 18:    set NODENUM [crc32 [HTTP::header "user-name"]]
 19:    set NODENUM [expr $NODENUM % [active_members pool_pool_something_lb] ]
 20:    LB::reselect pool pool_pool_something_lb member [lindex [active_member -list pool_pool_something_lb] $NODENUM]
 21:    } else { 
 22:      pool pool_something_lb
 23:      LB::reselect
 24:    }
 25: }

 

Well there you have it.  You now have a irule that can shape traffic based on query, headers and whatever fails.