Part 2: Deploying WordPress in an AWS Three-Tier Web Architecture – Implementation

Implementation

The purpose of this section is not to explain step-by-step how to deploy WordPress in an AWS Three-Tier Web Architecture, but rather to talk about my experience deploying WordPress in this architecture.
I attempt to go more in-depth into the steps followed in the process and share the challenges I encountered, deviations, options, and reasoning behind some of the decisions made.
With some understanding of how to work around the AWS console, anyone could build this project without the need for a step-by-step guide by following the steps listed below.

There are multiple resources on the web that show this process in detail.
This project was based on the project provided by Azeez Salu on[i] AOS Note which I highly recommend.
Other sources for deploying WordPress with different architectures can be found at:

Steps to build a three-tier architecture in AWS and install WordPress:

1. Select the desired Region.  I selected N. Virginia since it is the closest to my location.

2. Create a VPC

a. Name VPC
b. Enter the CIDR block

The CIDR blocks utilized here and in the subnets were based on the diagram displayed at the beginning of this article. For example, for the VPC I used the CIDR block 10.0.0.0/16, these are roughly 64,000 IP addresses[iv] since AWS does reserve a small number of addresses from the CIDR block.

 

 

 

c. Click on Create VPC

All other settings are left as default.

3. Enable DNS Hostname in the VPC in the VPC Actions.

This is done so that the VPC supports assigning public DNS hostnames to instances with public IP addresses[v].

4. Create an Internet Gateway (IG)

To enable access to or from the Internet for instances in a subnet in a VPC

a. Name IG
b. Click on Create IG

5. Attach the IG to the VPC

Only a single IG can be attached to a VPC

6. Create the Public Subnets (2x)

These are public because they have access to the Public Route Table, which has a route to the Internet Gateway, and therefore the Internet.

a. Select VPC
b. Name Subnet
c. Select Availability Zone (AZ)
d. Enter CIDR block
The CIDR blocks used here were:
10.0.0.0/24 on us-east-1a
10.0.1.0/24 on us-east-1b
e. Click on Create subnet

7. Enable IP auto-assign for the two Public Subnets

Any time you launch EC2 instances in this subnet, they will be assigned a public IPv4 address.

8. Create the public Route Table

a. Name Route Table
b. Select VPC
c. Click on Create route table

9. Add the public routes to the Route Table

To allow the route table to route traffic to the Internet.
CIDR is 0.0.0.0/0 with Target being the Internet Gateway

10. Associate (Explicit subnet associations) the two Public Subnets to the Public Route Table

11. Create the Private Subnets (4x)

These only have access to the Private Route Table, and not to the Public Route Table

a. Select VPC
b. Name Subnet
c. Select Availability Zone (AZ)
d. Enter CIDR block
The CIDR blocks used here were:
10.0.2.0/24 on us-east-1a for the App subnet
10.0.3.0/24 on us-east-1b for the App subnet
10.0.4.0/24 on us-east-1a for the Data subnet
10.0.5.0/24 on us-east-1b for the Data subnet

e. Click on Create subnet

12. Create NAT Gateway (2x)

A NAT gateway is a Network Address Translation (NAT) service. You can use a NAT gateway so that instances in a private subnet can connect to services outside your VPC but external services cannot initiate a connection with those instances[vi].

One for each AZ, to route traffic to the Internet from each AZ’s Private Table, and therefore Private Subnets.

a. Name NAT Gateway
b. Select a Public Subnet – A different AZ in each iteration.
c. Connectivity type – Public
d. Elastic IP allocation ID – Allocate Elastic IP

An Elastic IP address is a static and public IPv4 address, which is reachable from the internet[vii].  When you create a public NAT gateway in a public subnet, you must associate an elastic IP address with the NAT gateway at creation[viii].

e. Click on Create NAT gateway

13. Create a Private Route Table (2x)

One for each AZ
a. Name route table
b. Select VPC
c. Click on Create Route Table

14. Add routes to Private Route Table

CIDR is 0.0.0.0/0 with target NAT Gateway

15. Associate (Explicit subnet associations) the two Private Subnets to the Private Route Table corresponding to the desired AZ.  In our case the Private App Subnet AZ1 and Private Data Subnet AZ1, for the Private Route Table AZ1, and the same for AZ2.

