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.
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.
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
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
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
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
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.
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.
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
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.
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
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.