Load Balance Your Website on Amazon EC2

How to load balance a website hosted on an Amazon EC2 instance.

We’re going to run through the steps required to take a website hosted on a single Amazon EC2 instance, move it behind an Elastic Load Balancer (ELB), and spin up a fleet of servers to accommodate your hoards of dedicated fans.  Just to make it a tad more interesting, we’re also going to port the site across the country from North Carolina to one of Amazon’s west coast datacenters, and set up rsync to keep the filesystems in sync when changes are made to the master.  For this example we’re going to use Joomla, but it should work for pretty much any modern CMS on a Linux-Apache-MySQL-PHP (LAMP) stack.  We’re using Amazon’s Relational Database Service (RDS) to host our MySQL database, so replicating the database isn’t included in this tutorial.  In the intersest of scalability, separating your application from your database tier is a prerequisite step.  We’ll cover auto-scaling RDS in another tutorial if there’s demand.

Create the ELB and Point a DNS Record at It

The first thing we’re going to do is launch an Elastic Load Balancer (ELB) and point a DNS CNAME record at it.  It’s good to get this out of the way to give the DNS record time to propagate.  Unless you’re already using Amazon Route 53 to host DNS for your domain, you won’t want to point an A record to the IP address of your ELB as the IP address of your load balancer will change over time.  If you’re using Amazon’s Route 53 DNS service, you can also create an A record that acts as an alias.  By doing so, you can also point the zone apex record at it.  In this case though, we’re just going to create a subdomain called uswest.synergycode.com and point that at our ELB.

Click on Load Blancers on the left-hand menu.  In this region, we have none, so we’re going to create our first one.  Select Create Load Balancer.

Create Load Balancer 1

Name it something more useful than “Load Balancer 1.”  In this case we’re going to use it for web traffic, so I’m calling it “WWW”.  I’m only forwarding port 80 for this example.  If you’re serving SSL traffic you will want to open port 443 as well.

01-create-elb-2

For this example we’re going to use micro instances because they’re cheap – but not particularly effective.  We have to up the Response Timeout to something greater than 5 seconds or our servers will occasionally get diagnosed as unhealthy and dropped from the pool.  (Micro instances have a nasty habit of becoming CPU-bound even under light load.)

01-create-elb-3

You can skip adding instances at this time, review your options and click Create to launch your new Elastic Load Balancer.

01-create-elb-4

Once your ELB is created, select it from the list and copy the A Record from the Description tab.  This is what you’re going to point a DNS record to.

02-point-dns-cname

If you’re using Amazon’s Route 53 DNS service, you can create an A record that’s an Alias for this domain.  It’s similar to a CNAME record with one important difference:  You can use an Alias record to point the zone apex record for your domain.  This comes in really handy when you want traffic to yourdomain.com to be directed to your ELB.  If your DNS is hosted elsewhere, point a CNAME record at the fully qualified domain name for your ELB that you grabbed from the Description tab above.

02-point-dns-cname-2

Either way, now we can get to the fun stuff while the DNS records percolate.

Fire up an Ubuntu 12.04 Server

Launch a new Ubuntu 12.04 instance by whatever method you like best.   We’re going to use the Quck Launch Wizard to spin up a micro instance to use as a template for the others.  Let’s call this your Template server.

2-ubuntu

It doesn’t matter what Availability Zone (AZ) it’s in.  It has to be in the same AWS Region – an ELB can’t load balance across regions.  If you used the Quick Launch Wizard to create the instance, take note of the Security Group.

4-launch

Open up port 80 in the Security Group.  Similar to the ELB port forwarding, if you’re serving up SSL then you’ll need to open up port 443 as well.

ssh into the Instance

Alright, you’re here to set up an ELB – chances are you’re already familiar with how to ssh into a fresh instance.  If you don’t, there are a myriad of tutorials on how to do so.  If you’re on Windows using Putty, then you’ve got some hoops to jump through, but you can console yourself with the knowledge that your pain is self-inflicted.  If you insist, and need a refresher, you can always RTFM.

