From eab060df8cfde1137d24f372a2396963c69b5965 Mon Sep 17 00:00:00 2001 From: omagdy7 Date: Mon, 29 Apr 2024 01:20:53 +0300 Subject: Intial IaC code and setting up deployment using ansible --- .gitignore | 5 + ansible/gunicorn.service.j2 | 13 ++ ansible/hosts.ini | 2 + ansible/nginx.conf.j2 | 13 ++ ansible/setup-deployment.yml | 108 +++++++++++++++++ ansible/setup-docker.yml | 27 +++++ terraform/main.tf | 283 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 451 insertions(+) create mode 100644 .gitignore create mode 100644 ansible/gunicorn.service.j2 create mode 100644 ansible/hosts.ini create mode 100644 ansible/nginx.conf.j2 create mode 100644 ansible/setup-deployment.yml create mode 100644 ansible/setup-docker.yml create mode 100644 terraform/main.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2504ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.hosts.ini +.terraform +.terraform.lock.hcl +terraform.tfstate +terraform.tfstate.backup diff --git a/ansible/gunicorn.service.j2 b/ansible/gunicorn.service.j2 new file mode 100644 index 0000000..fe9c69c --- /dev/null +++ b/ansible/gunicorn.service.j2 @@ -0,0 +1,13 @@ +[Unit] +Description=gunicorn daemon +After=network.target + +[Service] +User=ec2-user +Group=nginx +WorkingDirectory=/opt/image_processor +ExecStart=/opt/image_processor/venv/bin/gunicorn --workers 3 --bind unix:{{ app_directory }}/myapp.sock -m 007 wsgi:app + +[Install] +WantedBy=multi-user.target + diff --git a/ansible/hosts.ini b/ansible/hosts.ini new file mode 100644 index 0000000..c3896e7 --- /dev/null +++ b/ansible/hosts.ini @@ -0,0 +1,2 @@ +[ec2_instances] +44.202.210.65 ansible_user=ec2-user ansible_ssh_private_key_file=~/keypair_amazon/deployer_key diff --git a/ansible/nginx.conf.j2 b/ansible/nginx.conf.j2 new file mode 100644 index 0000000..a890814 --- /dev/null +++ b/ansible/nginx.conf.j2 @@ -0,0 +1,13 @@ +server { + listen 80; + ec2_user {{ server_ip }}; + + location / { + proxy_pass http://unix:/opt/image_processor/myapp.sock; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + diff --git a/ansible/setup-deployment.yml b/ansible/setup-deployment.yml new file mode 100644 index 0000000..feb42c5 --- /dev/null +++ b/ansible/setup-deployment.yml @@ -0,0 +1,108 @@ +--- +- name: Deploy Flask Application on Amazon Linux + hosts: all + become: yes + + vars: + app_directory: "/opt/image_processor" + requirements: + - Flask==2.0.1 + - flask-cors + - boto3 + - opencv-python-headless + - numpy + - gunicorn + + tasks: + - name: Update all system packages + yum: + name: "*" + state: latest + update_cache: yes + + - name: Install essential packages + yum: + name: + - gcc + - gcc-c++ + - git + state: present + + - name: enable Nginx using amazon-linux-extras + command: amazon-linux-extras enable nginx1.12 + + - name: Install nginx + yum: + name: nginx + state: present + + - name: Upgrade pip + command: pip3 install --upgrade pip + + - name: Install virtualenv using pip + command: pip3 install virtualenv + + - name: Create application directory + file: + path: "{{ app_directory }}" + state: directory + mode: '0755' + + - name: Remove existing virtual environment + file: + path: "{{ app_directory }}/venv" + state: absent + + - name: Create a virtual environment using virtualenv + command: virtualenv -p python {{ app_directory }}/venv + + - name: Install Python packages in the virtual environment + pip: + name: "{{ item }}" + virtualenv: "{{ app_directory }}/venv" + loop: "{{ requirements }}" + + - name: Copy application files to the server + copy: + src: "{{ item }}" + dest: "{{ app_directory }}" + mode: '0644' + with_fileglob: + - "../../CloudRender/backend/*.py" + + - name: Setup Gunicorn systemd service + template: + src: gunicorn.service.j2 + dest: /etc/systemd/system/gunicorn.service + notify: + - Reload systemd + - Restart Gunicorn + + - name: Setup Nginx configuration + template: + src: nginx.conf.j2 + dest: /etc/nginx/conf.d/my_flask_app.conf + notify: + - Restart nginx + + - name: Ensure nginx is running and enabled + systemd: + name: nginx + state: started + enabled: true + + handlers: + - name: Restart nginx + systemd: + name: nginx + state: restarted + + - name: Reload systemd + systemd: + daemon_reload: yes + + - name: Restart Gunicorn + systemd: + name: gunicorn + state: restarted + diff --git a/ansible/setup-docker.yml b/ansible/setup-docker.yml new file mode 100644 index 0000000..e4cac93 --- /dev/null +++ b/ansible/setup-docker.yml @@ -0,0 +1,27 @@ +- name: Install Docker on EC2 instances + hosts: ec2_instances + become: yes + + tasks: + - name: Update yum package manager + yum: + name: '*' + state: latest + + - name: Install Docker + yum: + name: docker + state: present + + - name: Start and enable Docker service + service: + name: docker + state: started + enabled: yes + + - name: Add ec2-user to the docker group + user: + name: ec2-user + groups: docker + append: yes + diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..d956ad3 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,283 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" // Defines the provider source + version = "~> 5.0" + } + } +} + +# Configure the AWS Provider +provider "aws" { + region = "us-east-1" +} + +data "aws_ami" "amazon-linux-2" { + most_recent = true + + + filter { + name = "owner-alias" + values = ["amazon"] + } + + + filter { + name = "name" + values = ["amzn2-ami-hvm*"] + } +} + +# Create a VPC +resource "aws_vpc" "my_vpc" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = "DistribtedImageProcessing VPC" + } +} + +# Create an Internet Gateway +resource "aws_internet_gateway" "my_igw" { + vpc_id = aws_vpc.my_vpc.id + + tags = { + Name = "myigw" + } +} + +# Create a Public Subnet +resource "aws_subnet" "public_subnet" { + vpc_id = aws_vpc.my_vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = "us-east-1a" + + tags = { + Name = "mysubnet" + } +} + +# Create a Route Table +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.my_vpc.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.my_igw.id + } + + tags = { + Name = "myrt" + } +} + +# Associate the Route Table with the Public Subnet +resource "aws_route_table_association" "public_rt_assoc" { + subnet_id = aws_subnet.public_subnet.id + route_table_id = aws_route_table.public_rt.id +} + +# Create a Security Group for the EC2 instance +resource "aws_security_group" "ec2_sg" { + name = "EC2 Security Group" + description = "Allow inbound SSH, HTTP and HTTPS traffic" + vpc_id = aws_vpc.my_vpc.id + + # Allow SSH + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + + # Allow HTTP + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + + # Allow HTTPS + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "EC2 Security Group" + } +} + +# Create an EC2 instance +resource "aws_instance" "my_instance1" { + ami = "${data.aws_ami.amazon-linux-2.id}" + instance_type = "t2.micro" + subnet_id = aws_subnet.public_subnet.id + vpc_security_group_ids = [aws_security_group.ec2_sg.id, aws_security_group.ec2_sg.id] + associate_public_ip_address = true + key_name = aws_key_pair.deployer_key.key_name + iam_instance_profile = aws_iam_instance_profile.ec2_s3_access_profile.name + + tags = { + Name = "image_manipulator1" + } +} + +resource "aws_instance" "my_instance2" { + ami = "${data.aws_ami.amazon-linux-2.id}" + instance_type = "t2.micro" + subnet_id = aws_subnet.public_subnet.id + vpc_security_group_ids = [aws_security_group.ec2_sg.id, aws_security_group.ec2_sg.id] + associate_public_ip_address = true + key_name = aws_key_pair.deployer_key.key_name + iam_instance_profile = aws_iam_instance_profile.ec2_s3_access_profile.name + + tags = { + Name = "image_manipulator2" + } +} + +resource "aws_key_pair" "deployer_key" { + key_name = "deployer-key" + public_key = file("~/keypair_amazon/deployer_key.pub") +} + + +# IAM role for the EC2 instance +resource "aws_iam_role" "ec2_s3_access_role" { + name = "ec2_s3_access_role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ec2.amazonaws.com" + } + }, + ] + }) +} + +# IAM policy to grant S3 read and write permissions +resource "aws_iam_policy" "s3_read_write_policy" { + name = "s3_read_write_policy" + description = "Policy that allows S3 read and write access" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:ListBucket", + ], + Effect = "Allow", + Resource = [ + "${aws_s3_bucket.original_images.arn}/*", + "${aws_s3_bucket.processed_images.arn}/*" + ] + }, + ] + }) +} + +# Attach the IAM policy to the role +resource "aws_iam_role_policy_attachment" "ec2_s3_access_policy_attachment" { + role = aws_iam_role.ec2_s3_access_role.name + policy_arn = aws_iam_policy.s3_read_write_policy.arn +} + +# Create an IAM instance profile for the EC2 instance +resource "aws_iam_instance_profile" "ec2_s3_access_profile" { + name = "ec2_s3_access_profile" + role = aws_iam_role.ec2_s3_access_role.name +} + +resource "aws_s3_bucket" "original_images" { + bucket = "original-images-${random_pet.name.id}" // Ensures global uniqueness + + tags = { + Purpose = "Original Image Uploads" + } +} + + +resource "aws_s3_bucket" "processed_images" { + bucket = "processed-images-${random_pet.name.id}" // Ensures global uniqueness + + tags = { + Purpose = "Processed Image Downloads" + } +} + +resource "aws_s3_bucket_public_access_block" "original_images_public_acssess" { + bucket = aws_s3_bucket.original_images.id + + block_public_acls = false + block_public_policy = false +} +resource "aws_s3_bucket_public_access_block" "processed_images_public_acssess" { + bucket = aws_s3_bucket.processed_images.id + + block_public_acls = false + block_public_policy = false +} + + +resource "aws_s3_bucket_policy" "processed_images_allow_read_policy" { + bucket = aws_s3_bucket.processed_images.id + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = "*", + Action = "s3:GetObject", + Resource = [ + "${aws_s3_bucket.processed_images.arn}/*" + ] + }, + ] + }) +} + +resource "aws_s3_bucket_policy" "original_images_allow_read_policy" { + bucket = aws_s3_bucket.original_images.id + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = "*", + Action = "s3:GetObject", + Resource = [ + "${aws_s3_bucket.original_images.arn}/*" + ] + }, + ] + }) +} + +resource "random_pet" "name" { + length = 2 +} -- cgit v1.2.3