Last Updated: 2020-09-23
A three-tier architecture is a software architecture pattern where the application is broken down into three logical tiers:
This architecture is used in a client-server application such as a web application that has:
Each of these layers or tiers does a specific task and can be managed independently of each other. This a shift from the monolithic way of building an application where the frontend, the backend and the database are both sitting in one place.
In this lab, you will use the following GCP services to design and build a three-tier cloud infrastructure:
Your infrastructure will be designed to be highly available and fault tolerant.
The essence of having a three-tier architecture is to modularize your application so that each part can be managed independently of each other. With modularity, teams can focus on different tiers of the application and make changes as quickly as possible. Also, modularization helps you recover quickly from an unexpected disaster by focusing solely on the faulty part.
Each tier of the architecture can scale horizontally to support the traffic and request demand coming to it. This can easily be done by adding more GCE instances to each tier and load balancing across them. For instance, assuming you have two GCE instances serving your backend application and each of the GCE instances is working at 80% CPU utilization, you can easily scale the backend tier by adding more GCE instances to it so that the load can be distributed. You can also automatically reduce the number of the GCE instances when the load is less.
With the traditional data centre, your application is sitting in one geographical location. If there is an earthquake, flooding or even power outage in that location where your application is hosted, your application will not be available. With GCP, you can design your infrastructure to be highly available by hosting your application in different locations known as the availability zones.
You want your infrastructure to comfortably adapt to any unexpected change both to traffic and fault. This is usually done by adding a redundant system that will account for such a spike in traffic when it does occur. So instead of having two GCE instances working at 50% each, such that when one instance goes down, the other instance will be working at 100% capacity until a new instance is brought up by your Managed Instance Group, you have an extra instance making it three instances working at approximately 35% each. This is usually a tradeoff made against the cost of setting up a redundant system.
You want to design an infrastructure that is secured and protected from the prying eyes of hackers. As much as possible, you want to avoid exposing your interactions within the application over the internet. This simply means that the application parts will communicate with each other using a private IP. The presentation (frontend) tier of the infrastructure will be in a private subnet (the subnet with no public IP assigned to its instances) within the VPC. Users can only reach the frontend through the cloud load balancer. The backend and the database tier will also be in the private subnet because you do not want to expose them over the internet. You will use Identity-Aware Proxy for remote SSH and a NAT gateway for your private subnets to access the internet. The GCP firewall rules help us limit access to your infrastructure.
In this codelab, you're going to build a 3 Tier Web application.
Your app will:
This codelab is focused on building a 3 Tier infrastructure. Non-relevant concepts and code blocks are glossed over and are provided for you to simply copy and paste.
In this step, you register for the Google Cloud Platform free trial and create a project. The free trial provides you:
To register for the free trial open the free trial Registration page.
If you do not have a Gmail account, follow the steps to create one. Otherwise, login and complete the registration form.
Read and agree to the terms of service. Click Accept and start a free trial.
Next, create your first project using the Google Cloud Platform Console. The project is used to complete the rest of the lab.
To create a project in the Google Cloud Platform Console, click Select a project > Create a project
.
In the New Project dialog: for Project name, type whatever you like. Make a note of the Project ID
in the text below the project name box; you need it later. Then click Create.
In the upper-right corner of the console, a button will appear asking you to upgrade your account. Click Create a Project when you see it. If the Upgrade button does not appear, you may skip this step. If the button appears later, click it when it does.
When you upgrade your account, you immediately have access to standard service quotas, which are higher than those available on the free trial.
On the GCP Console, use the left-hand side menu to navigate to Compute Engine and ensure that there are no errors.
At the end of this lab, you may delete this project and close your billing account if desired.
You will do most of the work from the Google Cloud Shell, a command line environment running in the Cloud. This virtual machine is loaded with all the development tools you'll need (docker
, gcloud
, kubectl
and others) and offers a persistent 5GB home directory and runs in Google Cloud, greatly enhancing network performance and authentication. Open the Google Cloud Shell by clicking on the icon on the top right of the screen:
You should see the shell prompt at the bottom of the window:
Once connected to Cloud Shell, you should see that you are already authenticated and that the project is already set to your project ID.
Run the following command in Cloud Shell to confirm that you are authenticated
gcloud auth list
If it's the first time you are running Cloud Shell - authorize it.
You might need to run the command again after authorization. Command output:
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
Check if your project is set correctly.
gcloud config list project
Command output
[core]
project = <PROJECT_ID>
If it is not, you can set it with this command:
gcloud config set project <PROJECT_ID>
Virtual Private Cloud (VPC) is a virtual network where you create and manage your GCP resource in a more secure and scalable manner. For this lab we will use a regional VPC with custom subnets. To create a new VPC run:
$ gcloud compute networks create three-tier \
--project=${GOOGLE_CLOUD_PROJECT} \
--subnet-mode=custom \
--mtu=1460 \
--bgp-routing-mode=regional
...
NAME SUBNET_MODE BGP_ROUTING_MODE IPV4_RANGE GATEWAY_IPV4
three-tier CUSTOM REGIONAL
...
The subnet is a way for you to group our resources within the VPC with their IP range. A subnet can be public or private. GCE instances within a public subnet have public IPs and can directly access the internet while those in the private subnet do not have public IPs and can only access the internet through a NAT.
For your setup, you will create the following subnets with the corresponding IP ranges.
The second private subnet will have Private Google Access enabled, so that VMs in this subnet can access Google services without assigning external IP addresses (e.g., Cloud SQL).
$ gcloud compute networks subnets create private-subnet-1 \
--network=three-tier \
--range=10.0.1.0/24 \
--region=europe-west1
$ gcloud compute networks subnets create private-subnet-2 \
--network=three-tier \
--range=10.0.2.0/24 \
--region=europe-west1 \
--enable-private-ip-google-access
In Cloud Console navigate to Networking → VPC network → VPC networks.
You should see your new network with 2 subnets at the bottom:
Cloud NAT lets your GCE instances communicate with the internet using a shared, public IP address.
Cloud NAT uses NAT gateway to manage those connections. A NAT gateway is region- and VPC network-specific. If you have VM instances in multiple regions, you'll need to create a NAT gateway for each region.
Before we create the actual NAT Gateway we need to create a Cloud Router, that can dynamically exchange routes between a VPC network and any non-Google network. By default, Cloud Router advertises subnets in its region for regional dynamic routing. To create a Cloud Router, we need to provide an Autonomous System Number (64512 - 65534, 4200000000 - 4294967294), that is not used anywhere in our infrastructure.
$ gcloud compute routers create nat-router \
--network three-tier \
--asn 65000 \
--region europe-west1
The simplest configuration that we will use automatically allocates the necessary external IP addresses to provide NAT services to a region. VM instances without external IP addresses in any subnet of the region are provided internet access through NAT.
$ gcloud compute routers nats create nat-gw \
--router=nat-router \
--auto-allocate-nat-external-ips \
--nat-all-subnet-ip-ranges \
--region europe-west1
In Cloud Console navigate to Networking → Network services → Cloud NAT.
You should see your NAT Gateway up and running using your Cloud Router.
VPC firewall rules let you allow or deny connections to or from your GCE instances based on a configuration that you specify. Enabled VPC firewall rules are always enforced, protecting your instances regardless of their configuration and operating system, even if they have not started up.
Every VPC network functions as a distributed firewall. While firewall rules are defined at the network level, connections are allowed or denied on a per-instance basis. You can think of the VPC firewall rules as existing not only between your instances and other networks, but also between individual instances within the same network.
In addition to firewall rules that you create, Google Cloud has other rules that can affect incoming (ingress) or outgoing (egress) connections:
169.254.169.254
.Default VPC networks include pre-populated firewall rules, including an ingress rule that allows ingress from sources in the 10.128.0.0/9 range (CIDR for default network). For custom subnets and custom mode VPC networks, you'll need to create your own ingress allow firewall rules.
First, let's set up basic Firewall rules.
Create a firewall rule to allow access (TCP, UDP, ICMP ports) internally within our private subnets in VPC:
$ gcloud compute firewall-rules create allow-internal-private \
--allow tcp:1-65535,udp:1-65535,icmp \
--source-ranges 10.0.1.0/24,10.0.2.0/24 \
--network three-tier \
--priority 65534
Create a firewall rule to allow ingress ICMP traffic from any source to any instance in the network. It enables tools such as ping.
$ gcloud compute firewall-rules create default-allow-icmp-three-tier \
--allow icmp \
--network three-tier \
--priority 65534
We will be creating load balancers & health checks later on, so we need a firewall rule that will allow Load Balancer health checks to reach our private instances in the private subnet. Also, to limit which instances are going to be accessible for health checks we're going to add a network tag:
$ gcloud compute firewall-rules create allow-lb-health-three-tier \
--direction=INGRESS \
--action=allow \
--rules=tcp:80 \
--target-tags=allow-healthcheck \
--source-ranges=130.211.0.0/22,35.191.0.0/16,209.85.152.0/22,209.85.204.0/22 \
--network three-tier
Our users will access our frontend instances from any part of the world through load balancer, so we need to allow HTTP from anywhere in the world. Once again, limiting the instancess affected using a tag:
$ gcloud compute firewall-rules create allow-http-three-tier \
--direction=INGRESS \
--action=allow \
--rules=tcp:80 \
--target-tags=allow-http \
--network three-tier
Finally, we can use Identity-Aware Proxy (IAP) TCP forwarding to enable administrative access to VM instances that do not have public IP addresses or do not permit direct access over the internet, such as the ones in our private subnet. Same as before, we're going to use a network tag to limit which instances are accessible that way.
$ gcloud compute firewall-rules create allow-ssh-ingress-from-iap \
--direction=INGRESS \
--action=allow \
--rules=tcp:22 \
--source-ranges=35.235.240.0/20 \
--target-tags=allow-ssh \
--network three-tier
To set up a load balancer with a Compute Engine backend, your VMs need to be in instance groups. Managed Instance Groups can adjust the number of the GCE instances serving the application based on need, so it can scale to accommodate traffic or shrink when there is no traffic to save cost.
You will create four virtual machine instances: two to serve as backends and two to serve as frontends.
To create an instance group, you need to create an Instance Template for the VMs in the group. We will use a startup script to populate our dummy backend application.
Create the template:
$ gcloud compute instance-templates create backend-template \
--region=europe-west1 \
--network=three-tier \
--subnet=private-subnet-2 \
--tags=allow-ssh,allow-healthcheck \
--image-family=debian-10 \
--image-project=debian-cloud \
--boot-disk-size=10GB \
--machine-type=f1-micro \
--no-address \
--metadata=startup-script='#! /bin/bash
sudo apt-get update
sudo apt-get install -y git apache2
cd /var/www/html
sudo rm index.html -f
sudo git init
sudo git pull https://github.com/DmyMi/2048.git
ZONE=$(curl "http://metadata.google.internal/computeMetadata/v1/instance/zone" -H "Metadata-Flavor: Google")
sed -i "s|zone-here|$ZONE|" /var/www/html/index.html
sudo systemctl restart apache2'
Next, we will create a regional instance group spread across two availability zones:
$ gcloud compute instance-groups managed create backend-mig \
--template=backend-template \
--size=2 \
--zones=europe-west1-b,europe-west1-c \
--region=europe-west1
Finally, we need to setup auto scaling policy to scale up to a maximum of 5 instances with 70% CPU threshold:
$ gcloud compute instance-groups managed set-autoscaling backend-mig \
--max-num-replicas=5 \
--scale-based-on-cpu \
--target-cpu-utilization=0.7 \
--region=europe-west1
In Cloud Console navigate to Compute → Compute Engine → VM Instances.
You should see 2 instances online:
There are several types of load balancers in Google Cloud Platform including :
Let's create an internal regional network load balancer for our backend instance group.
For that we need a backend service. A backend service allows you to have a single access point to all the instances in a group and is necessary for load balancing.
First, create a health check. As we're creating a Network load balancer - we will use a TCP health check.
$ gcloud compute health-checks create tcp backend-check \
--port 80
Then create a backend service:
$ gcloud compute backend-services create backend-bs \
--global-health-checks \
--protocol=TCP \
--load-balancing-scheme=INTERNAL \
--network=three-tier \
--health-checks=backend-check \
--region=europe-west1
Next, add our instance group to the backend service:
$ gcloud compute backend-services add-backend backend-bs \
--instance-group=backend-mig \
--instance-group-region=europe-west1 \
--balancing-mode=CONNECTION \
--region=europe-west1
Now, lets create an internal regional network load balancer targeting our instance group:
$ gcloud compute forwarding-rules create backend-lb \
--ports 80 \
--backend-service backend-bs \
--load-balancing-scheme=INTERNAL \
--network=three-tier \
--subnet=private-subnet-2 \
--ip-protocol=TCP \
--region=europe-west1
Check if the load balancer is created and write down its IP address, we will need it later.
$ gcloud compute forwarding-rules list
NAME REGION IP_ADDRESS IP_PROTOCOL TARGET
backend-lb europe-west1 10.0.2.x TCP europe-west1/backendServices/backend-bs
Now we need to do the same for the frontend part of the application. As a dummy front end we will use a NGINX that will just forward requests to the backend.
First, create a startup script:
$ touch startup.sh
$ cloudshell edit startup.sh
Paste the following code:
#! /bin/bash
# Install NGINX
sudo yum -y update; sudo yum clean all
sudo yum -y install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm; sudo yum -y makecache
sudo yum -y install nginx-1.14.0
# Remove default files we don't need
sudo rm -f /etc/nginx/conf.d/default.conf
sudo cat <<__EOF__>/etc/nginx/nginx.conf
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 1024;
events {
multi_accept on;
worker_connections 1024;
}
http {
upstream myapp {
server INTERNAL_LOADBALANCER_IP;
}
server {
listen 80 default_server;
server_name "";
location / {
proxy_pass http://myapp;
proxy_set_header Host \$host;
proxy_http_version 1.1;
proxy_read_timeout 120s;
}
}
}
__EOF__
sudo systemctl restart nginx
Back in Cloud Shell, create the template:
$ gcloud compute instance-templates create frontend-template \
--region=europe-west1 \
--network=three-tier \
--subnet=private-subnet-1 \
--tags=allow-ssh,allow-healthcheck,allow-http \
--image-family=centos-7 \
--image-project=centos-cloud \
--boot-disk-size=20GB \
--machine-type=f1-micro \
--no-address \
--metadata-from-file startup-script=startup.sh
Next, we will create a regional instance group spread across two availability zones:
$ gcloud compute instance-groups managed create frontend-mig \
--template=frontend-template \
--size=2 \
--zones=europe-west1-b,europe-west1-c \
--region=europe-west1
Finally, we need to setup auto scaling policy to scale up to a maximum of 5 instances depending on HTTP load balancing hits:
$ gcloud compute instance-groups managed set-autoscaling frontend-mig \
--max-num-replicas=5 \
--scale-based-on-load-balancing \
--target-load-balancing-utilization=0.7 \
--region=europe-west1
Run the following to check if instances are created:
$ gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
backend-mig-79pz europe-west1-b f1-micro 10.0.2.x RUNNING
frontend-mig-gjmn europe-west1-b f1-micro 10.0.1.x RUNNING
backend-mig-zfbk europe-west1-c f1-micro 10.0.2.x RUNNING
frontend-mig-h65c europe-west1-c f1-micro 10.0.1.x RUNNING
You should see 4 instances online: 2 backends and 2 frontends.
Let's create a regional HTTP load balancer for our frontend instance group. It will allow us to connect to our application from the outside world.
First, create a health check. As we're creating a HTTP load balancer - we will use a http health check.
$ gcloud compute health-checks create http frontend-check \
--port 80
Set a named port on our instance group:
$ gcloud compute instance-groups managed set-named-ports frontend-mig \
--named-ports http:80 \
--region=europe-west1
Then create a global backend service for frontend instances:
$ gcloud compute backend-services create frontend-bs \
--global \
--global-health-checks \
--protocol=HTTP \
--port-name=http \
--health-checks=frontend-check
Next, add our instance group to the backend service:
$ gcloud compute backend-services add-backend frontend-bs \
--instance-group=frontend-mig \
--instance-group-region=europe-west1 \
--balancing-mode=RATE \
--max-rate-per-instance=100 \
--capacity-scaler=0.8 \
--global
Next, create an URL map to route the incoming requests to the appropriate backend services. In this case, the request path mappings defined via the --path-rules
flag split traffic according to the URL path in each request to your site. Traffic that does not match an entry in the --path-rules
list is sent to the entry in the --default-service
flag.
$ gcloud compute url-maps create frontend-map \
--default-service frontend-bs
$ gcloud compute url-maps add-path-matcher frontend-map \
--default-service frontend-bs \
--path-matcher-name pathmap \
--path-rules="/*=frontend-bs"
Now, let's create a HTTP Proxy and HTTP load balancer targeting our instance group:
$ gcloud compute target-http-proxies create frontend-proxy \
--url-map=frontend-map
$ gcloud compute forwarding-rules create frontend-lb \
--ports 80 \
--global \
--target-http-proxy=frontend-proxy
Check if the load balancer is created and copy its public IP.
$ gcloud compute forwarding-rules list
NAME REGION IP_ADDRESS IP_PROTOCOL TARGET
frontend-lb xx.xx.xx.xx TCP frontend-proxy
backend-lb europe-west1 10.0.2.8 TCP europe-west1/backendServices/backend-bs
Open it in the browser to see our application:
We can use IAP to tunnel TCP connections to our private instances. So we can access them even without public IP.
Let's get a list of our instances again:
$ gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
backend-mig-79pz europe-west1-b f1-micro 10.0.2.x RUNNING
frontend-mig-gjmn europe-west1-b f1-micro 10.0.1.x RUNNING
backend-mig-zfbk europe-west1-c f1-micro 10.0.2.x RUNNING
frontend-mig-h65c europe-west1-c f1-micro 10.0.1.x RUNNING
Choose one of the instances and enter the following (modify according to your output of previous command):
$ gcloud compute ssh frontend-mig-... --zone=europe-west1-c --tunnel-through-iap
[user@frontend-mig-... ~]$
Let's try checking if we have access to the Internet through NAT:
[user@frontend-mig-... ~]$ sudo yum update -y
You should see that packages were updated successfully!
Disconnect from the instance:
[user@frontend-mig-... ~]$ exit
You can simply copy paste the following:
gcloud compute forwarding-rules delete frontend-lb --global --quiet
gcloud compute target-http-proxies delete frontend-proxy --quiet
gcloud compute url-maps delete frontend-map --quiet
gcloud compute backend-services delete frontend-bs --global --quiet
gcloud compute health-checks delete frontend-check --quiet
gcloud compute instance-groups managed delete frontend-mig \
--region=europe-west1 --quiet
gcloud compute instance-templates delete frontend-template --quiet
gcloud compute forwarding-rules delete backend-lb \
--region=europe-west1 --quiet
gcloud compute backend-services delete backend-bs \
--region=europe-west1 --quiet
gcloud compute health-checks delete backend-check --quiet
gcloud compute instance-groups managed delete backend-mig \
--region=europe-west1 --quiet
gcloud compute instance-templates delete backend-template --quiet
gcloud compute routers nats delete nat-gw \
--router=nat-router \
--region=europe-west1 --quiet
gcloud compute routers delete nat-router \
--region europe-west1 --quiet
gcloud compute firewall-rules delete allow-internal-private --quiet
gcloud compute firewall-rules delete default-allow-icmp-three-tier --quiet
gcloud compute firewall-rules delete allow-lb-health-three-tier --quiet
gcloud compute firewall-rules delete allow-http-three-tier --quiet
gcloud compute firewall-rules delete allow-ssh-ingress-from-iap --quiet
gcloud compute networks subnets delete private-subnet-1 \
--region=europe-west1 --quiet
gcloud compute networks subnets delete private-subnet-2 \
--region=europe-west1 --quiet
gcloud compute networks delete three-tier --quiet
rm ~/startup.sh
More GCP functionality ;)