Deploying Python-Flask Application in AWS ECS(Fargate), RDS(Postgres) provisioned using Terraform

Vijaykumar
4 min readNov 28, 2020

--

This article is accompanied by working code examples of a Python-Flask Application, Docker Image & Terraform code.

Architecture

AWS ECS(Fargate) & RDS(Postgres) Architecture
AWS ECS(Fargate) & RDS(Postgres) Architecture

Tasks

  1. Create a Flask Application Using Python & Postgres as backend database.
  2. Dockerize the Flask Application using docker.
  3. Create a Terraform template to provision & deploy docker image in AWS

Terraform enables you to create, change & improve infrastructure using code. By the end of this section we will be able to run one command to deploy entire application which gives us:

  1. A Virtual Private Cloud with Public and private subnets
  2. Internet Gateway to contact the outer world
  3. Security groups for RDS Postgres and for ECS
  4. A Load balancer distributing traffic between containers & database
  5. RDS Postgres instance
  6. Auto scaling group for ECS cluster with launch configuration
  7. ECS cluster with task and service definition

Let’s get started

Let’s begin to create a project and setting-up the file structure we want to use.

mkdir terraform-aws-ecs-postgres-docker-flask-example && cd terraform-aws-ecs-postgres-docker-flask-example

Next with the root project we’ll add a .gitignore file with the following comments

# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log

# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
# .tfvars files are managed as part of configuration and so should be included in
# version control.
#
# example.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

.idea/

Flask Application using Python

Within the root of the project add flask-app directory. Inside the directory let’s add the following files:

  • wsgi.py
  • application.py
  • config.py
  • requirements.txt
  • add common directory and add following files
  • model.py
  • view.py
  • __init__.py
Project Structure

Added the following Code:

Flask is required to run this application, so I have created requirements.txt file. This will hold the packages which need to be installed inside the container.

The Docker Part

Let’s take a look at the contents of Dockerfile and entrypoint.sh.

Basically, Dockerfile is the package required to create an image and then deploy it.

entrypoint.sh file executes flask application. write the following:

To build this image, I’ll run:

$ docker build --file Dockerfile  --tag "terraform-flask-postgres-docker-example:test" .

And to run the container, I’ll run:

$ docker run --name my-container --hostname=0.0.0.0 \
--publish-all=true --privileged=true -t -i -d \
-e "APP_PORT=5000" \
-e "APP_HOME=/usr/src/app/" \
-e "APP_SECRET_KEY=nC5CfQ@d2jNvqrba" \
-e "FLASK_APP=app" \
-e "FLASK_ENV=dev" \
-e "POSTGRES_DATABASE=postgresdb" \
-e "POSTGRES_ENDPOINT=xxxx:5432" \
-e "POSTGRES_PASSWORD=xxx" \
-e "POSTGRES_USER=postgres" \
"terraform-flask-postgres-docker-example:latest"

Next, here to log in our docker cli to tag our image and push to Docker Hub.

$ docker login -u ${username} -p ${password}

Replace ${username} & ${password} with your credentials.

After logging in, we will tag our image and push to Docker Hub, as follows:

$ docker tag "terraform-flask-postgres-docker-example:test" "terraform-flask-postgres-docker-example:latest"$ docker push "terraform-flask-postgres-docker-example:latest"

The Terraform Part

ECS is Amazon’s Elastic Container Service. That’s how you get docker containers running in the cloud.

Within the root of the project add a terraform/ directory. As you’ll see next - Terraform config files use the file extension .tf.

Inside the directory you just made let’s add the following files:

  • provider.tf
  • vpc.tf
  • internet_gateway.tf
  • route_table.tf
  • subnets.tf
  • route_table_association.tf
  • security_groups.tf
  • db_subnet.tf
  • postgres_db.tf
  • alb.tf
  • ecs.tf
  • template_file.tf
  • task_definition.tf
  • ecs_service.tf
  • variables.tf
  • output.tf
  • task_definition.json.tpl

I tried breaking the code up into multiple files handling each of the different parts of infrastructure. Hopefully, this is for more clarity, however it will work just fine if you have all the code in just one big main file as well.

The first thing we’ll do is specify the provider. This can be done in different ways but here we’re telling Terraform our provider is AWS and that it can find our credentials in $HOME/.aws/credentials which is the default location for AWS credentials on Mac and Linux & Windows.

This gives us back the load balancer DNS name (A record) & Postgres endpoint after we deploy.

Time to run:

terraform init terraform/ in your terminal in the root of your project. This gets it all set up and ready to apply.

Finally, run terraform apply terraform/ to get this deployed! It will refresh state for all your resources and show you what will be created/removed/updated. It then prompts you to reply with “yes” if you want to perform the actions. That’s it! You will see everything getting provisioned. This will take a few minutes.

When output returns load balancer url, copy the url and hit enter. You should see the JSON returned from the initial block in your deployed flask-app.

(Wow! Also remember to delete the things created above! using terraform destroy terraform/)

Source Code: https://github.com/jvk243/terraform-aws-ecs-postgres-docker-flask-example.git

The rest will be done next time!

--

--