16. Create Security Groups (SG) (5x)

a. Name SG
b. Provide a description of SG
c. Select VPC
d. Click on Create Inbound Rules

Five security groups need to be created for this architecture as follows:

ALB Security Group
Port: 80 & 443  / Source: 0.0.0.0/0
Allows HTTP and HTTPS connection from Anywhere.

SSH Security Group
Port: 22  / Source: Your IP Address
Allows you to start an SSH session to connect into a resource.

Webserver Security Group
Port: 80 & 443  / Source: ALB Security Group
Allows HTTP and HTTPS connection from the Application Load Balancer (ALB).

Database Security Group
Port: 3306  / Source: Webserver Security Group
Allows MySQL protocol (default port 3306) connection from the Webservers.

EFS Security Group
Port: 2049  / Source: Webserver Security Group and Elastic File System (EFS) Security Group
Allows Network File System (NFS – port 2049) connection from the Webservers.
Port: 22  / Source: SSH Security Group
Allows you to start an SSH session to connect to the EFS.

 

17. Create the Database (DB) Subnet group

a. Name subnet group
b. Provide a description to the subnet group
c. Select VPC
d. Add AZ
e. Select DB subnet from AZ
f. Click on Create

18. Create the Database (DB) server (1x) in one of the AZ

a. Create RDS Database

i. Create MySQL DB
ii. Select desired instance type and include the correct VPC, security group, and desired AZ
iii. Add DB name

For this project it was built as a db.t2.micro instance type with 20GB of storage.

19. Create an Elastic File System (EFS)

To allow the Webservers on both AZs to access WP files in this shared file system.

a. Create EFS
b. Include the correct VPC, desired AZ, and mount target to either the App or DB private subnet with the EFS security group

20. Create a KeyPair

KeyPairs serve as our security credential to access the EC2 instances.

a. Name KeyPair
b. Key pair type – RSA
c. Select the desired Private key file format, depending on the method used to SSH into the EC2 instances
d. Click on Create key pair

21. Create an EC2 instance to set up WordPress

a. Name EC2
b. Selected desired instance type
c. Select Keypair
d. Select VCP
e. Select Public subnet
f. Assign the SSH SG, ALB SG, Web server security groups
g. Click on Launch Instance

22. Access the setup server (SSH into EC2 instance)

23. Installing WordPress from within the setup server

a. Create the html directory
b. Mount the EFS to the html directory
c. Install Apache
d. Install PHP
e. Install MySQL
f. Set permissions for the ec2-user in the web server directory
g. Download the WordPress files
h. Create and edit the wp-config.php file to add the database information

      • DB_NAME — DB Name
      • DB_USER — DB username
      • DB_PASSWORD — DB password
      • DB_HOST — DB hostname (DB Endpoint)

i. Restart the setup server

Additional details can be found directly in the AWS documentation[ix]: Host a WordPress blog

 

WP database parameters in the wp-config.php file.

 

 

 

 

 

 

 

 

 

WordPress now accessible thru the setup server’s public IP address
Setup server public IP address.

 

 

 

 

 

WP accessible thru the setup server’s public IP address
WP accessible thru the setup server’s public IP address.

 

24. Create the Web servers in the Private Subnet (2x)

One EC2 in each AZ

a. Name EC2
b. Select desired instance type and storage
c. Select VPC
d. Select the Private subnet
e. In the User Data add the commands to install Apache, PHP and MySQL, and mount the EFS when the server boots for the first time to run WordPress in the Web server
f. Select the Web server security group
g. Select Keypair

25. Create a Target Group (TG)

Target group is a logical grouping of EC2 instances that sits behind a load balancer where traffic is forward to, based on protocols and ports defined in a listener rule[x].

a. Instances TG
b. Name TG
c. Select VPC
d. Under Advanced health check settings
Success codes – 200, 301, 302 (added 301, 302 which are domain redirect status)
e. Register targets – Add both Web servers
f. Click on Include as pending below
g. Click on Create target group

26. Create an Application Load Balancer (ALB)

Traffic from the Internet will reach the ALB, which will forward the traffic to the Webservers in the Private Subnet.  Traffic from the Internet cannot reach the Private Subnet directly.

a. Name ALB
b. Scheme – Internet-facing
c. Select VPC
d. Under mapping select both AZs and their Public Subnets.

