End to end Flask application deploy using code pipeline
Hitaishi Sengupta
Serving NP || Immediate Joiner || AWS Certified || AWS DevOps Engineer || Cloud Engineer || CI/CD, Automation and AWS Cloud Infrastructure ||Technical Blogger || Terraform || Jenkins
Contents
2. Prerequisite
3. Steps
4. Output
5. Conclusion
1. Project Overview
2. Prerequisite
3. Steps
3.1 Write one flask application code
To deploy any flask app in ECS , need to write one flask application code. I used below sample code for this deployment.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
? ? ? ? return 'Hello New World'
# main driver function
if __name__ == '__main__':
? ? ? ? app.run(host='0.0.0.0',port=80)k
3.2. Write Dockerfile
As we are going to deploy app in ECS , need to create flask image for this deployment. Dockerfile is needed to create images automatically during pipeline. Pipeline will use this Dockerfile to push image to ECR and use it in our ECS Services.
FROM python:3.8-slim-buster
WORKDIR /python-docker
RUN pip3 install flask
COPY a.py .
EXPOSE 80
CMD [ "python3", "a.py"]
3.3. Terraform code to deploy infrastructure
The following are the required steps to start working with Terraform on AWS:
In codecommit repository add below files.
resource "aws_vpc" "main"
? cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "main" {
? vpc_id = aws_vpc.main.id
? cidr_block? ? ? ? ? ? ? = "10.0.1.0/24"
? availability_zone? ? ? ?= "us-east-1a"
? map_public_ip_on_launch = true
? tags = {
? ? Name = "TF-SG"
? }
}
resource "aws_subnet" "main2" {
? vpc_id? ? ? ? ? ? ? ? ? = aws_vpc.main.id
? cidr_block? ? ? ? ? ? ? = "10.0.2.0/24"
? availability_zone? ? ? ?= "us-east-1b"
? map_public_ip_on_launch = true
? tags = {
? ? Name = "TF-SG"
? }
}
resource "aws_internet_gateway" "gw" {
? vpc_id = aws_vpc.main.id
? tags = {
? ? Name = "TF-GW"
? }
}
resource "aws_route_table" "rt" {
? vpc_id = aws_vpc.main.id
? route {
? ? cidr_block = "0.0.0.0/0"
? ? gateway_id = aws_internet_gateway.gw.id
? }
? tags = {
? ? Name = "TF-RT"
? }
}
resource "aws_route_table_association" "a" {
? subnet_id? ? ? = aws_subnet.main.id
? route_table_id = aws_route_table.rt.id
}
resource "aws_route_table_association" "b" {
? subnet_id? ? ? = aws_subnet.main2.id
? route_table_id = aws_route_table.rt.id
}
{
resource "aws_lb" "test"
? name? ? ? ? ? ? ? ?= "alb"
? internal? ? ? ? ? ?= false
? load_balancer_type = "application"
? security_groups? ? = [aws_security_group.allow_tls.id]
? subnets? ? ? ? ? ? = [aws_subnet.main.id, aws_subnet.main2.id]
? ip_address_type? ? = "ipv4"
}
resource "aws_lb_target_group" "target_group" {
? name? ? ? ? = "tg"
? port? ? ? ? = 80
? protocol? ? = "HTTP"
? target_type = "ip"
? vpc_id? ? ? = aws_vpc.main.id
#? ?health_check {
#? ? ?healthy_threshold? ?= "3"
#? ? ?interval? ? ? ? ? ? = "300"
#? ? ?protocol? ? ? ? ? ? = "HTTP"
#? ? ?matcher? ? ? ? ? ? ?= "200"
#? ? ?timeout? ? ? ? ? ? ?= "3"
#? ? ?path? ? ? ? ? ? ? ? = "/"
#? ? ?unhealthy_threshold = "2"
#? ?}
}
resource "aws_lb_listener" "listener" {
? load_balancer_arn = aws_lb.test.id
? port? ? ? ? ? ? ? = "80"
? protocol? ? ? ? ? = "HTTP"
? default_action {
? ? type? ? ? ? ? ? ?= "forward"
? ? target_group_arn = aws_lb_target_group.target_group.id
? }
}
{
resource "aws_ecs_cluster" "foo"
? name = "white-hart"
? setting {
? ? name? = "containerInsights"
? ? value = "enabled"
? }
}
resource "aws_cloudwatch_log_group" "log-group" {
? name = "flaskapp-logs"
}
resource "aws_ecs_task_definition" "aws-ecs-task" {
? family = "task"
? container_definitions = <<DEFINITION
? [
? ? {
? ? ? "name": "container",
? ? ? "image": "582662663083.dkr.ecr.us-east-1.amazonaws.com/faskapp-new:latest",
? ? ? "portMappings": [
? ? ? ? {
? ? ? ? ? "containerPort": 80,
? ? ? ? ? "hostPort": 80
? ? ? ? }
? ? ? ],
? ? ? "logConfiguration": {
? ? ? ? "logDriver": "awslogs",
? ? ? ? "options": {
? ? ? ? ? "awslogs-group": "${aws_cloudwatch_log_group.log-group.id}",
? ? ? ? ? "awslogs-region": "${var.aws_region}",
? ? ? ? ? "awslogs-stream-prefix": "flaskapp-"
? ? ? ? }
? ? ? },
? ? ? "cpu": 256,
? ? ? "memory": 512,
? ? ? "networkMode": "awsvpc"
? ? }
? ]
? DEFINITION
? requires_compatibilities = ["FARGATE"]
? network_mode? ? ? ? ? ? ?= "awsvpc"
? memory? ? ? ? ? ? ? ? ? ?= "512"
? cpu? ? ? ? ? ? ? ? ? ? ? = "256"
? execution_role_arn? ? ? ?= aws_iam_role.ecsTaskExecutionRole.arn
? task_role_arn? ? ? ? ? ? = aws_iam_role.ecsTaskExecutionRole.arn
}
resource "aws_ecs_service" "aws-ecs-service" {
? name? ? ? ? ? ? ? ? ?= "ecs-service"
? cluster? ? ? ? ? ? ? = aws_ecs_cluster.foo.id
? task_definition? ? ? = aws_ecs_task_definition.aws-ecs-task.id
? launch_type? ? ? ? ? = "FARGATE"
? scheduling_strategy? = "REPLICA"
? desired_count? ? ? ? = 5
? force_new_deployment = true
? network_configuration {
? ? subnets? ? ? ? ? = [aws_subnet.main.id, aws_subnet.main2.id]
? ? assign_public_ip = true
? ? security_groups = [
? ? ? aws_security_group.allow_tls.id,
? ? ]
? }
? load_balancer {
? ? target_group_arn = aws_lb_target_group.target_group.arn
? ? container_name? ?= "container"
? ? container_port? ?= 80
? }
}
{
provider "aws"
? region? = var.aws_region
??
}
variable "aws_region" {
? default = "us-east-1"
}{
data "aws_iam_policy_document" "assume_role_policy"
? statement {
? ? actions = ["sts:AssumeRole"]
? ? principals {
? ? ? type? ? ? ? = "Service"
? ? ? identifiers = ["ecs-tasks.amazonaws.com"]
? ? }
? }
}
resource "aws_iam_role" "ecsTaskExecutionRole" {
? name? ? ? ? ? ? ? ?= "execution-task-role"
? assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}
resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_policy" {
? role? ? ? ?= aws_iam_role.ecsTaskExecutionRole.name
? policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}{
resource "aws_security_group" "allow_tls" {
? name? ? ? ? = "allow_tls"
? description = "Allow TLS inbound traffic"
? vpc_id? ? ? = aws_vpc.main.id
? ingress {
? ? description = "TLS from VPC"
? ? from_port? ?= 80
? ? to_port? ? ?= 80
? ? protocol? ? = "tcp"
? ? cidr_blocks = ["0.0.0.0/0"]
? }
? egress {
? ? from_port? ?= 0
? ? protocol? ? = "-1"
? ? to_port? ? ?= 0
? ? cidr_blocks = ["0.0.0.0/0"]
? }
}
terraform
? ? backend "s3" {
? ? ? ? region = "us-east-1"
? ? ? ? profile = "default"
? ? ? ? key = "codebuild/ecsnew.tfstate"
? ? ? ? bucket = "rahul030198"
? ? ? ? dynamodb_table? = "terraform-locking-state"?
? ? }
}{
领英推荐
3.4. Yaml file for each Codebuild project
version: 0.2
env:
? variables:
? ? ACCOUNT_ID: "582662663083"
? ? REPO_NAME: "faskapp-new"
? ? AWS_REGION: "us-east-1"
phases:?
? install:
? ? commands:?
? ? - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
? ? - echo $COMMIT_HASH
? ? - apt update -y
? ? - apt install docker.io -y
? ? - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2 &
? ? - timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
? pre_build:?
? ? commands:?
? ? - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
? build:?
? ? commands:?
? ? - docker build -t $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO_NAME:latest -t $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO_NAME:$COMMIT_HASH .
? post_build:?
? ? commands:?
? ? - docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO_NAME:latest
? ? - docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO_NAME:$COMMIT_HASH2
version: 0.2
phases:
? install:
? ? commands:
? ? ? - "apt install unzip -y"
? ? ? - "wget https://releases.hashicorp.com/terraform/1.0.7/terraform_1.0.7_linux_amd64.zip"
? ? ? - "unzip terraform_1.0.7_linux_amd64.zip"
? ? ? - "mv terraform /usr/local/bin/"
? pre_build:
? ? commands:
? ? ? - terraform init?
? build:
? ? commands:
? ? ? - terraform plan
2
version: 0.2
phases:
? install:
? ? commands:
? ? ? - "apt install unzip -y"
? ? ? - "wget https://releases.hashicorp.com/terraform/1.0.7/terraform_1.0.7_linux_amd64.zip"
? ? ? - "unzip terraform_1.0.7_linux_amd64.zip"
? ? ? - "mv terraform /usr/local/bin/"
? pre_build:
? ? commands:
? ? ? - terraform init??
? build:
? ? commands:
? ? ? - terraform apply -auto-approve2
version: 0.2
phases:
? install:
? ? commands:
? ? ? - "apt install unzip -y"
? ? ? - "wget https://releases.hashicorp.com/terraform/1.0.7/terraform_1.0.7_linux_amd64.zip"
? ? ? - "unzip terraform_1.0.7_linux_amd64.zip"
? ? ? - "mv terraform /usr/local/bin/"
? pre_build:
? ? commands:
? ? ? - terraform init?
? build:
? ? commands:
? ? ? - terraform destroy -auto-approve2
3.5. Create build project for each stages : Now we need to configure our build project. Go to CodeBuild click on create build project and do the following configuration.
1. Build project for image push :
Give Source provider as codecommit and repository which you have already created. Branch as Reference type and give your corresponding branch.
3.6. Create pipeline: The AWS CodePipeline will be used for CI/CD (Continuous Integration/Continuous Delivery). Our pipeline consists of five stages — Sources, image push, terraform-plan, terraform apply, approval for destroy , terraform destroy
4. Output : Able to access ECS application using load balancer DNS name
5. Conclusion : Deploying a Flask application to ECS can provide a powerful and reliable platform for running your application in the cloud, and can help you streamline the process of deploying and managing your application at scale.
This was pretty long, but I hope the extra details help anyone wanting to do something similar, CI/CD fully integrated into AWS.
Any thoughts? Drop a comment! Like the article? Clap a few times below and let me know how much you enjoyed it .
Thank you!
AWS DevOps | SRE | Senior Software Engineer | CKA | GitOps | 2x AWS certified | 3x Redhat Certified( EX294+EX180+EX280) | Python Backend Developer | AWS DevOps Engineer | FastAPI | Flask | Trainer | Codechef
1 年Useful content
Data Analyst @ BT | | ExTCSer | | Business Analyst | | Sql & plsql Developer
1 年Awesome
Ex-DevOps Engineer | Full Time Entrepreneur
1 年Useful Content