Load Balancing
Last updated: November 8th 2022
Introduction
In this article we will cover how to set up simple load balancing on Webdock with Nginx and proxy the traffic to two or more application servers.
If you don't want to bother with the nitty gritty of setting up your own load balancer and you can afford it, we can recommend the Cloudflare Load Balancer.
Prerequisites
In this article we assume you want to load balance a PHP application that needs MariaDB/MySQL access. You will need at least 4 servers to follow along, 1 load balancer, 2 (or more) application servers and a database/cache server as well.
Provision application servers
As stated in the prerequisites you will need at least two application servers. Spin them up if you haven't already. Mine are called appserverone and appservertwo.
Notice their IP addresses. You will use them shortly.
In order for us to see the balancing in action later, SSH into the two application servers and modify the default index.php file in the /var/www/html/
folder:
$ sudo nano /var/www/html/index.php
Change the name of the headings to differentiate the two so you know which server you are hitting when you visit your load balanced setup in a browser.
Create the load balancer and install required software
Next up is the load balancing server. This server is responsible for proxying requests to the application servers.
You have two options here:
- Provision one of our perfect stacks that comes with everything you need ie. Nginx, SSL certificate and more but also comes with an overhead of software not needed for the load balancer. You can of course just remove this software with apt remove --purge list-of-packages.
- Configuring the load balancer from scratch yourself. This guide will follow this approach.
Start off by provisioning a server (I called it loadbalancer) with the latest stable Ubuntu software (Focal 20.04 at the time of writing), SSH into the server and install nginx:
$ sudo apt-get update $ sudo apt-get install -y nginx
Go ahead and open the load balancer in order to see that Nginx is installed by copying the public IP address and paste it into your browser. You should see the Welcome to nginx! default page.
Configure the load balancer and obtain an SSL certificate
First, remove the default sites-enabled file from nginx:
$ sudo rm /etc/nginx/sites-enabled/default
Second, add your own configuration file for nginx to spread out the traffic to the app servers:
$ sudo touch /etc/nginx/conf.d/serverdomain.com.conf
And modify it:
server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; server_name serverdomain.com www.serverdomain.com; }
Test if syntax is correct and reload:
$ sudo nginx -t $ sudo service nginx reload
Now download the Let’s Encrypt snap client and symlink it so the binary run from anywhere:
$ sudo snap install --classic certbot $ sudo ln -s /snap/bin/certbot /usr/bin/certbot
If you encounter any issues with the above, try upgrading your system as a whole with the regular apt commands:
$ sudo apt update $ sudo apt upgrade
Obtain the SSL/TLS Certificate - follow the instructions to set it up:
$ sudo certbot --nginx -d serverdomain.com
Finally, configure the .conf file to round-robin traffic between the app servers. Notice that Certbort modified the confguration quite a bit - add the following shown in bold:
upstream backend { server appserveripaddress:443; server appserveripaddress:443; } server { server_name serverdomain.com; # managed by Certbot listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/loadbalancer.vps.webdock.io/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/loadbalancer.vps.webdock.io/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot # We will include proxy_params which will help us proxy off to http requests the proxy_pass off to our upstream called “backend” location / { include proxy_params; proxy_pass https://backend; proxy_redirect off; } } server { if ($host = loadbalancer.vps.webdock.io) { return 301 https://$host$request_uri; } # managed by Certbot # Notice the root is removed listen 80 ; listen [::]:80 ; server_name loadbalancer.vps.webdock.io; return 404; # managed by Certbot }
Let's see if this works:
$ sudo nginx -t $ sudo service nginx reload
Session persistence - simple option
With round robin-load balancing there is no guarantee that the same client will be always directed to the same server. In order to ensure this you need to be able to persist the user session when balancing the servers. This can be done in several ways and the most simple method is by ip hashing.
With ip hashing, the client’s IP is used as a hashing key to determine what application server should be selected for the client’s requests. This method ensures that the requests from the same client (provided they are not behind a proxy or a shared IP address) will always be directed to the same server except when this server is unavailable. Add the following in bold to your load balancers .conf file server block:
upstream backend { ip_hash; server appserveripaddress:443; server appserveripaddress:443; }
Test it out by going to your loadbalancer and notice that you won't get bounced from appserverone to appservertwo from now on.
Database server
You can read a detailed explanation on how to enable remote access to MariaDB on Webdock here. The linked information assumes you start with our LEMP stack.
If you haven't spun up a database server go ahead and do so. I'll use a LEMP stack for this. Notice it comes with an overhead of software but gives you all the database tools you need
To allow remote connections to a MySQL server, you need to perform the following steps:
- Configure the MySQL server to listen for specific IP addresses
- Open the MySQL port in your firewall.
- Grant access to the remote user.
Configure the MySQL server
The first step is to set the MySQL server to listen on specific IP addresses. To do so, you need to edit the MySQL configuration file and add or change the value of the bind-address option. If the address is 0.0.0.0, the MySQL server accepts connections on all host IPv4 interfaces. Note that we are communicating on a public connection, thus no 127.0.0.1.
For MariaDB v10.6 and below:
$ sudo nano /etc/mysql/my.cnf
For MariaDB version above 10.6:
$ sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
Locate the bind address and change it from 127.0.0.1
(localhost) to 0.0.0.0
bind-address = 0.0.0.0
And restart mysql for changes to take affect
$ sudo systemctl restart mysql
Configuring UFW firewall
The last step is to configure your firewall to allow traffic on the default MySQL port (port 3306).
$ sudo ufw allow from appserverone_ip_address to any port 3306 $ sudo ufw allow from appservertwo_ip_address to any port 3306
Granting Access to the application servers
The next step is to allow access to the database to the application servers. Log in to the MySQL server:
$ sudo mysql
From inside the MySQL shell, run the GRANT statement as follows:
GRANT ALL ON database_server_name.* TO app_server_root_user_name@'app_server_ip_address' IDENTIFIED BY 'database_server_database_password';
Test it out!
If everything is setup up correctly, you will be able to login to the remote MySQL server from the terminal using your password:
$ mysql -u user_name_on_app_server -h database_server_ip -p
For testing access to data from the database server we need some data in the database to show, then try to dump the data. If you follow along - SQL query executed in PHPMyAdmin:
CREATE TABLE Persons ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, firstname VARCHAR(30) NOT NULL, lastname VARCHAR(30) NOT NULL, email VARCHAR(50), reg_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )
And manually fill some data to the table
Then the following to the top of any of your application servers index.php files:
database_server_ip"; $userName = "admin"; $password = "database_server_password"; $databaseName = "database_server_name"; try { $connection = new PDO("mysql:host=$server;dbname=$databaseName", $username, $password); $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $statement = $connection->prepare("SELECT id, firstname, lastname FROM Persons"); $statement->execute(); $result = $statement->fetchAll(); die(var_dump($result)); } catch(PDOException $exception) { $exception->getMessage(); } $connection = null; ?>
Please note this is just simple code for example purposes and you shouldn't really access your database in this manner in PHP but use some sort of database wrapper or framework.
Conclusion
In this article we have shown how you can get a basic load balancing setup up and running on Webdock and allow database access from any of your application servers. MariaDB can be further spread out over multiple servers for load balancing of the database itself but that is beyond the scope of this article. We hope you found it useful!
Article Author: Thomas Damsgaard
Related articles
-
Webdock Performance Guarantee
In this article we define what our Performance Guarantee really means, and what you can expect from our hardware profiles as a customer.
Last updated: November 16th 2022
-
Webdock Server Benchmarks
Webdock is seriously fast. Click through to read more about how Webdock performs across our varying infrastructure.
Last updated: November 5th 2024
-
Setting Cache control headers for common content types Nginx and Apache
In this article we go through how to set correct Cache Control headers and best practices.
Last updated: November 8th 2022
-
How to Benchmark your server with ApacheBench
In this article we go through the commands you need to run in order to benchmark your server using ApacheBench as well as how you interpret the results.
Last updated: November 8th 2022
-
Enabling HTTP2
Learn how to Enable and configure HTTP2.
Last updated: November 8th 2022
-
Enabling Brotli Compression
In this article we show how to enable Brotli compression. Brotli has shown compression ratios of up to 26% smaller than current methods, with less CPU usage.
Last updated: November 8th 2022
-
Why Google Pagespeed or GTMetrix scores aren't as relevant as you think
In this article we discuss why Google Pagespeed and GTMetrix scores aren't as relevant an indicator of how good your website is as you might think.
Last updated: November 8th 2022
-
Optimizing Nginx for High Traffic Websites
Tweaking Nginx configuration for much better performance.
Last updated: July 19th 2023
-
Optimizing Apache for High Traffic Websites
Tweaking Apache configuration for much better performance.
Last updated: July 19th 2023