The ALB will route traffic only to the targets in these AZs.

e. Select the ALB SG
f. Listeners and routing – Select desired Target group, for now only in HTTP:80.  HTTPS:443 will be set up later.
g. Click on Create load balancer

 

WordPress now accessible thru the ALB DNS name
ALB DNS name.

 

 

 

WP accessible thru the ALB DNS name.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

27. Update WordPress URL and Site Address URL with the ALB DNS name

The WordPress address (site URL) covers all relevant WordPress files such as theme, plugins, and media files. Under this URL you can also reach the admin area of your WordPress installation.

The website URL (home URL) is the address where your website can be reached externally[xi].

 

 

 

 

 

 

 

 

 

 

 

28. Terminate Setup Server, since is no longer needed.

29. Have a domain name available or register a new domain using AWS Route 53.

For this project I already owned a domain name, managed by a third party (Go Daddy).

30. Create a record set in Route 53

In Route 53
a. Create Hosted Zone
b. In Hosted Zones

i. Create record – Add record name and record type A
ii. Turn Alias on
iii. Route traffic to the ALB

 

WordPress now accessible thru the domain name

 

WP accessible thru the domain name.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

31. Update WordPress URL and Site Address URL with the domain name

 

 

 

 

 

 

Up until now, we have been able to access WordPress using the HTTP protocol. To transfer data between the web server and the web browser securely we need to use the HTTPS protocol.  To access WordPress using the HTTPS protocol we need to do the following steps.

32. Create Encryption in Transit (HTTPS)

a. Certificate Manager

i. Request a Public Certificate
ii. Domain names
Add the root domain domainname.com, and any subdomain such as *.domainname.com, in additional lines
iii. Validation method – DNS validation
iv. Request

b. Validate ownership of the domain

i. In the certificate go to Create records in Route 53
ii. Select all the domains
iii. Click on Create Records

AWS makes it very easy to validate the ownership of the domain when the domain name is managed by Route 53.
Since I was using a domain name managed by GoDaddy.com, I had to make some updates in the GoDaddy domain management console.  I also had to validate my domain ownership through DNS validation.
AWS offers clear instructions on what changes need to be made when owning a domain managed by GoDaddy, and instructions on validating ownership through email validation and DNS validation.

The instructions I followed on this step can be found here:

33. Create the HTTPS Listener in the ALB

a. Under Load Balancers (EC2 section)

i. Select the ALB and go to the Listeners tab (only HTTP:80 is showing)
ii. Add Listener

1. Select Protocol HTTPS
2. Default actions Forward
3. Select the Target group
4. Under Secure listener settings – Default SSL/TLS certificate

From ACM – Select the desired certificate

b. Then edit the HTTP listener to redirect traffic to the HTTPS listener

i. Remove existing Forward under Default actions
ii. Under Default actions now select Redirect

HTTPS:443

iii. Save changes

Now our ALB is redirecting any HTTP traffic to HTTPS.

 

34. Create Bastion Hosts (Jump Box/Jump Server) to SSH to Private Subnet

Create EC2 in the Public Subnet with SSH SG

a. Name EC2
b. Selected desired instance type
c. Select Key pair
d. Select VCP
e. Select Public subnet
f. Select SSH SG
g. Click on Launch Instance

 

35. SSH to the Bastion Host, and access one of the web servers in the private app subnet.

36. Modify the WordPress wp-config.php

At the beginning of the document add a command seen in the image below.
This command is used to indicate that SSL should be enforced.
The purpose of this code is to handle situations where a website is served through a load balancer that terminates the SSL/TLS (HTTPS) connection, as is in our case.

HTTPS code required in the wp-config.php file.

 

 

 

 

 

37. Modify the URL inside WP with the HTTPS URL

 

WordPress now accessible thru the domain name using HTTPS

 

WP accessible thru the domain name using HTTPS.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Our WordPress application is currently running in a three-tier architecture, split into two separate Availability Zones for increased availability.  It is also accessible through the desired domain name using HTTPS protocol.  This project can be considered successful at this point and marked as completed.

 

We can create an autoscaling group to create a more robust and scalable environment.

 

38. Create Launch Template (LT) (EC2 section)

