Amazon-Web-Services

First, let me start by saying that the Elastic Load Balancer is a pretty neat feature of Amazon Web Services.  Straight from the Amazon ELB page:

Elastic Load Balancing automatically distributes incoming application traffic across multiple Amazon EC2 instances in the cloud. It enables you to achieve greater levels of fault tolerance in your applications, seamlessly providing the required amount of load balancing capacity needed to distribute application traffic.

The end goal of AWS, or any cloud implementation, is stateless computing.  We don’t care where, when, or how (to a degree, of course) our application is running.  ELB helps to complete that goal by constantly checking on the instances and assigning them network load depending on a host of characteristics.  It can check to see server load, see if the instance is actually healthy and accepting traffic, or any number of other custom things.

However, sometimes you don’t need the complexity (or expense) of an ELB, but still want some simple load balancing.  That’s what this post is about.  There are some prerequisites to all this.  You’ll need:

– A properly setup AMI that can be used for all instances in the load balancing group

– AWS tools installed and configured in the AMI (https://github.com/boto/boto)

– An IAM role setup allowing this EC2 instance group access to DNS (Route 53)

– Auto scaling configured and enabled

– A functioning Route 53 hosted zone

The idea here is that auto-scaling has determined that some metric has increased.  We’ll assume it’s load, as that’s an easy one.  The auto-scaling policy launches the AMI as directed.  Now we need to get this instance into DNS so it can start taking some of the load off the instance group.  This example uses Amazon Linux, which means it should work for just about any RHEL clone distro.

We need an init script to run as the last thing (at least after network is up).  That script (/etc/init.d/aws) is as follows:

#!/bin/bash

# chkconfig: 35 99 10
# description: Add/remove DNS entries from Route53
#

. /etc/init.d/functions
lockfile=/var/lock/subsys/aws

case "$1" in
  start)
    /home/ec2-user/scripts/startup.sh
    touch $lockfile
  ;;
  stop)
    /home/ec2-user/scripts/shutdown.sh
    rm -f $lockfile
  ;;
  restart)
    $0 stop
    $0 start
  ;;
  *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 1
  ;;
esac

exit 0

A couple of things to notice here. The script is set to be the LAST thing to run at startup (denoted by the 99 in the third line) and the FIRST thing to run at shutdown (denoted by the 10 in the third line). This ensures that the instance is ready to receive traffic at start and that it stops receiving traffic as soon as a shutdown begins. Also notice that the script redirects to other scripts in the ec2-user home folder. To be honest, I can’t remember why I did that, but there was a reason for it.

So now let’s follow the scripts down the path. Here is the contents of /home/ec2-user/scripts/startup.sh:

#!/bin/sh
# This script called by /etc/init.d/aws start

# Get instance info
ZONEID="xxxxxxxxxxxxxxxxxx"
PUBIP=`curl -s --fail http://169.254.169.254/latest/meta-data/public-ipv4`
INSTANCEID=`curl -s --fail http://169.254.169.254/latest/meta-data/instance-id`

# Create DNS record
/usr/bin/route53 add_record $ZONEID stegsolutions.com A $PUBIP 60 $INSTANCEID 10

Here we use the default AWS metadata to get the public IP of the server and its id. We need the instance ID because when adding the record, we need a unique identifier. Since we also need that same unique identifier when shutting down, it makes the most sense to use the already unique instance id. Obviously, be sure to use the proper zone id and not “xxxxxxxxxxxx”.

Next here is the shutdown script, /home/ec2-user/scripts/shutdown.sh:

#!/bin/sh
# This script called by /etc/init.d/route53 stop

# Get instance info
ZONEID="xxxxxxxxxxxxx" 
PUBIP=`curl -s --fail http://169.254.169.254/latest/meta-data/public-ipv4`
INSTANCEID=`curl -s --fail http://169.254.169.254/latest/meta-data/instance-id`

# Delete DNS record
/usr/bin/route53 del_record $ZONEID stegsolutions.com A $PUBIP 60 $INSTANCEID 10

# Wait before continuing to reduce possibility of outage
sleep 75

This script just removes everything startup.sh added. It then does one final thing. In the startup script, we used a TTL of 60 seconds. That means clients will cache the DNS record for a minute. Even though its been removed from DNS, the instance must stay up for at least another 60 seconds to service requests from clients who have the IP still fresh in their cache. So the script sleeps for 75 seconds, and then proceeds to shutdown.

And those are the basics of how to do some simple load balancing of AWS instances without having to resort to an ELB.

This post originally appeared on stegsolutions.com. Need help with your virtualization project? Let us help. Contact zac@stegsolutions.com for more information.