1. Home
  2. Help Articles
  3. Using HAProxy for Load Balancing on E2E Cloud: Session Stickiness And Security

Using HAProxy for Load Balancing on E2E Cloud: Session Stickiness And Security

In the first part of this blog we set up HAProxy on a Virtual Load Balancer node on E2E Cloud. We also configured HAProxy to route requests to back-end web servers using a round-robin policy.

In this second and concluding part of this blog, we will run through a couple of advanced configuration settings that most websites require: session stickiness, and secure access to the web-site using SSL. But before we do so, we will show how our single HAProxy instance can cater to a large web-site with many web applications, by virtue of ACLs. While at it, we will bring up a few additional configuration options, like weighted round robin policy, and conclude with pointers to other important considerations for a production deployment.

Load-balancer Set-up For Multiple Web Applications

Many large web-sites consist of several clusters of web-servers, each cluster hosting a different web application. For example, there may be a web-site with main content along with a web-site for fun and entertainment. HAProxy is capable of routing requests to the right cluster based on URL patterns and Access Control Lists (ACLs).

To illustrate this capability, we first create two new web-server nodes on the E2E Cloud, named ‘webserver3‘ and ‘webserver4‘. As usual, we install Apache web server and PHP packages on the two new nodes. Then we deploy a second PHP application at the URL ‘/fun/films.php‘ on these two newly added nodes only. (The second application uses session persistence and its functionality will be explained in the next section.)

So, we have the following nodes (4 web servers and a load balancer) visible on our Dashboard:

Figure 1: Web-servers for Multiple Web Applications

So HAProxy must route client requests for the URL ‘/fun/films.php‘ to one of the nodes ‘webserver3‘ and ‘webserver4‘ only, or else the request cannot be serviced at all. To meet this requirement, we modify HAProxy configuration (/etc/haproxy/haproxy.cfg) and create one more backend (named ‘films‘) consisting of the two new web-server nodes only. This is in addition to the existing backend named ‘site‘.

Figure 2: Backend configuration for a new application

Then, in the existing frontend configuration, we introduce an ACL as shown below. What this means is that any URL path that begins with ‘/fun‘ will be routed to the backend named ‘films‘, whereas by default all other requests will land upon the (pre-existing) backend named ‘site‘ (consisting of nodes ‘websrv1‘ and ‘websrv2‘ only).

Figure 3: Frontend configuration with ACL

Figure 4: HAProxy Load-balancing with ACLs

Advanced Configuration Settings

Weighted Round Robin Policy: While making modifications for ACLs, we introduced a weightage to each server. In production, different servers may have different computing power, so using weights allows HAProxy to route more requests to more powerful servers. (In our case all web server nodes are similar, and all weights are equal, but we can use this as a template and tweak it in a different scenario.)

Max Connections Per Server: Secondly, we also made use of the ‘maxconn‘ parameter at the backend server level, so that each server can limit the number of connections to what is reasonable for its processing power. The global ‘maxconn‘ should be larger than the sum total of the server-level values of this parameter (leaving some room for more web server nodes to be added for scalability).

Session-stickiness

The new PHP application happens to use PHP sessions. Each time a client accesses this application, it supplies the name of a favourite film (through a HTTP GET parameter named ‘fav’), which is added to the PHP session. The server responds with the list of favourite films (unique to each client) gathered so far.

  1. <?php
  2. session_start();
  3. $fav = $_GET[‘fav’];
  4. if ( isset( $_SESSION[‘favourites’] ) ) {
  5.     $_SESSION[‘favourites’] .= ‘ , ‘;
  6.     $_SESSION[‘favourites’] .= $fav;
  7. } else {
  8.     $_SESSION[‘favourites’] = $fav;
  9. }
  10. $msg = ‘My Favourite Films: ‘. $_SESSION[‘favourites’];
  11. ?>
  12. <html>
  13.     <head>
  14.         <title>My Favourite Films</title>
  15.     </head>
  16.     <body>
  17.         <?php
  18.         echo $msg;
  19.         ?>
  20.     </body>
  21. </html>

PHP sessions may be local to a web-server instance (unless session persistence or replication are enabled). So, if the same client is randomly routed to different backend web-server instances at different times, when it makes a series of requests, its session data may become unpredictable. But a client can be forced to ‘stick‘ to a single web-server instance within the duration of a session.

One way to set this up in HAProxy is to introduce the ‘appsession‘ parameter at the relevant backend configuration (‘films‘ , in our case). Different types of applications use different HTTP Cookies for session management. In case of PHP applications, this Cookie is named ‘PHPSESSID’. By setting ‘appsession‘, HAProxy associates a single web-server with each ‘PHPSESSID’ (the one where this session was created), and routes repeated client requests containing the same session id to that same web server, as long as it is up and running or the session is timed out.

  1. appsession PHPSESSID len 32 timeout 1h

Of course, if that web server becomes unavailable for any reason, HAProxy should be free to route requests with that same session id to a different active server instance. This can be ensured by adding the option ‘redispatch‘ in the ‘defaults‘ section of the HAProxy configuration file.

  1. option redispatch

Testing

Now we can test both ACLs and session stickiness. We can tail the HAProxy logs on the load-balancer node:

  1. tail -f /var/log/haproxy.log

