Deploy Flask Application on AWS EC2 and PostgreSQL

November 18, 2022

/

17 min read

--- views

•

--- comments

Last Updated December 03, 2022

Introduction

This tutorial will show you how to deploy a simple Python Flask Application on AWS EC2 step by step.

Here is the link to the repository of the final project: https://github.com/arlenxuzj/flask-todos

This tutorial assumes that you have:

  • A NEW AWS Account or An AWS Account CAN use Free Tier services - See Free Tier FAQs for more information.
  • Git installed locally – See the installation guides for Git.
  • Python version 3.7+ (Recommend 3.10+) installed locally – See the installation guides for OS X, Windows, and Linux.

This tutorial assumes that you know:

  • Basic knowledge of Python.
  • Basic knowledge of Flask.

Get Started

Download the Source Code

First, we need to download the source code from GitHub. Open Terminal and run the next line command:

$ git clone [email protected]:arlenxuzj/flask-todos.git

Open the Project Directory

$ cd flask-todos

Create Virtual Environment

Use the following commands to create and activate the virtual environment:

# Create a Python Virtual Environment called flask-app
$ python -m venv venv

# Activate flask-app Virtual Environment
$ source venv/bin/activate

# upgrade pip
$ pip install --upgrade pip

Install the Dependencies

$ pip install -r requirements.txt

Install PostgreSQL

For Windows users, you can check the download options here.

For Mac users, you can check the download options here.

We will use the Interactive installer by EDB, which is mentioned in the PostgreSQL Download Page to install PostgreSQL (works for both Windows and Mac users).

Run Application on Local Machine

Create Local PostgreSQL Database and Table

  1. Open the pgAdmin 4 application downloaded with the PostgreSQL installer.

  2. Right Click Databases - Select Create - Click Database....

    create-postgresql-database-1
  3. Create a new database called flask-todos and Save.

    create-postgresql-database-2
  4. Right Click flask-todos - Click Generate ERD.

    create-postgresql-database-3
  5. You can design the ERD diagram and generate the SQL script for the database in this ERD Panel. Now click Load Project to load the ERD diagram from the flask-todos/model/todos.pgerd file.

    create-postgresql-database-4
  6. Click Generate SQL and click Execute/Refresh to create the todos table in the new Query Panel.

    create-postgresql-database-5
    create-postgresql-database-6
  7. Clear the content in the Query Panel, copy and paste the following SQL, and execute the SQL to insert some sample data:

    INSERT INTO "public"."todos" ("id", "title", "completed") VALUES
    (1, 'delectus aut autem', 'f'),
    (2, 'quis ut nam facilis et officia qui', 'f'),
    (3, 'fugiat veniam minus', 'f'),
    (4, 'et porro tempora', 'f'),
    (5, 'illo expedita consequatur quia in', 'f');
    
    create-postgresql-database-7
  8. Go to flask-todos - Schemas - public - Tables - Right Click todos - Select View/Edit Data - Click All Rows to view the data inserted.

    create-postgresql-database-8
    create-postgresql-database-9

Connect Database in Flask Application

  1. Open flask-todos in your favorite IDE, and open the .env file.

  2. Open Terminal and run the following command to generate a Flask Secret key:

    $ python -c 'import secrets; print(secrets.token_hex())'
    
  3. Copy the generated key and replace the FLASK_SECRET_KEY field with it in the .env file.

  4. Replace the DB_PASSWORD fields in the .env file with your PostgreSQL postgres user password (NOT pgAdmin 4 master password).

    create-postgresql-database-10
  5. Run Application by the following command in the Terminal:

    $ python src/app.py
    

Configure AWS EC2 Server

Create Server

  1. In the AWS Home Page, type EC2 in the search bar and click Instances in the EC2 area to enter the EC2 Instances Dashboard.

    create-server-1
  2. Click Launch Instance to create a new EC2 Instance.

    create-server-2
  3. Type flask-todos in the Name field. Select Ubuntu Server 22.04 LTS (HVM), SSD Volume Type in the Application Machine Images (AMI) field. Select t2.micro in the Instance type field. Click.

    create-server-3
  4. Click Create new key pair to create a new key pair. Type flask-todos in the Key pair name field. Click Create Key Pair to download the key pair file flask-todos.pem.

    create-server-4
    create-server-5
  5. In Network settings, select Create new security group. Check Allow SSH traffic and ONLY allow SSH traffic from your IP address. Check Allow HTTP traffic and Allow HTTPS traffic.

    create-server-6
  6. Click Launch instance on the right side of the page.

Connect to Server

  1. Go back to the EC2 Instances Dashboard and Click the Instance ID to enter the Instance Details Page.

  2. Copy the Public IPv4 DNS address.

    connect-to-server-1
  1. Run command in the Terminal to change the permission of the key pair file:

    $ chmod 400 path_to_key_pair/flask-todos.pem
    
  2. Run command in the Terminal to connect to the server:

    $ ssh -i path_to_key_pair/flask-todos.pem ubuntu@your-instance-public-ipv4-dns-address
    

Install Packages

Run the following commands step by step in the server:

$ sudo apt update

