logo
Tutorial

Deploy a Cloud Database to Scale a LAMP App

A key method for increasing application capacity is re-architecting monolithic applications and applying horizontal scaling. Managed cloud services simplify the process of breaking apart monoliths into component services. This tutorial demonstrates how to migrate and scale a monolithic application to distributed cloud architecture.

Published Aug 11, 2023

In previous tutorials we deployed a single application on a VPC. Applications like these are called monolithic applications because all the components are tightly coupled in a single server. Cloud architectures are frequently loosely coupled with application components connected via the network. This tutorial is an update of a Lightsail workshop.

Unlike the tutorial, and in keeping with conventions of the Practical Cloud Guide. we will complete an updated version of the workshop with the AWS CLI instead of the AWS Lightsail console. we will deploy a monolithic LAMP (Linux, Apache, MySQL, PHP) application and an external relational database, the replace the monolithic app’s database with the external database. In addition, we will scale the application by adding additional servers to a load balancer that distributes requests to the servers.

  • How to deploy a LAMP stack application as a monolith in a single Lightsail instance.
  • Re-architect the application by separating the application from the database.
  • Scaling and load balancing the LAMP stack.

Info
✅ AWS Level200 - Intermediate
⏱ Time to complete45 minutes
💰 Cost to completeFree when using the AWS Free Tier or USD 1.01
🧩 Prerequisites- An AWS account: If you don't have an account, follow the Setting Up Your AWS Environment tutorial for a quick overview. For a quick overview for creating account follow Create Your AWS Account.
- AWS credentials: Follow the instructions in Access Your Security Credentials to get your AWS credentials
-A Cloud9 environment for an individual.
- AWS CLI V2 installed.
- AWS Lightsail CLI plugin for Linux installed
- A git client: Follow the instructions to Install Git for your operating system.
💻 Code SampleCode sample used in tutorial on GitHub
📢 FeedbackAny feedback, issues, or just a 👍 / 👎 ?
⏰ Last Updated2023-08-11

In a monolithic application all the components are in a single VPS. In this example, the components include a MySQL database. a PHP application framework, and the application all hosted in a single Lightsail instance.

Monolithic application architecture

We will deploy the application on a AWS Lightsail Virtual Private Server (VPS) using the Lightsail command line client (CLI). The CLI provides a way to configure the server at launch with commands or a shell script. A script can install software, change file permissions, and set configuration parameters that a server requires for deploying an application.

This tutorial uses a script that does the following:

  • The Bitnami image has a default web page installed which needs to be removed. The script starts by changing into the root directory of the web server (/opt/bitnami/apache2/htdocs) and deleting the existing files
  • Next the script clones the application code to render the web front-end from the lab’s Github repo
  • To ensure that the PHP application can write to the settings file (connectvalues.php), the script changes the ownership (chown) on the file to match the account under which the Apache web server runs, as well as ensuring that account can write to the file (via chmod)
  • Each Bitnami-based instance generates a unique password for the locally installed MySQL database, this next command in the script opens the settings file and updates it with this password (which can be found at /home/bitnami/bitnami_application_password)
  • Finally the script issues a set of SQL commands to MySQL (via the MySQL command line tool) that will initialize the local database

Copy the script and save it to a file named launch.sh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

echo "removing default website"
cd /opt/bitnami/apache2/htdocs
rm -rf *

echo "cloning github repo"
git clone https://github.com/build-on-aws/sample-php-app .

echo "setting ownership on settings file"
sudo chown bitnami:daemon connectvalues.php
sudo chmod 666 connectvalues.php

echo "adding db password to settings file"
sed -i.bak "s/<password>/$(cat /home/bitnami/bitnami_application_password)/;" /opt/bitnami/apache2/htdocs/connectvalues.php

echo "creating tasks database"
cat /opt/bitnami/apache2/htdocs/data/init.sql | /opt/bitnami/mariadb/bin/mysql -u root -p$(cat /home/bitnami/bitnami_application_password)

To ease deployments, AWS Lightsail VPS are preconfigured with commonly used software called blueprints. Our application is deployed on a LAMP stack and we can use the the Lightsail CLI to find a LAMP stack blueprint. Use the get-blueprints command and filter the results using the Linux utility grep.

