Introduction

AWS is the largest public cloud provider, with the biggest number of services. One of the most important services is the Virtual Private Cloud (Amazon VPC). VPC lets you provision a logically isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define. You have complete control over your virtual networking environment, including selection of your own IP address range, creation of subnets, and configuration of route tables and network gateways.

You can easily customize the network configuration for your Amazon VPC. For example, you can create a public-facing subnet for your web servers that has access to the Internet, and place your backend systems such as databases or application servers in a private-facing subnet with no Internet access. You can leverage multiple layers of security, including security groups and network access control lists, to help control access to Amazon EC2 instances in each subnet.

In this article, I would like to describe how to set up a reliable Wordpress VPC. Two instances of Wordpress servers are deployed at two different availability zones (AZ) to increase the availability, in case one of the AZ is down. Each AZ is divided into three subnets

  • Public subnet. This is the only subnet which can be accessed via Internet.
  • App subnet. This is the application subnet, where all the applications are located. Internet traffic traffic cannot reach here directly, instead they have to reach here through a NAT Gateway located in the public subnet.
  • Data subnet. This is most secure subnet, where all the database or file storages are located. We should configure it as secure as possible to protect the data.

There is a Application Load Balancer to load balance the traffic for the two Wordpress instances. For database, we provision production MySQL with one master database and one standby database. In case the master database is down, the traffic will be automatically routed to the standby database.

We also provision a Network File System share, via the managed service Elastic File System (EFS). This makes it very easy to deploy addtional Wordpress instance. We just need to mount the shared EFS, on which we have installed the Wordpress.

Amazon Elastic File System (Amazon EFS) provides a simple, scalable, elastic file system for Linux-based workloads for use with AWS Cloud services and on-premises resources. It is built to scale on demand to petabytes without disrupting applications, growing and shrinking automatically as you add and remove files, so your applications have the storage they need – when they need it. It is designed to provide massively parallel shared access to thousands of Amazon EC2 instances, enabling your applications to achieve high levels of aggregate throughput and IOPS with consistent low latencies. Amazon EFS is a fully managed service that requires no changes to your existing applications and tools, providing access through a standard file system interface for seamless integration.

Below the diagram for the reliable AWS VPC network

Reliable AWS VPC


VPC

Go to VPC, click Launch VPC, select the VPC Configuration with “VPC with Public and Private Subnets”. In addition to containing a public subnet, this configuration adds a private subnet whose instances are not addressable from the Internet. Instances in the private subnet can establish outbound connections to the Internet via the public subnet using Network Address Translation (NAT). The configuration is as below:

  • VPC name: wp-vpc
  • VPC IPv4 CIDR: 10.0.0.0/16 (65531 IP)

Availability Zone A

Public subnet A

Subnet

  • CIDR: 10.0.0.0/24 (251 IP)
  • AZ: Zone A (per your region)
  • Name: Public-AZ-A

Route table

Destination Target
10.0.0.0/16 local
0.0.0.0/0 Internet Gateway

The default route 0.0.0.0/0 will be routed to public network.

App subnet A

Subnet

  • CIDR: 10.0.1.0/24 (251 IP)
  • AZ: Zone A
  • Name: App-AZ-A

Route table

Destination Target
10.0.0.0/16 local
0.0.0.0/0 NAT Gateway A

The default route 0.0.0.0/0 will be routed to NAT Gateway at public subnet.

Data subnet A

Subnet

  • CIDR: 10.0.2.0/24 (251 IP)
  • AZ: Zone A
  • Name: Data-AZ-A

Route table

Destination Target
10.0.0.0/16 local
0.0.0.0/0 NAT Gateway A

The default route 0.0.0.0/0 will be routed to NAT Gateway at public subnet.


Availability Zone B

Public subnet B

Subnet

  • CIDR: 10.0.3.0/24 (251 IP)
  • AZ: Zone B
  • Name: Public-AZ-B

Route table

Destination Target
10.0.0.0/16 local
0.0.0.0/0 Internet Gateway

The default route 0.0.0.0/0 will be routed to public network.

App subnet B

