DEVOPS-PROJECT
DevOps Project Report
On
Deploying a 2-tier Application on AWS Cloud Using
Terraform
Submitted by
Name: Dibyajyoti Nayak
Regd. No. 2241014076
Branch: CSE
Section: 2241051
Project Overview
Title: Deploying a 2-Tier Application on AWS Cloud Using Terraform and Docker
Objective:
The goal of this project is to design and deploy a 2-tier application architecture on
AWS using Terraform, Docker, and Bash scripting. The application comprises a
frontend for user interaction and a backend database for data storage, ensuring
secure and efficient communication between the two tiers.
Problem Statement
The task involves creating an automated infrastructure and deployment pipeline for
a 2-tier application using Terraform, AWS resources, Docker containers, and Bash
scripting. The project is divided into three main stages:
1. Create_Infra
2. Deploy_Apps
3. Test_Solution
Stage 1: Create_Infra
[Link]
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "devops_vpc" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "DevOps-VPC"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.devops_vpc.id
cidr_block = var.public_subnet_cidr
map_public_ip_on_launch = true
availability_zone = var.availability_zone
tags = {
Name = "Public-Subnet"
}
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.devops_vpc.id
cidr_block = var.private_subnet_cidr
availability_zone = var.availability_zone
tags = {
Name = "Private-Subnet"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.devops_vpc.id
tags = {
Name = "DevOps-IGW"
}
}
data "aws_eip" "nat_eip" {
id = var.nat_eip_allocation_id
}
resource "aws_nat_gateway" "nat_gw" {
allocation_id = data.aws_eip.nat_eip.id
subnet_id = aws_subnet.[Link]
tags = {
Name = "DevOps-NAT-GW"
}
depends_on = [aws_internet_gateway.gw]
}
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.devops_vpc.id
route {
cidr_block = "[Link]/0"
gateway_id = aws_internet_gateway.[Link]
}
tags = {
Name = "Public-Route-Table"
}
}
resource "aws_route_table_association" "public_rta" {
subnet_id = aws_subnet.[Link]
route_table_id = aws_route_table.public_rt.id
}
resource "aws_route_table" "private_rt" {
vpc_id = aws_vpc.devops_vpc.id
route {
cidr_block = "[Link]/0"
nat_gateway_id = aws_nat_gateway.nat_gw.id
}
tags = {
Name = "Private-Route-Table"
}
}
resource "aws_route_table_association" "private_rta" {
subnet_id = aws_subnet.[Link]
route_table_id = aws_route_table.private_rt.id
}
resource "aws_security_group" "frontend_sg" {
name = "frontend-sg"
description = "Allow HTTP and SSH"
vpc_id = aws_vpc.devops_vpc.id
ingress {
description = "Allow HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["[Link]/0"]
}
ingress {
description = "Allow SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["[Link]/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["[Link]/0"]
}
tags = {
Name = "Frontend-SG"
}
}
resource "aws_security_group" "backend_sg" {
name = "backend-sg"
description = "Allow PostgreSQL & SSH from frontend subnet"
vpc_id = aws_vpc.devops_vpc.id
ingress {
description = "PostgreSQL"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [var.public_subnet_cidr]
}
ingress {
description = "SSH from frontend"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.public_subnet_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["[Link]/0"]
}
tags = {
Name = "Backend-SG"
}
}
resource "aws_instance" "frontend" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = aws_subnet.[Link]
key_name = var.key_pair_name
vpc_security_group_ids = [aws_security_group.frontend_sg.id]
provisioner "file" {
source = "[Link]"
destination = "/home/ubuntu/[Link]"
}
provisioner "remote-exec" {
inline = [
"chmod +x /home/ubuntu/[Link]",
"bash /home/ubuntu/[Link]"
]
}
connection {
type = "ssh"
user = "ubuntu"
private_key = file("${var.key_pair_name}.pem")
host = self.public_ip
}
tags = {
Name = "Frontend-EC2"
}
}
resource "aws_instance" "backend" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = aws_subnet.[Link]
key_name = var.key_pair_name
vpc_security_group_ids = [aws_security_group.backend_sg.id]
provisioner "file" {
source = "[Link]"
destination = "/home/ubuntu/[Link]"
}
provisioner "remote-exec" {
inline = [
"chmod +x /home/ubuntu/[Link]",
"bash /home/ubuntu/[Link]"
]
}
connection {
type = "ssh"
user = "ubuntu"
private_key = file("${var.key_pair_name}.pem")
host = self.private_ip
bastion_host = aws_instance.frontend.public_ip
bastion_user = "ubuntu"
bastion_private_key = file("${var.key_pair_name}.pem")
}
tags = {
Name = "Backend-EC2"
}
}
[Link]
variable "aws_region" {
description = "AWS region"
default = "eu-north-1"
}
variable "vpc_cidr" {
description = "CIDR block for the VPC"
default = "[Link]/16"
}
variable "public_subnet_cidr" {
description = "CIDR block for the public subnet"
default = "[Link]/24"
}
variable "private_subnet_cidr" {
description = "CIDR block for the private subnet"
default = "[Link]/24"
}
variable "availability_zone" {
description = "Availability zone for the subnets"
default = "eu-north-1a"
}
variable "nat_eip_allocation_id" {
description = "Elastic IP allocation ID for NAT Gateway"
default = "eipalloc-0a0777cb608ca6716"
}
variable "ami_id" {
description = "AMI ID for EC2 instances"
default = "ami-042b4708b1d05f512"
}
variable "instance_type" {
description = "EC2 instance type"
default = "[Link]"
}
variable "key_pair_name" {
description = "Name of the AWS key pair"
default = "PROJECT-KEY"
}
[Link]
output "frontend_public_ip" {
description = "Public IP of the frontend EC2 instance"
value = aws_instance.frontend.public_ip
}
output "backend_private_ip" {
description = "Private IP of the backend EC2 instance"
value = aws_instance.backend.private_ip
}
Stage 2: Deploy_Apps
[Link]
#!/bin/bash
sudo apt-get update -y
sudo apt-get install -y [Link]
sudo systemctl start docker
sudo systemctl enable docker
sudo docker pull kanhax04/frontend:latest
sudo docker run -d -p 5000:5000 --name frontend-app kanhax04/frontend:latest
[Link]
#!/bin/bash
sudo apt-get update -y
sudo apt-get install -y [Link]
sudo systemctl start docker
sudo systemctl enable docker
sudo docker pull kanhax04/backend:latest
sudo docker run -d -p 5432:5432 --name backend kanhax04/backend
cd APP/FRONTEND
docker build -t kanhax04/frontend:latest .
docker push kanhax04/frontend:latest
cd APP/BACKEND
docker build -t kanhax04/backend:latest .
docker push kanhax04/backend:latest
Stage 3: Test_Solution
1. Terraform Output
- Public IP: [Link]
2. Frontend URL
- [Link]
3. Curl Test
- Confirmed app loads via curl [Link]
Manual Testing
1. Entered Name “Dibyajyoti Nayak” & Email “sitanayak1980@[Link]” in Frontend
Form
2. Verified DB on BACKEND using SQLite
- Output: SELECT * FROM users;
2TIER-APPLICATION
2TIER-APPLICATION
APP/
├── FRONTEND/
├── [Link]
└── Dockerfile
├── backend/
├── [Link]
├── [Link]
└── Dockerfile
APP/FRONTEND/[Link]
<!DOCTYPE html>
<html>
<head>
<title>Simple Form</title>
</head>
<body>
<h1>Enter Your Details</h1>
<form id="userForm">
<label for="name">Name:</label><br />
<input type="text" id="name" name="name" required /><br />
<label for="email">Email:</label><br />
<input type="email" id="email" name="email" required /><br /><br />
<button type="submit">Submit</button>
</form>
<p id="message"></p>
<script>
[Link]("userForm").addEventListener("submit", async function(e) {
[Link]();
const name = [Link]("name").value;
const email = [Link]("email").value;
try {
const response = await fetch("[Link] {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: [Link]({ name, email }),
});
const result = await [Link]();
[Link]("message").textContent = [Link];
} catch (err) {
[Link]("message").textContent = "Error contacting backend!";
}
});
</script>
</body>
</html>
APP/FRONTEND/Dockerfile
FROM nginx:alpine
COPY [Link] /usr/share/nginx/html/[Link]
EXPOSE 80
APP/BACKEND/[Link]
from flask import Flask, request, jsonify
from flask_cors import CORS
import sqlite3
app = Flask(__name__)
CORS(app)
def init_db():
conn = [Link]('[Link]')
cursor = [Link]()
[Link]('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL
)
''')
[Link]()
[Link]()
init_db()
@[Link]("/submit", methods=["POST"])
def submit():
data = request.get_json()
name = [Link]("name")
email = [Link]("email")
if not name or not email:
return jsonify({"message": "Missing name or email"}), 400
conn = [Link]('[Link]')
cursor = [Link]()
[Link]("INSERT INTO users (name, email) VALUES (?, ?)", (name, email))
[Link]()
[Link]()
return jsonify({"message": "User saved successfully!"}), 200
if __name__ == "__main__":
[Link](host="[Link]", port=5000)
APP/BACKEND/[Link]
Flask
APP/BACKEND/Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY . .
RUN pip install -r [Link]
EXPOSE 5000
CMD ["python", "[Link]"]