GCP DevOps-Infra — Three Tier Architecture Deployment using Terraform

Efficient Three-Tier Deployment on GCP with Terraform

Jitendra Gupta
Google Cloud - Community
8 min readFeb 24, 2023

--

GCP DevOps-Infra — Three Tier Architecture Deployment using Terraform #gcp #architecture #terraform

What is a three-tier architecture?

A three-tier architecture is a software design pattern that separates an application into three layers, each with its own specific function. these layers are:

Presentation tier

Application Tier

Database tier

  1. Presentation Tier: This tier is responsible for providing the user interface (UI) for the application. It is the layer that interacts with users, presenting them with the application’s visual interface and receiving input from them, usually implemented as a web-based UI, such as a web page or a mobile app.
  2. Application Tier: This tier contains the application logic and processes data from the presentation tier. It is responsible for handling user requests, performing calculations, and executing business rules. The application tier also interacts with databases, to retrieve or store data.
  3. Data Tier: This tier is responsible for storing and retrieving data. It contains the database or data storage system and provides a standardized interface for accessing data. The data tier is typically implemented using a relational database management system (RDBMS), such as MySQL, Postgres, or SQL Server.

Three-tier Architecture in GCP:

Now, we learnt, what is a three-tier architecture, let’s explore it further with below diagram.

Three-tier Application Architecture in GCP

The above diagram has below components:

1. Front-end Web Application

2. Backend middleware application

3. Cloud MemoryStore to cache frequently data

4. Cloud SQL Backend (Postgres Database)

5. Cloud Build

6. Other Services — Cloud Identity, Cloud Monitoring & Cloud Logging

Front-end Web Application: which is deployed on Cloud run, a serverless compute platform, allows to run stateless containers that are triggered by incoming HTTP requests.

Backend Middleware Application: Similar to front-end application, it is also deployed to Cloud run, and integrates with the front-end web app through backend apps endpoint URL, and then executes the necessary logics and communicates with the database to retrieve or update data as required.

Cloud MemoryStore: for quick retrieval of data, cloud Memorystore is used, which is a fully managed in-memory data store that provides sub-millisecond latency for caching. The frequently access data is cached in cloud memorystore and backend app first checks for data in memorystore, if it’s not already cached, it will retrieve data from Cloud SQL database.

Cloud SQL Backend: the database tier is deployed on Cloud SQL, which provides fully managed relational databases in the cloud. Here we have used Postgres SQL as the database. Cloud SQL takes care of managing the database infrastructure, backups, and scaling, so we don’t have to worry about managing the underlying infrastructure.

Cloud Build: It is a CI/CD tool to deploy all these services to a GCP project.

The other services used in this architecture are Cloud identity to manage the users, and groups, Cloud monitoring and Cloud logging is enabled by default once these serverless and managed services are deployed to GCP.

Terraform code to Deploy Three-tier Application in GCP:

I have implemented this architecture using Terraform, a tool for deploying and managing the infrastructure on GCP. With Terraform, you can define your Infrastructure-as-Code (IaC) and automate the deployment process. This makes it easy to deploy and manage complex infrastructure, such as the three-tier architecture as discussed above.

Below are the Terraform Configs which anyone can try in their projects.

List of files -

backend-app.tf
dev.tfvars
enable-services.tf
networks.tf
postgresdb.tf
providers.tf
service-accounts.tf
variables.tf
versions.tf
webapp-frontend.tf
outputs.tf

backend-app.tf

resource "google_cloud_run_service" "bkd-app" {
name = "${var.app_name}-bkd-app"
provider = google-beta
location = var.region
project = var.project_id

template {
spec {
service_account_name = google_service_account.runsa.email
containers {
image = "gcr.io/three-tier-app-prj/todo-bckend-app:latest"
env {
name = "redis_host"
value = google_redis_instance.main.host
}
env {
name = "db_host"
value = google_sql_database_instance.main.ip_address[0].ip_address
}
env {
name = "db_user"
value = google_service_account.runsa.email
}
env {
name = "db_conn"
value = google_sql_database_instance.main.connection_name
}
env {
name = "db_name"
value = "todo"
}
env {
name = "redis_port"
value = "6379"
}

}
}

metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "30"
"run.googleapis.com/cloudsql-instances" = google_sql_database_instance.main.connection_name
"run.googleapis.com/client-name" = "terraform"
"run.googleapis.com/vpc-access-egress" = "all"
"run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.main.id

}
}
}
metadata {
labels = var.labels
}
autogenerate_revision_name = true
depends_on = [
google_sql_user.main,
google_sql_database.database
]
}

dev.tfvars

//value for dev
region = "us-central1"
zone = "us-central1-a"
project_id = "three-tier-app-prj"

enable-services.tf

module "project-services" {
source = "terraform-google-modules/project-factory/google//modules/project_services"
version = "13.0.0"
disable_services_on_destroy = false

project_id = var.project_id
enable_apis = var.enable_apis

activate_apis = [
"compute.googleapis.com",
"cloudapis.googleapis.com",
"vpcaccess.googleapis.com",
"servicenetworking.googleapis.com",
"cloudbuild.googleapis.com",
"sql-component.googleapis.com",
"sqladmin.googleapis.com",
"storage.googleapis.com",
"run.googleapis.com",
"redis.googleapis.com",
]
}

networks.tf

resource "google_compute_network" "main" {
provider = google-beta
name = "${var.app_name}-private-network"
auto_create_subnetworks = true
project = var.project_id
}

resource "google_compute_global_address" "main" {
name = "${var.app_name}-vpc-address"
provider = google-beta
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = google_compute_network.main.name
project = var.project_id
}

resource "google_service_networking_connection" "main" {
network = google_compute_network.main.self_link
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.main.name]

}

resource "google_vpc_access_connector" "main" {
provider = google-beta
project = var.project_id
name = "${var.app_name}-vpc-ac"
ip_cidr_range = "10.8.0.0/28"
network = google_compute_network.main.name
region = var.region
max_throughput = 300
}

postgresdb.tf

resource "google_redis_instance" "main" {
authorized_network = google_compute_network.main.name
connect_mode = "DIRECT_PEERING"
location_id = var.zone
memory_size_gb = 1
name = "${var.app_name}-cache"
display_name = "${var.app_name}-cache"
project = var.project_id
redis_version = "REDIS_6_X"
region = var.region
reserved_ip_range = "10.137.125.88/29"
tier = "BASIC"
transit_encryption_mode = "DISABLED"
labels = var.labels
}

resource "random_id" "id" {
byte_length = 2
}


resource "google_sql_database_instance" "main" {
name = "${var.app_name}-db-${random_id.id.hex}"
database_version = "POSTGRES_14"
region = var.region
project = var.project_id

settings {
tier = "db-g1-small"
disk_autoresize = true
disk_autoresize_limit = 0
disk_size = 10
disk_type = "PD_SSD"
user_labels = var.labels
ip_configuration {
ipv4_enabled = false
private_network = "projects/${var.project_id}/global/networks/${google_compute_network.main.name}"
}
location_preference {
zone = var.zone
}
database_flags {
name = "cloudsql.iam_authentication"
value = "on"
}
}
deletion_protection = false

depends_on = [
google_service_networking_connection.main
]
}


resource "google_sql_user" "main" {
project = var.project_id
name = "${google_service_account.runsa.account_id}@${var.project_id}.iam"
type = "CLOUD_IAM_SERVICE_ACCOUNT"
instance = google_sql_database_instance.main.name
deletion_policy = "ABANDON"
}

resource "google_sql_database" "database" {
project = var.project_id
name = "todo"
instance = google_sql_database_instance.main.name
deletion_policy = "ABANDON"
}

providers.tf

#ensure gcs bucket is created in your project before deploying the infrastructure
# or comment out this block to save statefile locally on your machine
terraform {
backend "gcs" {
bucket = "webapp-tf"
prefix = "three-tier-app"
}
}

service-accounts.tf

resource "google_service_account" "runsa" {
project = var.project_id
account_id = "${var.app_name}-run-sa"
display_name = "Service Account for Cloud Run"
}

resource "google_project_iam_member" "allrun" {
for_each = toset(var.run_roles)
project = data.google_project.project.number
role = each.key
member = "serviceAccount:${google_service_account.runsa.email}"
}