1
2
3
aws lightsail get-blueprints | grep lamp
"group": "lamp_8_bitnami",
"blueprintId": "lamp_8_bitnami",

We need to specify the size of the VPS, and like a blueprint, we can use the CLI to find an appropriately sized VPS. The specifications (such as the number of CPUs, memory size, and disk size) for a VPS are called bundles. We will scale application by adding additional servers or horizontal scaling. We can use a small VPS bundle that can be copied, or cloned, to scale the application horizontally.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
aws lightsail get-bundles
...
{
"supportedPlatforms": [
"LINUX_UNIX"
],
"name": "Small",
"power": 1000,
"price": 10.0,
"ramSizeInGb": 2.0,
"diskSizeInGb": 60,
"transferPerMonthInGb": 3072,
"cpuCount": 1,
"instanceType": "small",
"isActive": true,
"bundleId": "small_2_0"
}

With the lamp_8_bitnami blueprint deploy a small_2_0 VPS named PHP-fe-1 using the create-instances command. Note that the launch.sh script you created will be called by --user-data parameter. This deploys the application at launch.

1
2
3
4
5
6
aws lightsail create-instances \
--instance-names PHP-fe-1 \
--availability-zone us-west-2a \
--blueprint-id lamp_8_bitnami \
--user-data file://launch.sh \
--bundle-id small_2_0

It will take several minutes to instantiate the VPS. You can check the status of an instance with the get-instance-state command.

1
aws lightsail get-instance-state --instance-name PHP-fe-1

The CLI returns the state of the VPS in a JSON document.

1
2
3
4
5
6
{
"state": {
"code": 16,
"name": "running"
}
}

When the server is ready verify the connection between the PHP application and the locally-running MySQL database. To find the public IP of your lightsail instance check the card for your instance on the Lightsail console home page or use the Lightsail CLI command get-instance-access-details.

TIP: To find specific values in a JSON file install jq, a utility for parsing JSON. sudo yum install jq -y

1
aws lightsail get-instance-access-details --instance-name PHP-fe-1 | jq .accessDetails.ipAddress

Verify the connection between the PHP application and the locally-running MySQL database by opening a browser to http://<ipAddress>.

Verify application is deployed

In this section, we’ll deploy a Lightsail database, a managed database service that reduces the complexity of deploying and managing database software. Lightsail manages the underlying infrastructure and database engine while you create and deploy databases and tables running inside the service.

With the Lightsail CLI, create a MySQL 5.7 database (--relational-database-blueprint-id mysql_5_7). The point of this lab is to deploy a fault-tolerant and scalable implementation of the web application which requires a High Availability database plan, e.g., --relational-database-bundle-id micro_ha_2_0. Name the database todo-db (--relational-database-name todo-db).

By default Lightsail will create a strong password for you. However, for this tutorial, keep the password simple (--master-user-password taskstasks) and assign a user name (--master-username dbmasteruser)

1
2
3
4
5
6
7
aws lightsail create-relational-database \
--relational-database-name todo-db \
--relational-database-blueprint-id mysql_5_7 \
--relational-database-bundle-id micro_ha_2_0 \
--master-username dbmasteruser \
--master-user-password taskstasks \
--no-publicly-accessible

The next step is to replace the local MySQL instance running in the VPS with a high availability Amazon Relational Database Service (RDS) database. The application architecture will look like this:

Re-architecting the monolithic application

Update the application configuration to point to the highly-available Lightsail database. First, we will need the address of the high availability database. Use the get-relational-databases command to get the a JSON doc describing the instance and filter it with jq.

1
aws lightsail get-relational-databases | jq .relationalDatabases[].masterEndpoint.address

In the ToDo application click Settings from the top menu.

Change database endpoint

Paste the endpoint value of your Lightsail database under DB Hostname. Enter dbmasteruser for the DB Username, and taskstasks for the DB Password. Choose Save Settings.

Change database connection setting

Test the new database by clicking List Tasks in the top menu, there shouldn’t be any tasks to display. Also note at the bottom of the screen it should list your Lightsail database endpoint as the value for Database host

If your web app is still showing the previously deployed database (denoted by localhost as the database host), you may need to use either a new browser window or an incognito window.