Create a NAT Gateway (B) in Availability Zone B. Put it in the public subnet B.

Subnet

  • CIDR: 10.0.4.0/24 (251 IP)
  • AZ: Zone B
  • Name: App-AZ-B

Route table

Destination Target
10.0.0.0/16 local
0.0.0.0/0 NAT Gateway B

The default route 0.0.0.0/0 will be routed to NAT Gateway at public subnet.

Data subnet

Subnet

  • CIDR: 10.0.5.0/24 (251 IP)
  • AZ: Zone B
  • Name: Data-AZ-B

Route table

Destination Target
10.0.0.0/16 local
0.0.0.0/0 NAT Gateway B

The default route 0.0.0.0/0 will be routed to NAT Gateway at public subnet.


How to test the network is working

Now the VPC is set up. We need to test the network if it is working properly. What we can do is to install a Linux instance in app subnet, and execute a ping command from inside of the instance. If the ping is executed successfully, it means that the instance in the app subnet can reach the Internet.

Before installing the EC2, we need to create a role, named ssm-role from the IAM, and associate it with policy AmazonEC2RoleforSSM. This role allows us to run script via System Manager. We will run the ping command via the System Manager.

We need to install a EC2 in subnet App-AZ-A. Go to EC2, and launch the following EC2 instance.

  • AMI: Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type - ami-0fb6b6f9e81056553. Do not install Amazon Linux 2
  • Subnet: App-AZ-A
  • Security Group: new, without any rule, since we will test it with outbound traffic only
  • Role: ssm-role, so that Run Command can access it.
  • Name: webserver-demo-1

To run the ping command in webserver-demo-1, do the following:

Go to System Manager, Run Command, select “AWS-RunShellScript”. Choose the instance to run manually, ie webserver-demo-1

Enter the folllowing comand:

ping -c 4 8.8.8.8

If the instance can ping google DNS, then it shows that the instance in app subnet can access Internet.

Perform the the same steps for subnet App-AZ-B to test its out-bound traffic.


Create security groups for external-web and internal-web

A security group acts as a virtual firewall that controls the traffic for one or more instances. When you launch an instance, you can specify one or more security groups; otherwise, we use the default security group. You can add rules to each security group that allow traffic to or from its associated instances. You can modify the rules for a security group at any time; the new rules are automatically applied to all instances that are associated with the security group. When we decide whether to allow traffic to reach an instance, we evaluate all the rules from all the security groups that are associated with the instance.

external-web security will be applied to all instances in the public subnets. internal-web security group will be applied to all instance in the app subnets and data subnets.

external-web security group

Allow http and https from all IP

internal-web security group

Allow http and https from external-web security group


Install web server in webserver-demo-1 and webserver-demo-2

Use the System Manager Run Command, AWS-RunShellScript to run the following scripts:

yum install httpd -y
service httpd start

Provision the Application Load Balancer

Provision an Application Load Balancer with the following configurations:

  • Internet facing
  • Associate it with Public-AZ-A and Public-AZ-B subnets
  • Associate with external-web security group
  • Target group is webserver-demo-1 and webserver-demo-2

To test if the Application Load Balancer is set up properly, go to Application Load Balancer, copy the DNS Name, and open it in browser. If you can see a test page, that mean the set up is successful.


Fix Health Check for Application Load Balancer

Even though you can see the Apache web page via the Application Load Balancer, but the health check is not yet configured properly. Go to EC2, Load Balancing, Target Group, it shows that two web servers are unhealthy, and the traffic will be routed to both of the servers. This is not what we want. We need to fix this health check issue.

We can create a health check page:

Go to System Manager, Run Command, AWS-RunShellScript:

echo "<h1>Hello World</hl>" > /var/www/html/index.html

Go to Target Group, Health Checks tab, edit the path as /index.html

After a couple of minutes, the Targets status will show healthy.


Clean out the webserver-demo-1 and webserver-demo-2

Now we can confirm that the networks are set up properly. We can remove the demo servers.

Go to Target Group, unregister the two web servers.

Go to EC2, terminate the two web servers.


Set up the RDS

Create security group for RDS

  • Name: data
  • Rule: only accept MYSQL/Aurora TCP 3306 from internal-web security group