resource "google_cloud_run_service_iam_member" "noauth_bkd-app" {
location = google_cloud_run_service.bkd-app.location
project = google_cloud_run_service.bkd-app.project
service = google_cloud_run_service.bkd-app.name
role = "roles/run.invoker"
member = "allUsers"
}

resource "google_cloud_run_service_iam_member" "noauth_webapp" {
location = google_cloud_run_service.webapp.location
project = google_cloud_run_service.webapp.project
service = google_cloud_run_service.webapp.name
role = "roles/run.invoker"
member = "allUsers"
}

variables.tf

variable "project_id" {
type = string
description = "project ID"
}

variable "region" {
type = string
description = "Compute Region"
}

variable "zone" {
type = string
description = "Compute Zone"
}

variable "app_name" {
type = string
description = "prefix for app."
default = "todo-list-app"
}

variable "labels" {
type = map(string)
description = "map of labels."
default = { "todo-list-app" = true }
}

variable "enable_apis" {
type = string
description = "enable apis."
default = true
}

variable "run_roles" {
description = "roles used by cloud run service"
type = list(string)
default = [
"roles/cloudsql.instanceUser",
"roles/cloudsql.client",
]
}

versions.tf

terraform {
required_version = ">= 0.13"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 3.53, < 5.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 3.53, < 5.0"
}
random = {
source = "hashicorp/random"
version = ">= 2.2"
}
}

provider_meta "google" {
module_name = "blueprints/terraform/terraform-google-three-tier-app/v0.1.5"
}
}

data "google_project" "project" {
project_id = var.project_id
}

webapp-frontend.tf

resource "google_cloud_run_service" "webapp" {
name = "${var.app_name}-webapp"
location = var.region
project = var.project_id

template {
spec {
service_account_name = google_service_account.runsa.email
containers {
image = "gcr.io/three-tier-app-prj/todo-webapp:latest"
ports {
container_port = 80
}
env {
name = "ENDPOINT"
value = google_cloud_run_service.bkd-app.status[0].url
}
}
}
}
metadata {
labels = var.labels
}
}

outputs.tf

output "webpage-url" {
value = google_cloud_run_service.webapp.status[0].url
description = "webapp url"
}
output "database-name" {
value = google_sql_database_instance.main.name
description = "database name."
}

Terraform commands to execute:

#ensure gcs bucket is created in your project before deploying the infrastructure
# or comment out providers.tf block to save statefile locally on your machine

#clone the code repository
git clone https://github.com/jitu028/gcp-three-tier-app.git

cd folder-containing-terraform-files # change directory to path contaning terraform files

terraform init # to download the plagins and initialize the provider

terraform validate # to validate the configs

terraform fmt # to format the content

terraform plan -input=false -var-file=dev.tfvars # to generate the deployment plan
# update the dev.tfvars file your project-id

terraform apply -auto-approve -input=false -var-file=dev.tfvars # to auto-approve and deploy the changes to your project
# update the dev.tfvars file your project-id

Summary

In this article, we learnt how to efficiently deploy a three-tier architecture on GCP using Terraform. Discovered the different tiers of the architecture, including presentation, application, and data, and how they work together. Explored the different components of the GCP three-tier architecture, such as front-end web applications and cloud SQL backends, and plus, get a look at the Terraform code used to automate the deployment process and manage infrastructure on GCP.

References:

About me — I am a GCP Cloud Architect with over a decade of experience in IT industry. A multi-cloud certified professional. Past few months I wrote 17+ cloud certification (10x GCP). My current engagements are helping customer migrate their workloads from on-prem datacenter and other cloud providers to Google Cloud.

If you got any question, you can reach me on LinkedIn and twitter @jitu028 and DM, I’ll be happy to help!!

You can also schedule 1:1 discussion with me on https://www.topmate.io/jitu028 for any Google Cloud related support.

Appreciate the technical knowledge shared? Support my work by buying me a book. Just scan the QR code below to make a difference.

https://www.buymeacoffee.com/jitu028

--

--

Jitendra Gupta
Google Cloud - Community

Manager - GCP Engineering, Fully GCP-certified, helping customers migrate workloads to Google Cloud, career guidance, Tech-Philosopher, Empathy, Visionary