# Type `y` and press `Enter` to continue
$ sudo apt upgrade

After the update and upgrade, reboot your instance and reconnect to the server.

Next, run the following commands to install some packages:

# Type `y` and press `Enter` to continue
sudo apt install python3-pip python3-venv python3-dev libpq-dev

# Set python3 as default python
sudo ln -s /usr/bin/python3 /usr/local/bin/python

# Set pip3 as default pip
sudo ln -s /usr/bin/pip3 /usr/local/bin/pip

Check the location of python and pip:

$ which python
/usr/local/bin/python

$ which pip
/usr/local/bin/pip

$ python --version
Python 3.10.x # Python version should be 3.10+

Configure AWS PostgreSQL Server

Create Database

  1. In the AWS Home Page, type RDS in the search bar and click Databases in the RDS area to enter the RDS Databases Dashboard.

    create-database-1
  2. Click Create database to create a new RDS Database.

    create-database-2
  3. Select Standard create. Select PostgreSQL in the engine type field. Select PostgreSQL 14.5-R1 in the engine version field. Select Free tier in the Templates field.

    create-database-3
  4. Type flask-todos in the DB instance identifier field. Keep the default value in the Master username field. Enter a password in the Master password field.

    create-database-4
  5. Select General Purpose SSD (gp2) in the Storage type field. Modify the storage size to 20 GB. Uncheck Enable storage autoscaling.

    create-database-5
  6. Select Connect to an EC2 compute resource in the Connectivity Section. Select flask-todos EC2 Instance you created in the previous step in the EC2 instance field. Select IPv4 in the Network type field.

    create-database-6
  7. If you want to disable automated backups, uncheck Enable automatic backups in the Additional configuration section.

  8. Scroll down to the bottom of the page and click Create database.

Create AWS PostgreSQL Database and Table

  1. Go back to the RDS Databases Dashboard and click the DB identifier to enter the Database Instance Details Page. In the Connectivity & security section, copy the Endpoint address.

    create-aws-postgresql-database-and-table-1
  2. Open pgAdmin, Right Click Servers - Select Register - Click Server....

    create-aws-postgresql-database-and-table-2
  3. In the popup window, type aws in the Name field in General tab.

    create-aws-postgresql-database-and-table-3
  4. Copy and paste your database instance Endpoint address in the Host name/address field in Connection tab. Type the password you set for AWS database in the Password field in Connection tab.

    create-aws-postgresql-database-and-table-4
  5. Copy and paste your AWS EC2 Public IPv4 DNS address in the Tunnel host field in SSH Tunnel tab. Type ubuntu in the Tunnel username field. Select Identity file in the Authentication field. Click Browse to select the key pair file flask-todos.pem.

    create-aws-postgresql-database-and-table-5
  6. Click Save.

  7. Follow the instructions in Create Local PostgreSQL Database and Table section to create database flask-todos and table todos, and insert some sample dataset.

    create-aws-postgresql-database-and-table-6

Run Application on Server

Connect Server via VS Code

  1. Open VS Code and click the Remote Explorer icon on the left side of the page.

  2. Click + to add a new SSH target.

  3. Type ssh -i path_to_key_pair/flask-todos.pem ubuntu@your-instance-public-ipv4-dns-address in the SSH Command field. Press Enter to connect to the server.

  4. Press Enter to save the SSH target to the default SSH Configuration file.

    connect-server-via-vs-code-1
  5. Refresh the Remote Explorer and you will see the AWS EC2 server and click-> to connect to the server. Select Linux as the platform of the server.

    connect-server-via-vs-code-2
  6. Click Open Folder in the Side Bar and click OK to open the path /home/ubuntu.

    connect-server-via-vs-code-3
  7. Drag and Drop the flask-todos folder to the Side Bar of VS Code and confirm Copy Folder to upload the folder to the server.

    connect-server-via-vs-code-4

Create Virtual Environment on Server

Use the follow commands to create and activate the virtual environment:

# Open folder
$ cd flask-todos

# Create a Python Virtual Environment called flask-app
$ python3 -m venv venv

# Activate flask-app Virtual Environment
$ source venv/bin/activate

# upgrade pip
$ pip install --upgrade pip
create-virtual-environment-on-server-1

Install the Dependencies

$ pip install -r requirements.txt

Edit .env File

  1. Open the .env file in VS Code.
  2. Replace the DB_HOST value with your AWS database instance Endpoint address.
  3. Replace the DB_PASSWORD value with the password you set for AWS database.
  4. (Optional) Replace the FLASK_SECRET_KEY value if you like.

Run Application on Server in VS Code

Run command python src/app.py in the integrated terminal in VS Code to start the application. Then, you will see a popup window in the bottom-right corner of the VS Code. Click Open in Browser to open the application in the browser.

run-application-on-server-in-vs-code-1

Or you can click PORTS tab in the integrated terminal and click Open in Browser icon to open the application in the browser.

run-application-on-server-in-vs-code-2

Build a Nginx Web Server

Install Nginx

Run the following command to install Nginx:

# Type `y` and press `Enter` to continue
sudo apt-get install nginx

Nginx Configure