We also tail the web-server access logs on each of the nodes ‘webserver3‘ and ‘webserver4‘.

1. tail -f /var/log/apache2/access.log

From two different client machines, we send requests to the following URL from a browser: http://<External_IP_Address_Of_HAProxy_Node>/fun/films.php?fav=afilm

In each request we may set a different value for the query parameter named ‘fav‘.

From the first client, we send the following successive requests:

  1. http://<External_IP_Address_Of_HAProxy_Node>/fun/films.php?fav=avengers
  2. http://<External_IP_Address_Of_HAProxy_Node>/fun/films.php?fav=jurassicworld

The browser window on the first client will display:

Figure 5: One client session

From the second client, we send the following successive requests:

  1. http://<External_IP_Address_Of_HAProxy_Node>/fun/films.php?fav=race3
  2. http://<External_IP_Address_Of_HAProxy_Node>/fun/films.php?fav=gold

The browser window in the second client will display:

Figure 6: Another client session

From the response displayed in each client browser, it is clear that sessions are maintained correctly in each case. In Firefox, from the web console, we can also inspect the PHPSESSID values.

We can make the following two observations from the HAProxy and web-server logs:

  1. These requests with URL pattern ‘/fun‘ are routed to the backend named ‘films‘ only (ACL policies)
  2. A request from the same client IP address lands on the same web-server node (session stickiness)

Figure 7: HAProxy logs with sticky sessions

Figure 8: Access logs on webserver3 node with sticky sessions

Figure 9: Access logs on webserver4 node with sticky sessions

Security: SSL Termination

One last but crucial configuration we like to take up is related to security. Apache web servers can be configured to support HTTPS, but when we have HAProxy installed at the frontend, client requests will land upon the load balancer first.

So, it is recommended that we configure HAProxy to support HTTPS. This is a four-step process:

  1. Obtain a SSL certificate for our web-site. For experimental purposes, we created a self-signed certificate named haproxy.pem using openssl
  2. Bind the frontend to a HTTPS port (by default 443) and assign the SSL certificate to this frontend
  3. Enable HAProxy to redirect client requests sent over HTTP to the HTTPS port
  4. Add or set required headers to the HTTP request at each of the configured backends

The first three steps above are implemented at the frontend level. (We have renamed our frontend as ‘alltraffic‘ instead of ‘httptraffic‘.)

Figure 10: Frontend configuration for SSL

The fourth step above is implemented at each backend:

Figure 11: Backend configuration for SSL

Usually the X-Forward-Port and X-Forward-Proto headers help the application build the URL correctly when both HTTP and HTTPS requests are possible.

Terminating SSL at the load-balancer node does create some processing overhead at this node (compared to relaying the encrypted request to the backends). There is a parameter related to the underlying encryption algorithm being used. A higher value increases the processing overhead on the HAProxy node. It can be set in the ‘global‘ section of the HAProxy configuration:

  1. tune.ssl.default-dh-param 2048

So the SSL-encrypted requests terminate at the load-balancer, which routes unencrypted HTTP requests to the backend web-servers.

Figure 12: SSL Termination

Firewall Settings

We need to open the port 443 on the virtual load balancer node (running on CentOS), by running the command:

  1. iptables -A INPUT -i eth0 -p tcp –dport 443 -m state –state NEW,ESTABLISHED -j ACCEPT
  2. systemctl restart iptables

Testing

We now try to access our PHP Greeter application through a browser (Firefox), by pointing to the non-secure HTTP URL:

http://<External_IP_Address_Of_HAProxy_Node>/greet.php

Even then, Firefox will prompt us to add a security exception, indicating that the client is being forwarded to the secure HTTPS URL. This is also indicated in the address bar on the browser itself.

Figure 13: Accessing Web Application over SSL

Creating Machine Images And Monitoring

Tip: A lot of effort has been spent on configuring the virtual load balancer instance. A machine image of this node can be saved so that similar load-balancer instances can be created in future using it as a template. On E2E Cloud, this can be achieved by first shutting down the HAProxy node and then saving an image of this node from the Dashboard.

Figure 14: Saving HAProxy machine image

Figure 15: Creating machine image from HAProxy node

Optionally, a machine image of web-server instances could also be saved.

Monitoring Using Zabbix: The HAProxy node, like any other virtual node on the E2E Cloud, can be monitored using Zabbix. We should take advantage of this capability.

Figure 16: Monitoring Load Balancer Health

Conclusion and Next Steps

E2E Cloud offers Virtual Load Balancer nodes to set up load-balancing using HAProxy. In these two blogs we reviewed how an HAProxy installation on E2E Cloud can be configured to support round-robin load balancing for simple and large multi-application web-sites with session stickiness and secure access over HTTPS.

We will end this blog by taking note of a few other considerations for a production deployment:

  • Firstly the Virtual Load Balancer node (and optionally the web-server nodes) should be tied to a domain using appropriate DNS settings
  • Accordingly, the SSL certificate for secure access to the HAProxy instance should be tied to the same domain
  • Finally, the load balancer should not become a single point of failure. So, an active-passive deployment of HAProxy may be recommended.
Updated on January 18, 2019

Was this article helpful?

Related Articles

Add A Comment