Create data subnet group

  • Add the subnets Data-AZ-A and Data-AZ-B
  • Name: data-subnetgroup

Building data tier with RDS

Amazon Relational Database Service (Amazon RDS) makes it easy to set up, operate, and scale a relational database in the cloud. It provides cost-efficient and resizable capacity while automating time-consuming administration tasks such as hardware provisioning, database setup, patching and backups. It frees you to focus on your applications so you can give them the fast performance, high availability, security and compatibility they need.

Amazon RDS is available on several database instance types - optimized for memory, performance or I/O - and provides you with six familiar database engines to choose from, including Amazon Aurora, PostgreSQL, MySQL, MariaDB, Oracle Database, and SQL Server. You can use the AWS Database Migration Service to easily migrate or replicate your existing databases to Amazon RDS.

For this set up, we will provision MySQL RDS.

Create database

  • Mysql Production, Multi-AZ deployment
  • Class: db.t2.micro
  • Subnet group: wp-data-subnetgroup
  • Default database: wordpress

Please jot down the RDS credentials when the database is being provisioned.

After database is provisioned, look for the endpoint. The endpoint will always be same and may point to database in either of the availability zone.


Create EFS file system

Create security Group internal-efs. Set the rule to allow all traffic of instances from within internal-efs security group.

  • Provision EFS
  • Associate the iunternal-efs security group with
    • Availability zone A: subnet Data-AZ-A
    • Availability zone B: subnet Data-AZ-B
  • Name: internal-efs

Launch a setup WP server

This server is only used to set up Wordpress server. After being provisioned, it wil be terminated.

  • Name: wpsetup
  • Security group:
    • external-web (so the instance can access web without going to ALB)
    • internal-efs
    • ssh security group (create this security group to allow SSH access)
  • Public IP: auto assigned
  • AMI: Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type - ami-0fb6b6f9e81056553
    (do not use Amazon Linux 2 AMI)

Log in to the wpsetup server via SSH, and run the following comands:

yum update -y

sudo mkdir -p /var/www/html

sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-XXXXXXXX.efs.ap-southeast-1.amazonaws.com:/ /var/www/html
# replace the EFS endpoint with yours

sudo su

yum install -y httpd24 php70 mysql php70-mysqlnd
service httpd start
chkconfig httpd on
usermod -a -G apache ec2-user
sudo chown -R ec2-user:apache /var/www
sudo chmod 2775 /var/www

wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
cp -r wordpress/* /var/www/html/

cp /var/www/html/wp-config-sample.php /var/www/html/wp-config.php

nano /var/www/html/wp-config.php
# DB_NAME: wordpress
# DB_USER: admin
# DB_PASSWORD: when rds is provisioned
# DB_HOST: YOUR-RDS.ap-southeast-1.rds.amazonaws.com

Go to the wp-setup instance, and open the DNS name in the browser, you will see “Error establishing a database connection”. This is due to that it cannot connect to RDS. RDS only accept requests from internal-web. So We will give this security group to the wp-setup.

Atter giving the internal-web security group to the wp-setup, we can now access the Wordpress instance.


Deploy the wordpress servers in App subnets

Now we can access the wp-setup server via the Internet. We can set up the proper Wordpress instances in subnets App-AZ-A and App-AZ-B.

Launch a Amazon Linus AMI (1) in subnet App-AZ-A with the following configurations:

  • Security group: internal-web, internal-efs
  • Name: wordpress-1

After provision, using System Manager Run Command to run the following scripts:

yum update -y

yum install -y httpd24 php70 mysql php70-mysqlnd

service httpd start

chkconfig httpd on

echo "fs-XXXXXXXX.efs.ap-southeast-1.amazonaws.com:/ /var/www/html nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 0 0" >> /etc/fstab

mount -a

Wait for a few minutes before creating another wordpress instance.

Create another Wordpress instance for App-AZ-B with name wordpress-2

Go to Target Group, register these two instances. Wait for health theck to pass.

Open the ALB DNS name in browser. Now you have successfully provision a load-balanced Wordpress servers in two availability zones!