Snapshots are point-in-time copies of instances. Lightsail simplifies creating snapshots of instances that can be used to backup and restore instances, scale instance sizes up or down, and/or to deploy a new instance. Create a snapshot of the VPS with the create-instance-snapshot command.

1
2
3
aws lightsail create-instance-snapshot \
--instance-snapshot-name PHP-fe-ls-db \
--instance-name PHP-fe-1

The status will change to Snapshotting, we will need to wait for the process to complete before moving forward. This can take up to 5 minutes. You can check the status of a snapshot with this command:

1
aws lightsail get-instance-snapshot --instance-snapshot-name PHP-fe-ls-db | jq .instanceSnapshot.state

When the snapshot is complete, create two new instances with create-instances-from-snapshot. Note that you can use a list of instance names to create multiple instances.

1
2
3
4
5
aws lightsail create-instances-from-snapshot \
--instance-snapshot-name PHP-fe-ls-db \
--instance-names {PHP-fe-2,PHP-fe-3} \
--availability-zone us-west-2a \
--bundle-id small_2_0

Get the public IP of the two newly created front end instances with these commands.

1
2
3
aws lightsail get-instance --instance-name PHP-fe-2 | jq .instance.publicIpAddress

aws lightsail get-instance --instance-name PHP-fe-3 | jq .instance.publicIpAddress

Open a browser window and enter the IP address of the one of the new instances, then repeat it for the other instance. Notice that the hostname for that particular web front end instance is listed under your task list, and that it changes based on which instance you are visiting in the web browser.

The next step is to create a load balancer to provide scalability and fault tolerance. Set —instance-port to 80 and name the load balancer todo-lb.

1
2
3
aws lightsail create-load-balancer \
--instance-port 80 \
--load-balancer-name todo-lb

You can check the status of the load balancer. This command will return active when the load balancer is running.

1
aws lightsail get-load-balancer --load-balancer-name todo-lb | jq .loadBalancer.state

With the load balancer ready, attach the three instances.

1
2
3
aws lightsail attach-instances-to-load-balancer \
--load-balancer-name todo-lb \
--instance-names {"PHP-fe-1","PHP-fe-2","PHP-fe-3"}

The Lightsail load balancer performs a health check on each instance by sending a request to the root of the web application on each instance. If the web application returns an HTTP Status 200, the instance passes the health check. It can take some time for each instance to pass the health check, and you can verify the status of the instances with the get-load-balancer command.

1
aws lightsail get-load-balancer --load-balancer-name todo-lb | jq .loadBalancer.instanceHealthSummary

When all instances are "healthy" you've scaled your application. Congratulations! The new application architecture looks like this.

Scaled application architecture

You can find the address of the load balancer with get-load-balancer and filter the response to dnsName.

1
aws lightsail get-load-balancer —-load-balancer-name todo-lb | jq .loadBalancer.dnsName

Open a browser to http://< dnsName > to verify that application is working. Refresh the browser window and you will see the Front-end host ip change.

To prevent additional costs, delete the VPS instances, the snapshot, and the MySQL instance.

1
2
3
4
5
6
aws lightsail delete-instance --instance-name PHP-fe-1
aws lightsail delete-instance --instance-name PHP-fe-2
aws lightsail delete-instance --instance-name PHP-fe-3
aws lightsail delete-relational-database --relational-database-name todo-db
aws lightsail delete-instance-snapshot --instance-snapshot-name PHP-fe-ls-db
aws lightsail delete-load-balancer --load-balancer-name todo-lb

We’ve taken an application deployed on a single VPS and scaled it by replacing the local database with a high availability database, creating copies of the VPS, and attaching them to a load balancer that distributes the requests among the VPS. The import takeaways are;

  • Configure a VPS with a launch script.
  • Replace a local database with high availability instance.
  • Create a snapshot to clone a VPS.
  • Scaling an application with a load balancer.

You are well on your way to becoming a cloud engineer.

The next section is a departure from deploying applications and databases. You will learn about creating accounts and managing user roles and permissions. Modern enterprises commonly have several environments such as dev, test, and production. Managing multiple environments for an enterprise is a core requirement for a cloud engineer.