This post originally appeared on the Layer 7 Blog.

In a previous blog post, I showed how to use NetScaler CPX to load balance a set of backend web servers. In this post, I’ll show how to use the CPX in a modern microservices-based architecture.

In a micro services environment, a service is split up into several co-operating micro services, each located at a different network endpoint. A different way to build the same service is to use a monolith pattern.

For example, imagine a WidgetShop service that has been built as a monolith:

Although there are clearly identifiable services within the monolith, the entire service is always deployed and scaled together. So, even if the cart service didn’t need to be scaled up, it still gets deployed to every backend server. When the monolith is split into micro services, it might look like this:

Each microservice gets deployed in containers and can be deployed independently of the other microservices and also scaled independently. Depending on the networking, each Docker container could get a unique routable IP address, or get a unique TCP port on its host. To route the incoming traffic  properly, the load balancer has to look at the path component of the incoming URL. Based on the path, the load balancer “switches” or “routes” the traffic to different servers/microservices/containers. This is sometimes known as URL-based routing.

To achieve this in NetScaler CPX we have to use something called “content switching,” Content Switching is very powerful — it can switch/route traffic based on the URL, HTTP headers, hostname, the payload itself and so on. To demonstrate URL-based routing, we’ll use Docker Compose again to layout the WidgetShop topology. To follow along, use the ‘ex2’ folder from this git repository https://github.com/chiradeep/cpxblog/

The Docker Compose file:

version: ‘2’
services:
accounts_a:
image: httpd:alpine
volumes:
– ${PWD}/:/usr/local/apache2/htdocs/
expose:
– 80
accounts_b:
image: httpd:alpine
volumes:
– ${PWD}/:/usr/local/apache2/htdocs/
expose:
– 80

cart_a:
image: httpd:alpine
volumes:
– ${PWD}/:/usr/local/apache2/htdocs/
expose:
– 80

catalog_a:
image: httpd:alpine
volumes:
– ${PWD}/:/usr/local/apache2/htdocs/
expose:
– 80
catalog_b:
image: httpd:alpine
volumes:
– ${PWD}/:/usr/local/apache2/htdocs/
expose:
– 80
cpx:
image: store/citrix/netscalercpx:11.1-53.11
ports:
– 22
– 88
tty: true
privileged: true

You can see that there are 2 Accounts containers, 2 Catalog containers and 1 cart container, plus the CPX. Let’s run this and determine the IP addresses Docker assigns to these containers.

$ docker-compose up -d
$ names=$(docker-compose ps | awk -F” ” ‘{print $1}’ | tail -n+3)
$ for c in $names ; do ip=$(docker inspect –format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}’ $c); echo “$c : $ip”; done
ex2_accounts_a_1 : 172.21.0.6
ex2_accounts_b_1 : 172.21.0.5
ex2_cart_a_1 : 172.21.0.2
ex2_catalog_a_1 : 172.21.0.4
ex2_catalog_b_1 : 172.21.0.3
ex2_cpx_1 : 172.21.0.7

To configure content switching in the NetScaler we first have to enable it:

$ docker-compose port ex2_cpx_1 22
0.0.0.0:32862
$ ssh -p 32862 root@localhost
root@629e788ff846:~# <span class="skimlinks-unlinked">cli_script.sh</span> 'enable feature cs'
Done

In a NetScaler a content switching virtual server (“cs vserver”) becomes the front-end listener. This in turn sends traffic to LB vservers that represent the backend microservices. The microservices are grouped into Service Groups.

The rule that tells the cs vserver to send traffic to a particular lb vserver is called a policy (cs policy). The set of CLI commands looks like this:

First, configure the service groups with the backend IPs we have already discovered:

<span class="skimlinks-unlinked">cli_script.sh</span> 'add servicegroup accounts HTTP'
<span class="skimlinks-unlinked">cli_script.sh</span> 'add servicegroup cart HTTP'
<span class="skimlinks-unlinked">cli_script.sh</span> 'add servicegroup catalog HTTP'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind servicegroup accounts 172.21.0.6 80'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind servicegroup accounts 172.21.0.5 80'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind servicegroup cart 172.21.0.2 80'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind servicegroup catalog 172.21.0.3 80'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind servicegroup catalog 172.21.0.4 80'

Then create the lb vservers and bind the service groups to them.

<span class="skimlinks-unlinked">cli_script.sh</span> 'add lb vserver Accounts HTTP'
<span class="skimlinks-unlinked">cli_script.sh</span> 'add lb vserver Cart HTTP'
<span class="skimlinks-unlinked">cli_script.sh</span> 'add lb vserver Catalog HTTP'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind lb vserver Accounts accounts'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind lb vserver Cart cart'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind lb vserver Catalog catalog'

Create the WidgetShop cs vserver:

<span class="skimlinks-unlinked">cli_script.sh</span> 'add cs vserver WidgetShop HTTP 172.21.0.7 88'

Create the policies and bind them to the cs verserver:

<span class="skimlinks-unlinked">cli_script.sh</span> 'add cs policy accounts_policy -url "/accounts/*"'
<span class="skimlinks-unlinked">cli_script.sh</span> 'add cs policy cart_policy -url "/cart/*"'
<span class="skimlinks-unlinked">cli_script.sh</span> 'add cs policy catalog_policy -url "/catalog/*"'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind cs vserver WidgetShop -policyname accounts_policy -targetLBVServer Accounts'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind cs vserver WidgetShop -policyname cart_policy -targetLBVServer Cart'
<span class="skimlinks-unlinked">cli_script.sh</span> 'bind cs vserver WidgetShop -policyname catalog_policy -targetLBVServer Catalog'

Try it out:

$ docker-compose port ex2_cpx_1 88
0.0.0.0:32861
$ wget -q -O – http://localhost:32861/accounts/
$

In an upcoming blog post we’ll see how to automate this somewhat manual process.