This guide will walk you though the details of deploying a plain-vanilla Linux, Apache, MySQL, PHP (LAMP) application in the Amazon Web Services (AWS) cloud. If you've never used the cloud before, or don't understand how some of the various components relate to each other, then this article is a great place to start. The cloud components we'll be using throughout this guide are Amazon's Elastic Compute Cloud (EC2), Simple Storage Service (S3), Auto Scaling, Elastic Load Balancing, and Relational Database Service.
For the purposes of this guide, we're going to assume you're doing this on Ubuntu Linux. If you're using something else, you'll need to make a few adjustments here and there for your own specific operating system.
First things first, you'll need an Amazon AWS account. Go to http://aws.amazon.com and click on the Sign Up Now button:

During the signup process you'll be provided an X.509 certificate key pair; you'll want to save both files (a cert-*.pem and a pk-*.pem file) to your computer. Don't lose these, as you will need both of them to connect to the cloud.
You'll also need to separately sign up for Amazon Relational Database Services (RDS).
Once you've signed up for EC2, S3, and RDS, and put in all your billing information, you'll want to point to the Your Account menu and choose the Security Credentials link:

Scroll down to the Access Credentials section, and write down the Access Key ID that is shown. Click on the Show link under Secret Access Key, and write that down as well:

Scroll to the bottom of the page, to Account Identifiers, and write down your AWS Account ID:

Last but not least, go to the AWS web console, click the Key Pairs link on the left, and then click the Create Key Pair button:

Save the RSA private key it offers you to your computer. Don't lose this, as this is how you will SSH into your servers within the cloud. You should also note the name of the key pair, as we'll need this later. Call it something like gsg-keypair.
You should now have six pieces of information:
- Your RSA private key
- The name of your RSA key pair
- Your X.509 certificate key pair, consisting of two different .pem files
- Your Access Key ID, which will look something like this: 06XKS8FQAE117S820PYW
- Your Secret Access Key, which will look something like this: Ly9qW5LIvQwBQzwl/3TrPmo1A9fGOjI9+8UKUviH
- Your AWS Account ID, which will look something like this: 1234-5678-9012
You will need all of the above for the next steps in this article.
If you don't have it already, you should now install Java on your local system. This is because all of the command-line tools for managing cloud services are written in Java. We have to use the command-line tools because a lot of Amazon's cloud functionality is inaccessible via their web interface, and can only be managed via the command-line.
You can use either OpenJDK or Sun Java.
On Ubuntu Linux, OpenJDK is installed to /usr/lib/jvm/java-6-openjdk. Edit your ~/.bashrc file and add the following line to it:
export JAVA_HOME=/usr/lib/jvm/java-6-openjdk
Make sure Java is working by sourcing your .bashrc file and then checking Java's version:
source ~/.bashrc $JAVA_HOME/bin/java -version
Next, in your home directory, create a folder named .ec2:
mkdir ~/.ec2
Put a copy of your two .pem files within that folder, and also put a copy of your RSA private key in that same folder. Set the permissions on them to keep them secure:
chmod 0700 ~/.ec2 chmod 0400 ~/.ec2/*
Now edit your ~/.bashrc file again, and add the following two lines to it:
export EC2_PRIVATE_KEY=~/.ec2/pk-STRING.pem export EC2_CERT=~/.ec2/cert-STRING.pem
Replace STRING in the above example with the name of your .pem files.
We now have all the information we need to begin configuring and managing cloud services. However, the last thing we need to do before we begin is download the command-line tools from Amazon. Download the Zip archive that each of the following pages link to, and save them all to your computer:
- Amazon EC2 API Tools
- Elastic Load Balancing API Tools
- Auto Scaling API Tools
- Amazon RDS Command Line Toolkit
- S3cmd (this is open source software provided by a third party)
On Linux, create the /usr/local/aws directory on your system:
sudo mkdir -p /usr/local/aws
cd to the /usr/local/aws directory and unzip and untar all of the files you previously downloaded. When you've done that, you should have a collection of directories that look something like this:
AutoScaling-1.0.9.0 ec2-api-tools-1.3-46266 ElasticLoadBalancing-1.0.3.4 RDSCli-1.0.005 s3cmd-0.9.9.91
Rename all the directories to something a bit more sane, for example:
sudo mv AutoScaling-1.0.9.0 as sudo mv ec2-api-tools-1.3-46266 ec2 sudo mv ElasticLoadBalancing-1.0.3.4 elb sudo mv RDSCli-1.0.005 rds sudo mv s3cmd-0.9.9.91 s3cmd
Now edit your ~/.bashrc file again, and add the following lines to it:
export EC2_HOME=/usr/local/aws/ec2 export AWS_ELB_HOME=/usr/local/aws/elb export AWS_AUTO_SCALING_HOME=/usr/local/aws/as export AWS_RDS_HOME=/usr/local/aws/rds
Last but not least, update the PATH on your system by adding this line to your ~/.bashrc file:
export PATH=$PATH:$EC2_HOME/bin:$AWS_ELB_HOME/bin:$AWS_AUTO_SCALING_HOME/bin:$AWS_RDS_HOME/bin:/usr/local/aws/s3cmd
Source your ~/.bashrc file, and at last, we are ready to begin!
As you may already know, the typical web application is built around a three-layer approach: a web (or presentation) layer, which communicates with the application (or business logic) layer, which communicates with the database (or storage) layer. Many modern web applications collapse the presentation and application layers together, as will we for the purposes of this guide.
We are going to build our test application from the ground up, or more literally, from the database up. We will begin by setting up our persistent SQL storage; the setup the web layer on top of it; then put a firewall in front of the web layer; put a load balancer in front of the web layer; and finally, set up an auto scaling configuration that will dynamically grow and shrink the number of web servers (instances) we have within our load balanced pool depending upon load.
A lot of people setup one or more instances (virtual EC2 servers) and put MySQL (or some other database) on them. This is the wrong way to do it. The correct way is to use Amazon's Relational Database Service, or RDS. RDS behaves for all intensive purposes just like MySQL, but doesn't require that you actually setup and administer your own MySQL servers.
Before we create our first RDS instance, we should create a security group to contain it. Within AWS, security groups are kind of like firewalls: you can specify what IP addresses can access what ports of services running inside the group. You can also allow one security group to access another. Given the highly-public nature of the cloud, it's always a good idea to use security groups to tightly restrict who and what can access your servers and services.
So in that vein, let's create a security group that will house our RDS instance. You create the group by running:
rds-create-db-security-group TestDBSecurityGroup --db-security-group-description "Test security group"
Make sure it's there by running:
rds-describe-db-security-groups
At this point, there are no permissions associated with the group; the default is to deny all traffic.
Now create an RDS instance by running:
rds-create-db-instance --db-instance-identifier TestInstance --allocated-storage 8 --db-instance-class db.m1.small --engine MySQL5.1 --master-username test --master-user-password password12345 --db-name TestDatabase --db-security-groups TestDBSecurityGroup
This will start creating a database instance with 8GB of storage. You can check on the creation status by running:
rds-describe-db-instances
Once the instance has been created, be sure to note the hostname (you might have to wait a few minutes for the instance to finish being created.) This is how you will connect to the database from your web servers. The hostname will look something like this: testinstance.cw25zy30u7xw.us-east-1.rds.amazonaws.com.
Now that we have a database instance running, our infrastructure so far looks like this:

Let's setup our first web server (EC2 instance.)
EC2 server instances are built from images, or AMIs. An AMI is basically a snapshot of an installed operating system, complete with whatever applications and configurations were present at the time the snapshot was made. Amazon has a lot of AMIs to choose from; to see a list of all the available AMIs:
ec2-describe-images -o amazon | grep "machine"
Let's narrow the list down a bit and just see the AMIs that were built using Fedora Linux with Apache:
ec2-describe-images -o amazon | grep "machine" | grep "fedora.*apache"
Each AMI is described by an XML file; for the purposes of this article, we'll use the ec2-public-images/fedora-core4-apache-mysql.manifest.xml AMI. You'll need to make a note of the AMI ID number which is shown by the above command. It will look something like this: ami-25b6534c.
Let's start our first instance. We will first need to create a security group to contain our instances. Note that this is a different security group than the one that holds our database instance.
ec2-add-group TestServerSecurityGroup -d "Test server security group"
Now start an instance using the AMI ID you chose, above, within your new security group:
ec2-run-instances ami-25b6534c -k gsg-keypair -g TestServerSecurityGroup
For the -k parameter, use the name of your RSA key pair. This must correspond to the RSA private key you earlier saved within the ~/.ec2 directory. You'll see why in a moment.
Wait a few minutes for the instance to start, and monitor its status by running:
ec2-describe-instances
Once that command reports that the instance is running, note its public hostname (it should look something like ec2-174-129-154-209.compute-1.amazonaws.com) and try to SSH into the instance via:
ssh -i ~/.ec2/gsg-keypair root@ec2-174-129-154-209.compute-1.amazonaws.com
Doesn't work, does it? This is because our security policy is not permitting access to TCP port 22. Let's now permit ourselves access to the EC2 instance:
ec2-authorize TestServerSecurityGroup -P tcp -p 22
This will allow any IP address to connect to TCP port 22 on our instance. Since the SSH login is secured via an RSA key, and not a password, this is reasonably safe. Let's try to SSH into the instance again:
ssh -i ~/.ec2/gsg-keypair root@ec2-174-129-154-209.compute-1.amazonaws.com
Look at that, it works!
Let's see if Apache is running on our instance (you should run the following commands via SSH within the instance, not on your local system):
ps -ef | grep http
But wait, is MySQL also running?
ps -ef | grep mysql
That's no good. We're going to be using our RDS instance, not MySQL. Let's shut it down and configure it to not start on boot:
chkconfig mysqld off service mysqld stop
In order to effectively test that everything is working, let's put a very simple PHP page on our web server that will connect to our database and report the number of rows it found in a table named test_table. Create the file /var/www/html/index.php with the following content:
<? $server="testinstance.cw25zy30u7xw.us-east-1.rds.amazonaws.com"; $username="test"; $password="password12345"; $database="TestDatabase"; mysql_connect($server, $username, $password); @mysql_select_db($database) or die("Unable to select database!"); $result=mysql_query("SELECT * FROM test_table"); echo "<p>Got " . mysql_numrows($result) . " row(s) back from the database."; mysql_close(); ?>
Of course, adjust the server name as needed to match the hostname of your own database instance.
We now need to configure the database security group to permit the web server security group to access the database instance. We'll do that by running the following on our own local system:
rds-authorize-db-security-group-ingress TestDBSecurityGroup --ec2-security-group-name TestServerSecurityGroup --ec2-security-group-owner-id 1234-5678-9012
This will now permit our EC2 instance to communicate with our RDS instance. Let's verify that this is the case by returning to our EC2 instance and using the MySQL monitor to connect to the RDS instance. First, SSH into the EC2 instance:
ssh -i ~/.ec2/gsg-keypair root@ec2-174-129-154-209.compute-1.amazonaws.com
From within the EC2 instance, run:
mysql -h"testinstance.cw25zy30u7xw.us-east-1.rds.amazonaws.com" -u"test" -p"password12345" TestDatabase
It should connect just fine. Let's create a table (which we'll need later), put some test data into it, and then disconnect from the database:
CREATE TABLE `test_table` (`id` INT AUTO_INCREMENT, `name` VARCHAR(100), PRIMARY KEY(id)); INSERT INTO test_table (name) VALUES ("John Smith"),("Jane Smith"); QUIT
Back on our local system, we now need to permit access to TCP port 80 on our EC2 instance so that we can test out the PHP page we created:
ec2-authorize TestServerSecurityGroup -P tcp -p 80
Now everyone can get to both SSH (port 22) and Apache (port 80) on our EC2 instance. Fire up your favorite web browser, and go to http://ec2-174-129-154-209.compute-1.amazonaws.com/ in it.
It should display a web page that reads:
Got 2 row(s) back from the database.
Excellent! This means that we are communicating with our EC2 instance, which in turn is communicating with our RDS instance. Go ahead, if you'd like, and add more rows to the database table, and refresh the page. It should show the increased row count.
At this point we now have the following cloud infrastructure setup:

It works, although it's not terribly useful yet. For example, if the EC2 instance were to be destroyed, all of our customizations would be permanently lost. Also, should the instance reboot or crash for any reason, our site will be down. In the next article on the cloud we will solve both of these challenges: we'll create our own AMI that includes our customizations, and we'll setup a second EC2 instance and load balance incoming traffic between the two.
