As a part of the Udacity Full Stack Web Developer Nanodegree, they got us to deploy our Flask app to an Ubuntu server running Apache. This was relatively simple (find out more with my full server setup document).
However, on my own server I use the Nginx web server and you have to use uWSGI to serve the Flask app. Here I will document what I did to my Ubuntu server, both for myself and anybody that’s interested. I found the following tutorial very helpful: How To Serve Flask Applications with uWSGI and Nginx on Ubuntu 14.04.
Install and configure PostgreSQL
This is a repeated step from the Nanodegree, but needs to be done on my server all the same.
Install PostgreSQL with the following command:
sudo apt-get install postgresql postgresql-contrib
Create a PostgreSQL user called catalog with:
sudo -u postgres createuser -P catalog
You are prompted for a password. This creates a normal user that can’t create databases, roles (users).
Create an empty database called catalog with:
sudo -u postgres createdb -O catalog catalog
That’s it for the PostgreSQL setup.
Clone the catalog app
Clone the repository containing the catalog app to the home directory of a normal user.
git clone https://github.com/SteveWooding/fullstack-nanodegree-vm.git
Setup virtual Python environment and install software
To prevent the system from being clogged up with various Python packages, it is recommended to use a Python virtual environment when deploying apps. This also means that if another requires specific versions of Python packages, it is easy just to create another virtual environment for that app with those packages.
Install Python Pip and Virtualenv:
sudo apt-get install python-pip
sudo pip install virtualenv
One other system-wide package to install is libpq-dev
. This is required to build the psycopg2
package within the virtual environment.
sudo apt-get install libpq-dev
Create the environment inside the app directory:
cd ~/fullstack-nanodegree-vm/vagrant/catalog
virtualenv catalogenv
Go into the environment:
source catalogenv/bin/activate
The command prompt will now start with (catalogenv)
.
Install uWSGI and Flask and the other prerequisites the app needs:
pip install uwsgi
pip install flask
pip install oauth2client
pip install psycopg2
pip install sqlalchemy
pip install flask-seasurf
pip install requests
To make sure the catalog is working within the virtual environment, run the following command to run the app in debug mode using a local SQLite file:
python application.py
Check for any errors and use a browser to check http://localhost:8000
. If you only have terminal access to a remote service, install a text-only browser for local testing. elinks
is one I recommend.
When finished, use Ctrl-C
to exit the Python program. And remove the database file:
rm itemcatalog.db
Configure and test uWSGI
The file catalog.wsgi
is the entry point for uWSGI. Update file paths and the password to the PostgreSQL database in this file. Then test with the following command:
uwsgi --socket 0.0.0.0:8000 --protocol=http --wsgi-file catalog.wsgi
uWSGI needs a configuration file that does a similar job to the manually entered line above. Here is the file for this project (catalog.ini
):
[uwsgi]
wsgi-file = catalog.wsgi
master = true
processes = 5
socket = catalog.sock
chmod-socket = 660
vacuum = true
die-on-term = true
You can find this file in the root directory of the project repository.
The web app should be available again at http://localhost:8000
, except this time using the PostgreSQL database.
Come out of the virtual environment, now everything is set-up and working.
deactivate
Copy catalog app to /srv directory
To keep the system clean and tidy, copy just the catalog app to the /srv/
directory, from where the app will be served from.
sudo cp -R ~/fullstack-nanodegree-vm/vagrant/catalog /srv
Then change the ownership to the web server user for all files in the catalog app.
sudo chown -R www-data:www-data /srv/catalog
Now that the catalog app has been moved, update paths in /srv/catalog/catalog.wsgi
and choose a good secret for the value of application.secret_key
.
sudo -u www-data nano /srv/catalog/catalog.wsgi
Create an Upstart script
To make sure the catalog app is served at boot time and to make it a service that can be started and stopped, let’s make an Upstart script (assuming Ubuntu is the OS).
Open a file for editing:
sudo nano /etc/init/catalog.conf
This should contain the following in my case (see this for a more detailed explanation):
description "uWSGI server instance configured to serve catalog app"
start on runlevel [2345]
stop on runlevel [!2345]
setuid www-data
setgid www-data
env PATH=/srv/catalog/catalogenv/bin
chdir /srv/catalog
exec uwsgi --ini catalog.ini
Start the uWSGI process now with:
sudo start catalog
Configuring Nginx to pass on requests to the app
Nginx now needs to be configured to act as a proxy and pass on requests to the uWSGI app.
Create a new virtual server in the sites-available
directory:
sudo nano /etc/nginx/sites-available/catalog
This file contains the following in my case:
server {
listen 80;
server_name zooma.stevenwooding.com;
location / {
include uwsgi_params;
uwsgi_pass unix:/srv/catalog/catalog.sock;
}
}
If you are using a server name other than localhost
, make sure the DNS settings are updated to point at your server’s IP address.
Enable the new site with:
sudo ln -s /etc/nginx/sites-available/catalog /etc/nginx/sites-enabled
Check the Nginx configuration in valid with:
sudo nginx -t
Correct any errors. Once successful, restart Nginx with:
sudo service nginx reload
The app should now be successfully served.
Updating OAuth secrets files
My catalog app allows visitors to login via Google+ and Facebook using OAuth. I now have to tell Google and Facebook the new location of my app and fill in the secrets in the following two files:
/srv/catalog/g_client_secrets.json
/srv/catalog/fb_client_secrets.json
For Google, go to the Google Developers Console and for Facebook, go to Facebook Login. For extra security, I refreshed the client secrets, just in case previous secrets are now compromised.
Login to the app via Google and Facebook should now work. If not, double check the client IDs and secrets and the javascript_origins
variable in the g_client_secrets.json
file.
Double check on Facebook for the app that on:
Settings > Advanced > Client OAuth Settings > Valid OAuth redirect URIs
the correct URL for the app is present.
For Google this is in API Manager > Credentials
, select the web client and check the value of Authorized JavaScript origins
has the correct URL in it for the app. Don’t forget to save settings if any changes need to be made.
Reflections
So that’s how I deployed my Flask app I made during my Udacity Nanodegree to my server running Nginx. You can check it out here zooma.stevenwooding.com.
The next thing I want to do is use a Let’s Encrypt SSL certificate and put the app on HTTPS instead of HTTP. This raises the confidence of users that a website is safe and secure.
Update
My catalog app is now secured with SSL/TLS. All I had to do was follow my own basic guide: Let’s Encrypt All the Things. Then apply these extra settings to increase the SSL security of the site: How to improve HTTPS security.