NetScaler Lync 2013 Frontend (Reverse proxy) balancing

I will not go into much detail about Lync 2013 infrastructures its just a basic setup to use the NetScaler as reverse proxy for external access to the Lync Frontend server. I am still working on the virtual server configuration for the Lync Edge and internal Lync Frontend server but will follow soon. Especially the Lync Edge is a bit tricky because you can’t use SNAT (not recommended) for your load balancing configuration, so the NetScaler needs to own the gateway and all traffic must path though the NetScaler.

Very useful is the Lync 2013 Protocol Map to understand the communication flow.

Add Custom Availability Monitors

add lb monitor monitor-LYNC-TCP4443 TCP -LRTM ENABLED -destPort 4443 -secure YES 
add lb monitor monitor-LYNC-TCP8080 TCP -LRTM ENABLED -destPort 8080

Add Lync Servers

add server LYFE01 10.1.1.30
add server LYFE02 10.1.1.31

Create Service Groups and bind Monitors

add serviceGroup service-LYNC-FE_8080 HTTP -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP YES -appflowLog DISABLED 
add serviceGroup service-LYNC-FE_4443 SSL -maxClient 0 -maxReq 0 -cip DISABLED -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP YES -appflowLog DISABLED

bind serviceGroup service-LYNC-FE_8080 LYFE01 8080 -CustomServerID "\"None\"" 
bind serviceGroup service-LYNC-FE_8080 LYFE02 8080 -CustomServerID "\"None\"" 
bind serviceGroup service-LYNC-FE_8080 -monitorName monitor-LYNC-TCP8080 
bind serviceGroup service-LYNC-FE_4443 LYFE01 4443 -CustomServerID "\"None\"" 
bind serviceGroup service-LYNC-FE_4443 LYFE02 4443 -CustomServerID "\"None\"" 
bind serviceGroup service-LYNC-FE_4443 -monitorName monitor-LYNC-TCP4443

Create Virtual Servers for Lync

add lb vserver vserver-LYNC-FE_80 HTTP 10.44.124.6 80 -persistenceType COOKIEINSERT -timeout 180 -cookieName MS-WSMAN -cltTimeout 180 
add lb vserver vserver-LYNC-FE_443 SSL 10.44.124.6 443 -persistenceType COOKIEINSERT -timeout 180 -cookieName MS-WSMAN -cltTimeout 180

Bind Virtual Servers to Service Groups and configure SSL

bind lb vserver vserver-LYNC-FE_80 service-LYNC-FE_8080 
bind lb vserver vserver-LYNC-FE_443 service-LYNC-FE_4443 

set ssl vserver vserver-LYNC-FE_443 -tls11 DISABLED -tls12 DISABLED 
bind ssl vserver vserver-LYNC-FE_443 -certkeyName lync.mydomain.net

If you want more details please have a look into the Deplyment Guide – Citrix NetScaler for Microsoft Lync

More information about from Microsoft you find here: Network Planning, Monitoring, and Troubleshooting with Lync Server

 

F5 iRule for layer 7 balancing

This is an iRule for F5 BIGIP to balance on the requested http path, so http://domain.com/appversion1-0 or http://domain.com/appversion2-0 . The benefit is that you have different server pools under the same domain.

With the iRule you can also access every member and open a status.txt or servername.txt with the following path http://domain.com/monitor/servername.txt?app=appversion1-0&node=1 .  This option is for monitoring reasons as you can monitor every node over the external URL.

I also use the status.txt for an http monitor to see if the server is online or offline. You can use that to set the web server offline without doing that over the F5 management console.

GET /monitor/status.txt HTTP/1.1\r\nHOST:\r\nConnection: Close\r\n\r\n
Enable: ONLINE
Disable: OFFLINE

In the servername.txt I write the name of the server.

This is the iRule:

when HTTP_REQUEST {
switch -glob [string tolower [HTTP::path]] {
"*appversion1-0*" { pool pool_http80_domain.com-appversion1-0}
"*appversion2-0*" { pool pool_http80_domain.com-appversion2-0}
"*monitor*" {

switch [URI::query [HTTP::uri] "app"] {
appversion1-0 {
switch [URI::query [HTTP::uri] "node"] {
1 { pool pool_http80_domain.com-appversion1-0 member 10.0.1.1 80 }
2 { pool pool_http80_domain.com-appversion1-0 member 10.0.1.2 80 }

default { HTTP::respond 200 content "<html><head><title>Member Status</title></head><body>INVALID OR MISSING QUERYSTRING: You must enter the URL in the following format http://domain.com/monitor/(status|servername).txt?app=AppVersionNumber&node=NodeNumber" "Content-Type" "text/html" }

}
}

appversion2-0 {
switch [URI::query [HTTP::uri] "node"] {
1 { pool pool_http80_domain.com-appversion2-0 member 10.0.2.1 80 }
2 { pool pool_http80_domain.com-appversion2-0 member 10.0.2.2 80 }

default { HTTP::respond 200 content "<html><head><title>Member Status</title></head><body>INVALID OR MISSING QUERYSTRING: You must enter the URL in the following format http://domain.com/monitor/(status|serversame).txt?app=AppVersionNumber&node=NodeNumber" "Content-Type" "text/html" }

}
}

default { HTTP::respond 200 content "<html><head><title>Member Status</title></head><body>INVALID OR MISSING QUERYSTRING: You must enter the URL in the following format http://domain.com/monitor/(status|servername).txt?app=AppVersionNumber&node=NodeNumber" "Content-Type" "text/html" }

}
}

default { pool pool_http80_domain-appversion1-0 }

}
}

The default app pool is appversion1-0 so everything that didn’t match is forwarded to that pool.