a. Name LT
b. Add a description
c. Select Auto Scaling guidance
d. Select the desired instance type
e. Select Key pair
f. Select SG – Webserver SG since the horizontal scaling will be done to the web servers
g. Advance details – Add User data
Including all required packages and EFS attachment commands. Same as the user data used when the web servers were created.
h. Create launch template

39. Create autoscaling group (ASG)

Create autoscaling group (EC2 section)
a. Name ASG
b. Select Launch Template (click next)
c. Select VPC
d. Select AZ – Select both Private App Subnets (click next)
e. Load balancing – Attach to an existing load balancer
f. Choose from your load balancer target groups and select the existing ALB
g. Health checks – add ELB health check (recommended)
h. Enable cloud watch metrics (recommended) (click next)
i. Group size – Assign the desired capacity (click next)

For this project the following configuration was used.
Desired capacity – 2
Minimum capacity – 1
Maximum capacity – 4

j. Add notifications
Here a SNS Topic can be created if desired. (click next)
k. Add tags
Add tag if desired
l. Click on Create auto scaling group

After terminating the original Web servers the Auto Scaling Group successfully created the desired capacity of 2 EC2 instances.

 

Activity history showing the creation of two new EC2 instances thru the ASG.

 

 

 

 

 

 

 

 

 

Console view showing the two initial WebServers terminated and the two new ASG-Webservers.

 

 

 

 

 

 

 

By having this auto scaling group in place in more than one AZ we make sure that our system will be always available and running at the desired capacity.

 

Conclusion

As mentioned in Part 1, the purpose of building the three-tier architecture was to create a solution that provided increased security, availability, performance, and scalability.  With this architecture and configuration, we have accomplished our goal.  This is a robust and practical architecture for most web applications and not just WordPress.

We have achieved our objectives through:

Security:

    • Restrict access to the private subnets to only the bastion host and ALB.
    • Use separate subnets for the web servers, database server, and bastion host.
    • Use separate security groups with the principle of least privilege required for each service (ALB, EC2, EFS, etc.)
    • Implement the HTTPS protocol in our ALB.

Availability, Performance, and Scalability:

    • Duplicate services in two separate Availability Zones.
    • Application Load Balancer (ALB) to distribute traffic where there is a healthy web server.
    • Implementation of an auto-scaling group to replace any unhealthy web server or increase capacity when needed.

 

 

[i] AOS Note.  Retrieved July 7, 2023, from https://www.aosnote.com/

[ii] Tutorial: Deploy WordPress to an Amazon EC2 instance (Amazon Linux or Red Hat Enterprise Linux and Linux, macOS, or Unix).  Retrieved July 7, 2023, from https://docs.aws.amazon.com/codedeploy/latest/userguide/tutorials-wordpress.html

[iii] How to Install WordPress on AWS: Detailed, Step-by-Step Guide.  Retrieved July 7, 2023, from https://themeisle.com/blog/install-wordpress-on-aws/

[iv] Understanding IP Addressing and CIDR Charts.  Retrieved July 8, 2023, from https://www.ripe.net/about-us/press-centre/understanding-ip-addressing

[v] DNS attributes for your VPC.  Retrieved July 8, 2023, from https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html

[vi] NAT gateways.  Retrieved July 8, 2023, from https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html

[vii] Elastic IP addresses.  Retrieved July 8, 2023, from https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html

[viii] NAT gateways.  Retrieved July 17, 2023, from

[ix] Host a WordPress blog.  Retrieved July 8, 2023, from https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/tuts-wordpress.html

[x] Target Group.  Retrieved July 9, 2023, from https://www.lightlytics.com/resources/target-group

[xi] Change the WordPress URL: Three methods to make it happen.  Retrieved July 9, 2023, from https://www.ionos.com/digitalguide/hosting/blogs/wordpress-url-change/

[xii] Making Amazon Route 53 the DNS service for an existing domain.  Retrieved July 9, 2023, from https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/MigratingDNS.html

[xiii] Add a custom domain managed by GoDaddy. Retrieved July 9, 2023, from https://docs.aws.amazon.com/amplify/latest/userguide/to-add-a-custom-domain-managed-by-godaddy.html

[xiv] Validating domain ownership.  Retrieved July 9, 2023, from https://docs.aws.amazon.com/acm/latest/userguide/domain-ownership-validation.html