build-a-nginx-web-server-1

Then, run the following commands in the integrated terminal in VS Code:

# Create a new file called flask-todos
$ sudo touch /etc/nginx/sites-available/flask-todos

# Link the file to the sites-enabled directory
$ sudo ln -s /etc/nginx/sites-available/flask-todos /etc/nginx/sites-enabled

# Open the flask-todos file
$ code /etc/nginx/sites-available/flask-todos

Copy and paste the following content to the flask-todos file, replace the server_name value with your AWS EC2 Public IPv4 DNS address:

server {
	listen 80;
	listen [::];

	server_name xyz.compute-1.amazonaws.com;

	location / {
		proxy_pass http://127.0.0.1:8000;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Forwarded-Host $host;
		proxy_set_header X-Forwarded-Prefix /;
		proxy_redirect off;
	}
}

This configuration file tells Nginx to listen on port 80 and forward all the requests to the localhost and port 8000. This is called reverse proxy.

Then, open Command Palette by Shift+Command+P (Mac) / Ctrl+Shift+P (Windows/Linux) and type Save as Root to save the file as root

nginx-configure-1

Next, run the following commands in the integrated terminal in VS Code to enable and restart the Nginx service:

# Make sure the configuration file is correct
$ sudo nginx -t

# Restart Nginx service
$ sudo systemctl restart nginx

# Check the status of Nginx service
$ sudo systemctl status nginx # should be active (running)

# Enable Nginx service to start up at boot
$ sudo systemctl enable nginx

Test the Connection

Run the command next line to start the Flask application:

# --workers 1: 1 worker process for handling requests
# --bind 127.0.0.1:8000: listen on localhost and port 8000
# You can simply use gunicorn wsgi:app to start the application
# gunicon will use the default number of workers and listen on localhost and port 8000
$ gunicorn wsgi:app --workers 1 --bind 127.0.0.1:8000

Make Sure you activate the virtual environment before you run the command.

Gunicore is a Python WSGI HTTP Server for UNIX. You can check here for more information.

wsgi is the python file in the flask-todos folder, you can view the content of the file by clicking the wsgi.py file in the Side Bar of VS Code. The reason we use wsgi.py instead of src/app.py is that we don't want to change the debug value in the src/app.py file to False every time we want to run the application in production.

Then, open the browser and copy and paste the Public IPv4 DNS address of your AWS EC2 instance into the address bar. You will see the application is running.

Persist the Application

Now, we need to make sure the application will be running even if we close the terminal. We can use systemd to manage the application.

First, run the next line command to deactivate the virtual environment:

$ deactivate

Then, run the following commands to create a new service file:

# Create a new file called flask-todos.service
$ sudo touch /etc/systemd/system/flask-todos.service

# Open the flask-todos.service file
$ code /etc/systemd/system/flask-todos.service

Option 1: TCP/IP Socket (Easier)

Copy and paste the following content to the flask-todos.service file:

[Unit]
Description=Flask Todos Web Application
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/flask-todos
Environment="PATH=/home/ubuntu/flask-todos/venv/bin"
ExecStart=/home/ubuntu/flask-todos/venv/bin/gunicorn --workers 1 --bind 127.0.0.1:8000 wsgi:app

[Install]
WantedBy=multi-user.target

Then, run the following commands to start and enable the service:

# Start the service
$ sudo systemctl start flask-todos

# Check the status of the service
$ sudo systemctl status flask-todos # should be active (running)

# Enable the service to start up at boot
$ sudo systemctl enable flask-todos

That's it! Now, you can close the terminal, and the application will still be running.

Option 2: Unix Socket (More Secure and Higher Performance)

Copy and paste the following content to the flask-todos.service file:

[Unit]
Description=Flask Todos Web Application
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/flask-todos
Environment="PATH=/home/ubuntu/flask-todos/venv/bin"
ExecStart=/home/ubuntu/flask-todos/venv/bin/gunicorn --workers 1 --bind unix:flask-todos.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Then, run the following commands to start and enable the service:

# Start the service
$ sudo systemctl start flask-todos

# Check the status of the service
$ sudo systemctl status flask-todos # should be active (running)

# Enable the service to start up at boot
$ sudo systemctl enable flask-todos

Next, open the flask-todos file in the sites-available directory and change the proxy_pass value to unix:/home/ubuntu/flask-todos/flask-todos.sock;:

server {
	listen 80;
	listen [::];

	server_name xyz.compute-1.amazonaws.com;

	location / {
		proxy_pass http://unix:/home/ubuntu/flask-todos/flask-todos.sock;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Forwarded-Host $host;
		proxy_set_header X-Forwarded-Prefix /;
		proxy_redirect off;
	}
}

Next, run the command code /etc/nginx/nginx.conf to open the nginx.conf file and replace the user value to ubuntu in Line 1.

persist-the-application-1

Finally, run the following commands to restart the Nginx service:

# Make sure the configuration file is correct
$ sudo nginx -t

# Restart Nginx service
$ sudo systemctl restart nginx

# Check the status of Nginx service
$ sudo systemctl status nginx # should be active (running)

Now, you can close the terminal, and the application will still be running.