If you’re using a Linux box or Cygwin then all you need to do is:

  • chmod 600 your .pem file
  • ssh -i /path/to/your.pem ubuntu@ec2-xxx-xx-xx-xx.compute-1.amazonaws.com, where ec2-xxx-xx-xx-xx.compute-1.amazonaws.com is the Public DNS of your instance under the Description tab on the My Instances page.  Just remember to ssh in as the ubuntu user.

6-grab-public-dns

Configure the Server

It’s good practice (and often necessary) to update the apt package index.

sudo apt-get update

Install the minimum packages to get PHP and the MySQL client up-and-running:

sudo apt-get install apache2 php5 php5-mysql libapache2-mod-php5

Our website depends on a couple of other extensions to function.  Most servers have the GD and curl libraries installed, but it falls to you to figure out which additional extensions you need to install.  Anyway, this is the bare minimum we need to serve up synergycode.com.

sudo apt-get install php5-sqlite php5-gd php5-curl

Enable mod_rewrite.  It’s used in Joomla for Search Engine Friendly URLs and other nifty stuff.  Chances are pretty good that your site uses it.

sudo a2enmod php5 rewrite

rsync the Filesystem from Your Master Server

For this we’re going to need the .pem file you use to connect to the EC2 instance that currently hosts your website.  From here out, we’re going to refer to that instance as your Master server.  The instance we’re building now is your Template server, and later on we’re going to launch a fleet of Slave servers.

Grab the private key file you use to connect to your Master server and copy it to the instance we’re working on.  Assuming you’re running Linux locally, you can scp the file over.

scp -i template.pem master.pem 
 ubuntu@ec2-xxx-xx-xx-xx.compute-1.amazonaws.com:/home/ubuntu

Where:

  • template.pem is the private key file used to ssh into the Template server instance
  • ec2-xxx-xx-xx-xx.compute-1.amazonaws.com is the Public DNS of your Template instance
  • master.pem is the private key file you use to ssh into your Master server instance

Set the permissions on the master.pem file you just uploaded to your Template server.

chmod 600 master.pem

We’re going to do a little rsync Kung Fu to keep the Slave servers in sync with the Master server.  Keeping servers in sync is way beyond the scope of this little walkthrough, but this is a quick and easy way to set up very basic replication.

Let’s start by creating bin and log directories.  As ubuntu on the Template server:

cd
mkdir bin
mkdir log

Create a new file called syncup in /home/ubuntu/bin with the following contents (adjusted per your configuration, this assumes your website is currently being served up from /var/www on the Master server):

#!/bin/bash
sudo rsync -avz --delete --port=22 
 ubuntu@ec2-xxx-xx-xx-xx.compute-1.amazonaws.com:/var/www/ -e "ssh -i /home/ubuntu/master.pem" /var/www > /home/ubuntu/log/syncup_`date +"%F_%T"`.log

Make it executable:

chmod +x ~/bin/syncup

Reload the ubuntu default profile so that /home/ubuntu/bin is included in the path

source ~/.profile

Run our nifty little sync script:

syncup

When that’s done, check the log file to see if everything worked.  You should find a newly-created logfile in ~/log that lists all the files you just copied over.  If you look in /var/www you should see that your website filesystem as been replicated.  Check the ownership of the files you just copied over.  You may need to adjust file ownership and/or permissions.  (Apache2 expects all the files in /var/www to have user and group www-data.)  If needed:

sudo chown -R www-data:www-data /var/www/*

Configure Apache

We need to edit /etc/apache2/sites-enabled/000-default.  We’re going to make the following changes:

  • Change the ServerAdmin variable to something meaningful.
  • Set AllowOverride to for /var/www/ All (to enable .htaccess files to work)
  • Set index.php as the default file for /var/www/

sudo vi /etc/apache2/sites-enabled/000-default

Change the ServerAdmin email address from webmaster@localhost to something more useful.

Locate the <Directory /var/www/> </Directory> block and change AllowOverride to All, and add in a line for the DirectoryIndex directive.

<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
DirectoryIndex index.php
Order allow,deny
allow from all
</Directory>

Restart Apache

sudo service apache2 restart

Test your instance using the public DNS of the Template image.  It should just work, but on the off chance it doesn’t you can always blame it on the poor quality of this walkthrough.  Check the Apache error log in /var/log/apache2/error.log if it’s borked.  If the server is timing out without sending a response, double check that port 80 is open in the Security Group.  If that doesn’t do it then post a comment and we’ll see what we can do to help.

Create an Amazon Machine Image (AMI) from Your Template Server

The next thing we have to do is create an AMI from our kick-ass little Template server.  That’s as easy as right-clicking it in the Instances page and selecting Create Image (EBS AMI) from the drop-down list:

10-create-ami

Name your AMI.  In this case we’re going to call it WWW Template.  If you want to add additional storage or volumes you want to launch at instance startup then this is the time.  Our site is pretty small, so 8GB is plenty.  When you’ve got things set up as you’d like them, click Yes, Create.  Unless you select the No Reboot option, this will shut down your Template server instance to ensure that the volume(s) are consistent.

11-create-ami-2

Small AMIs usually only take a few minutes to become Available, but this is a good time to wander off and get a coffee.

11-create-ami-3

Freshly caffeinated, your AMI is ready.  Right-click the AMI you just created in the AMIs page and select Launch Instance from the drop-down list.

12-launch-from-ami

In the interests of brevity (yeah, too late), we’re going to launch 8 Slave instances right here.  This isn’t a fantastic approach as they’re all going to launch into the same AZ.  For a real production system we’d want to launch a couple of instances into each AZ in this region, as the ELB is going to evenly distribute requests across each AZ.  We also don’t have very good redundancy here.  The ELB will distribute the requests across all 8 of our Slaves, but they’re all in the same datacenter – so an outage that takes down our AZ would knock our site offline.  Best practice dictates spanning multiple AZs for maximum durability.

12-launch-from-ami-2

For this demonstration there’s nothing we need to enter into the instance details.

12-launch-from-ami-3

Whatever name or tags you attach now will apply to all the instances you create.  We’re just going to name them WWW Slave and leave it at that.

12-launch-from-ami-5

Pick a keypair to use for your Slave instances.

12-launch-from-ami-6

Launch your Slave instances into the same Security Group you created for the Template instance, or create a new dedicated Security Group for your Slave instances.

12-launch-from-ami-7

Review your selections and Launch your fleet of web servers!

12-launch-from-ami-8

It takes a few minutes for all of them to come to life and have a status of Running.  As soon as they’re all Running and have 2/2 checks passed in the Status Checks column, we’re ready to add them to the ELB.

13-instances-pending

First things first – test a couple of the instances to make sure they’re serving up traffic.  Browse to the Public DNS of a sample of the servers.  Each should serve up a copy of your website.  Once you’re satisified they’re working, select Load Balancers from the left-hand menu, and then select your new ELB from the list.

Click on the Instances tab of your ELB, and then hit the +/- icon in the top-right corner to Add/Remove instances.

14-add-instances-to-elb-1

Select all the WWW Slave instances.  Again, normally we’d like to see an equal number of instances in at least two AZs for a production rollout.

14-add-instances-to-elb-2

After adding the instances to the ELB, they’ll initially be Out of Service while they get registered and get their intial health check by the ELB.

14-add-instances-to-elb-3

After a minute or two, all your instances should now have a status of In Service.

14-add-instances-to-elb-4

Remember that DNS record you created at the very start of the walkthrough?  Browse to that address and you will now get served by a fleet of web servers behind the ELB.  Not bad for a morning’s work.

Advertisements
Leave a comment

Leave your opinion

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: