Deploying Flask to AWS: Amazon Linux 2, PostgreSQL, Nginx and, uWSGI

By Adam McQuistan in Python  07/02/2020 Comment

Introduction

In this article I discuss how to deploy a simple Flask Todo application complete with a PostgreSQL database to the AWS cloud on a Amazon Linux 2 AMI utilizing Nginx web server and uWSGI application server. If you would like to follow along in this tutorial you can clone the Flask Todo application from my GitHub repo. I also have a course titled Ultimate Guide to Deploying Flask to AWS where I demonstrate a number of deployment architectures, technologies, and techniques utilizing the AWS cloud if your interested in learning more.

Watch in YouTube if you Prefer Video

Launch AWS Linux 2 EC2 Instance

As mentioned previously I will be using the Amazon Linux 2 operating system for the EC2 hosting the app.

Step 1: First things first I must log into the AWS console, navigate to the EC2 dashboard and launch an EC2 instance of type Amazon Linux 2.

 

Step 2: Select the appropriate type (aka size) which for this demo I will use the t2.micro

Step 3: Keep the defaults for Configruation clicking to advace to the next step after 

Step 4: Keep defaults for Storage although, in a production instance I would recommend adding a separate volume for the PostgreSQL database's data directory. See my article Developer's Guide to PostgreSQL on Linux: Changing the Default Data Directory on how to do this if you desire.

Step 5: Click next through adding tags

Step 6: Add a security group that allows HTTP/HTTPS inbound traffic

Step 7: Click through review and launch screens accepting or downloading a key pair use to SSH onto new EC2 instance with.

Install Linux Software Packages

Back in the EC2 dashboard page I can click on the newly created EC2 instance in the instances table and display it's details. I grab the public IPv4 address and ssh onto the new EC2 server like so.

 

ssh ec2-user@ipaddress -i /path/to/keypair.pem

Once on the server I switch to the root user and install the following software packages.

sudo su
yum update -y
amazon-linux-extras install epel -y
yum install nginx -y
yum install git -y
yum install gcc -y
yum install build-essential -y
yum install python3-pip python3-devel python3-setuptools -y
amazon-linux-extras install postgresql11 -y
yum install postgresql-server -y

PostgreSQL Setup

In the last section I updated the local package list to include PostgreSQL version 11 then installed it. Now I need to initialize it and create a user and database to use in my Flask Todo application.

To initialize the database I switch users to the postgres user that gets created during the PostgreSQL install then use the pg_ctl program to initalize the database.

su - postgres
pg_ctl init

Next I exit back to the root user and start then enable the postgresql service.

exit
systemctl start postgresql
systemctl enable postgresql

Afterwards I switch back to the postgres user to create the database and user.

To create the user I use the createuser utility program, supplying it the username of flasktodo and entering a password of flasktodoappuser.

createuser -e --pwprompt flasktodo

Next up I create a database named todos using the createdb utiltity program and assign the flasktodo user as the owner.

createdb -e --owner=flasktodo todos

Cloning Flask Todo App to EC2 Instance

Before I clone the Flask App to the EC2 instance I should make a directory for serving the Flask app out of at /var/www.

I will do this as the root user but eventually I'll change the permissions and ownership to the ec2-user which is the default non-root users for the Amazon Linux 2 AMI.

mkdir /var/www
cd /var/www
git clone https://github.com/amcquistan/flasktodo.git /var/www

Installing Python Dependencies

With the flasktodo repo cloned to the /var/www directory I can now create a Python virtual environment and install the Python packages that the Flask Todo app has specified in the requirements.txt file within the repo.

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Configuring Flask Todo App to Connect to the Database

The Flask Todo application is designed to read in it's database connection string URI from a environment variables file named .env and kept at the project root which is /var/www. So I create such a file and place the following in it.

SECRET_KEY=thisisasecretkey
SQLALCHEMY_DATABASE_URI=postgresql://flasktodo:flasktodoappuser@localhost/todos

Now I can test the connection by using the Flask Migrate package that was installed and Flask CLI to run database migrations like so.

export FLASK_APP=flasktodo
flask db upgrade

Resulting in output show below.

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> e5c77b2f9d45, initial migration

*** note this should be ran with the Python3 virual environment activated.

Configuring uWSGI Container Server

The uWSGI container server was specified in the requirements.txt file and installed in the previous step. What now remains is to configure it. Within the Flask Todo repo is a directory named config that contains the following files.

ls config/
flasktodo.conf  flasktodo.ini  flasktodo.service  nginx.conf

The flasktodo.ini file is a config file for the uWSGI container server that will execute the Flask app. The flasktodo.ini file's contents is shown below.

[uwsgi]
module = wsgi:app

master = true
processes = 2
virtualenv = /var/www/venv

socket = flasktodo.sock
chmod-socket = 660
vacuum = true

logto=/var/log/uwsgi/uwsgi.log

die-on-term = true

The following non-obvious settings are explained.

  • module: specifies that the callable Flask application variable resides in the wsgi.py module at the root of the repo. If you open the wsgi.py file you'll see that it imports a create_app() application factory function from the flasktodo package, calls it and assigns the result to the variable named app
  • master: specifies that graceful reloading should occur
  • socket: signifies that communication will occur throug a socket file named flasktodo.sock

The flasktodo.service file is a systemd unit file which specifies how the uWSGI application container server should be managed (start, stop, restarted, ect...).

The contents of flasktodo.service are shown below.

[Unit]
Description=uWSGI Container Server for Flask Todo
After=network.target

[Service]
User=ec2-user
Group=nginx
WorkingDirectory=/var/www
Environment="PATH=/var/www/venv/bin"
ExecStart=/var/www/venv/bin/uwsgi --ini config/flasktodo.ini

[Install]
WantedBy=multi-user.target

The systemd until file simply says who to run the service as and defines the command that is required to start the uWSGI executable and pass it the ini settings file dictating it behavior as explained previously.

The flasktodo.service file needs copied to /etc/systemd/system directory where user defined systemd services are typically kept.

cp /var/www/config/flasktodo.service /etc/systemd/system

Now I can change the /var/www directory's permissions to 755 and ownership to ec2-user as user and nginx and as well as the target directory for logging /var/log/uwsgi

chmod -R 755 /var/www
chown -R ec2-user:nginx /var/www
mkdir /var/log/uwsgi
chown ec2-user:nginx /var/log/uwsgi/

Lastly I can start and enable the flasktodo service.

systemctl start flasktodo
systemctl enable flasktodo

Configuring Nginx

The other two files I listed in the config directory are for Nginx. the nginx.conf file is just a copy of the default installed config but the default server block is commented out because I want the flasktodo.conf to be the default for this server.

The flasktodo.conf file is shown below.

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  _;
    
    location /static {
        root /var/www/flasktodo;
    }

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/var/www/flasktodo.sock;
    }
}

This Nginx server block just says that it's the default server block, to serve /static asset requests directly using Nginx and to reverse proxy all other requests to the uWSGI container server.

The two files need copied to their respective default config directories like so.

mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf-orig
cp config/nginx.conf /etc/nginx/nginx.conf
cp config/flasktodo.conf /etc/nginx/conf.d/flasktodo.conf

Then finally just start and enable the nginx service.

systemctl start nginx
systemctl enable nginx

Now I can paste the IP address of the EC2 instance in my browser and the Flask Todo app loads up as shown below.

 

Resources For Learning More about Python, Flask and, Deploying Flask to AWS

thecodinginterface.com earns commision from sales of linked products such as the books above. This enables providing continued free tutorials and content so, thank you for supporting the authors of these resources as well as thecodinginterface.com

Conclusion

In this article I presented a recipe for launching and configuring an Amazon Linux 2 AMI EC2 instance in AWS for hosting a Flask web application. 

As always, I thank you for reading and please feel free to ask questions or critique in the comments section below.

 

 

 

Share with friends and colleagues

[[ likes ]] likes

Community favorites for Python

theCodingInterface