Automate Python Flask Deployment to the AWS Cloud

October 20, 2022 By Mark Otto 0

In our previous post, we demonstrated how to use open source tools to deploy an application to the AWS cloud. In this post, we expand on those concepts by introducing additional AWS services that demonstrate how to manage and deploy an application in the AWS Cloud. The updated workflow includes the following additions: Continuous Integration / Continuous Deployment (CI / CD) pipeline, applications tests, Blue / Green application deployment pattern, and updated Terraform modules. This additional functionality enables developers to automatically test and deploy new versions of the Flask application. Using Python Flask, Terraform, and Docker in conjunction with several AWS services, you configure a CI/CD pipeline for deploying a Python Flask application.

After going through this exercise, readers will know how to use AWS services and open source tools for managing an open source application in the AWS Cloud.

Prerequisites

Solution Overview

There is a sample Python Flask application that is deployed by AWS CodePipeline. The pipeline uses Docker to build and deploy the image to the Amazon Elastic Container Registry (Amazon ECR). Unit tests are run during the Docker image build. Next, the appspec file is updated to point to the latest ECS task Definition. AWS CodeDeploy kicks off a Blue / Green deployment and deploys the latest Docker image to Elastic Container Service running on EC2 instances. Once traffic is shifted to the new version of the application, CodeDeploy completes successfully and the latest version of the Flask application is running on ECS.

Python Flask deployment diagram

The pipeline deploys the Flask application:

  1. The developer pushes code to the main branch.
  2. An update to the main branch invokes the pipeline.
  3. The pipeline clones the AWS CodeCommit repository.
  4. Docker builds the container image, runs unit tests, and assigns tags.
  5. Docker pushes the image to Amazon ECR.
  6. CodeDeploy Blue / Green deployment with the latest version of Flask app deployed to ECS

Application Overview

Python Flask is the foundation for the rest API. Python Flask is a micro framework for building web applications. The Flask application has has a back end database of Amazon DynamoDB. The Flask API defines two routes. The first route maps to /, and the second maps to /v1/bestmusic/90s/artist. These are the only paths recognized by the application. If you enter any other URL when accessing the API, you will receive an error message. You can define specific error responses in the API routes. For example, referencing the Python functions in the get_artist method, “Artist does not exist” is the response returned when a users requests an artist that is not present in the DynamoDB table (musicTable). The create_artist method posts an artist and song to your DynamoDB.

Unit and functional tests are also included as part of this workflow. PyUnit is used for the unit tests. The below diagram depicts the application architecture.

Python Flask application architecture diagram

Deployment

You now configure and deploy the AWS services:

  1. Clone the demo code:

git clone https://github.com/aws-samples/deploy-python-flask-microservices-to-aws-using-open-source-tools-part2

  1. Change directory into the terraform/ directory:

cd terraform/

  1. Create the CodeCommit repository:

make configure-repo

You should see output similar to the following:

INFO: Creating CodeCommit repository.
{     repositoryMetadata: {         accountId: 01234567890,         repositoryId: 1b807cbf-c184-4e9c-823b-3262973b39cc,         repositoryName: FlaskDemoRepo-01234567890,         repositoryDescription: Flask demo application repo.,         lastModifiedDate: 2022-05-12T11:40:09.129000-04:00,         creationDate: 2022-05-12T11:40:09.129000-04:00,         cloneUrlHttp: https://git-codecommit.us-east-1.amazonaws.com/v1/repos/FlaskDemoRepo-01234567890,         cloneUrlSsh: ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/FlaskDemoRepo-01234567890,         Arn: arn:aws:codecommit:us-east-1:1234567890:FlaskDemoRepo-01234567890     } }
INFO: Successfully created CodeCommit repository.
INFO: The CodeCommit HTTP clone URL is https://git-codecommit.us-east-1.amazonaws.com/v1/repos/FlaskDemoRepo-01234567890

  1. Clone the AWS CodeCommit repository, you may be prompted for your CodeCommit username / password:

make clone

The output should look like the following:

Cloning into 'FlaskDemoRepo-01234567890-'...
warning: You appear to have cloned an empty repository.

  1. Configure the CodeCommit repository:

make upload-codecommit

The output should look like the following:

cd /home/ssm-user/private-rest-api/terraform/FlaskDemoRepo-01234567890 && \
git checkout -b main && \
cd /home/ssm-user/private-rest-api && \
tar czf demo-code.tar.gz app Dockerfile appspec.yaml buildspec.yml && \
tar -tvf demo-code.tar.gz && \
mv /home/ssm-user/private-rest-api/demo-code.tar.gz /home/ssm-user/private-rest-api/terraform/FlaskDemoRepo-01234567890 && \
cd /home/ssm-user/private-rest-api/terraform/FlaskDemoRepo-01234567890 && \
tar -xvf /home/ssm-user/private-rest-api/terraform/FlaskDemoRepo-01234567890/demo-code.tar.gz && \
rm /home/ssm-user/private-rest-api/terraform/FlaskDemoRepo-01234567890/demo-code.tar.gz && \
git add . && \
git commit -m "Configuring repo." && \
git push -u origin main
Switched to a new branch 'main'
drwxr-xr-x ssm-user/ssm-user 0 2022-07-13 00:47 app/
drwxr-xr-x ssm-user/ssm-user 0 2022-07-13 00:47 app/tests/
drwxr-xr-x ssm-user/ssm-user 0 2022-07-12 19:25 app/tests/lambda-tests/
-rw-r--r-- ssm-user/ssm-user 1970 2022-07-12 19:25 app/tests/lambda-tests/functional_test.py
-rw-r--r-- ssm-user/ssm-user  918 2022-07-12 19:25 app/tests/lambda-tests/functional_test.zip
-rw-r--r-- ssm-user/ssm-user  194 2022-07-13 00:47 app/tests/test.py
-rw-r--r-- ssm-user/ssm-user 1319 2022-07-13 00:47 app/app.py
-rw-r--r-- ssm-user/ssm-user  399 2022-07-12 19:25 Dockerfile
-rw-r--r-- ssm-user/ssm-user  291 2022-07-12 19:25 appspec.yaml
-rw-r--r-- ssm-user/ssm-user  957 2022-07-12 19:25 buildspec.yml
app/
app/tests/
app/tests/lambda-tests/
app/tests/lambda-tests/functional_test.py
app/tests/lambda-tests/functional_test.zip
app/tests/test.py
app/app.py
Dockerfile
appspec.yaml
buildspec.yml
[main (root-commit) 3e37158] Configuring repo.
7 files changed, 179 insertions(+)
create mode 100644 Dockerfile
create mode 100644 app/app.py
create mode 100644 app/tests/lambda-tests/functional_test.py
create mode 100644 app/tests/lambda-tests/functional_test.zip
create mode 100644 app/tests/test.py
create mode 100644 appspec.yaml
create mode 100644 buildspec.yml
Username for 'https://git-codecommit.us-east-1.amazonaws.com': awsjoe-at-01234567890
Password for 'https://[email protected]':
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 2 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (12/12), 3.73 KiB | 3.73 MiB/s, done.
Total 12 (delta 0), reused 0 (delta 0), pack-reused 0
To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/FlaskDemoRepo-01234567890
* [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

  1. Deploy the AWS Services:

make deploy-infra

This will result in great deal of output ending in:

Apply complete! Resources: 118 added, 1 changed, 0 destroyed.

Outputs:

aws_lb_dns_name = "ecsalb-123456789.us-east-1.elb.amazonaws.com"

  1. In the AWS Console open CodePipelinePipelines and select flask-demo-pipeline:

    Select Flask demo pipeline

  2. Scroll down to the Deploy phase and click Details:

    deploy phase details screenshot

  3. Wait for the deployment to complete, it should take around 8 minutes:

    AWS Console screen showing deployment is complete

  4. Back in your terminal, run the following command to list the Terraform outputs:

terraform output

You should see output similar to the following:

aws_lb_dns_name = "ecsalb-123456789.us-east-1.elb.amazonaws.com"

  1. Run the following command using the aws_lb_dns_name from the previous step to test that the application is functioning properly:

curl http://ecsalb-123456789.us-east-1.elb.amazonaws.com/

You should see output similar to the following:

Hello World!

Update the application

Now that you successfully deployed the Flask application. Test the CI / CD pipeline by making a change to the application. You now update the application and tests which triggers the pipeline to deploy the updated application.

  1. Change directory back to the root of the repository and run the following command to change directory to the CodeCommit repository directory:

cd terraform/FlaskDemoRepo-01234567890

  1. Open the app/app.py file in the text editor of your choice. Update the base_url text to “Hello Updated World!”:

import os
import boto3
from flask import Flask, jsonify, request
app = Flask(__name__)
client = boto3.client('dynamodb', region_name='us-east-1')
dynamoTableName = 'musicTable'
base_url = 'Hello Updated World!'
@app.route("/")
def hello():
return base_url

  1. Update the unit test to reflect the new base URL path. Open app/tests/test.py and update the file so that it looks like the following:

# test.py
from app import app

def test_base_route():
response = app.test_client().get('/')
assert response.status_code == 200
assert response.data.decode('utf-8') == 'Hello Updated World!'

  1. Change directory back to the terraform/ directory and update the CodeCommit repository by running the following command:

make update-flask-app

The output should look like the following:

[main cca0ab8] Updating Flask base path to Hello Updated World.
2 files changed, 2 insertions(+), 2 deletions(-)
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 2 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 619 bytes | 619.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0), pack-reused 0
To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/FlaskDemoRepo-01234567890
3e37158..cca0ab8  main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

  1. In the AWS Console open CodePipelinePipelines and select flask-demo-pipeline:

    AWS Console screenshot of the Flask pipeline

  2. Scroll down to the Deploy phase and click Details:

    Flask deploy details screenshot in the AWS Console

  3. Wait for the deployment to complete, it should take around 8 minutes:

    AWS Console deploy complete screenshot

  4. Back in your terminal, run the following command from the terraform/ directory to list the Terraform outputs:

terraform output

You should see output similar to the following:

aws_lb_dns_name = "ecsalb-01234567890.us-east-1.elb.amazonaws.com"

  1. Run the following command using the aws_lb_dns_name from the previous step to test that the application is functioning properly:

curl http://ecsalb-01234567890.us-east-1.elb.amazonaws.com/

You should see output similar to the following:

Hello World!

Cleanup

To delete all of the AWS infrastructure you previously deployed, run the following command from the root module directory:

make destroy

The command will result in a great deal of output. The final result should look like the following:

Destroy complete! Resources: 119 destroyed.

Conclusion

This post demonstrated how to use AWS services and open source tools to automate the deployment of a Python Flask application to the AWS Cloud. Using CodePipeline, you created a CI/CD pipeline and updated and deployed a Python Flask application. You then verified a successful pipeline deployment by testing the Flask application. Thanks for reading!