Setting up a Flask Web Server

Posted by: Salman Ali
Original Post Date: July 1, 2016
Last modified: January 8, 2017

This blog recounts my experiences in setting up the web server for this blog site. I originally started posting on Blogger but that platform wasn't for me. As I wanted to further my experience with Python and I had discovered Miguel Grinberg's excellent Flask Mega-Tutorial, I thought that it would be more interesting and educational to setup my own.

As always I got a bit more than I had bargained for which was further motivation to document my process as it might save some of you a bit of time. For one, Miguel first wrote the blog in 2012 and portions of his instructions are a bit dated now. Further, not all modules he used (especially SQLAlchemy) still seem to work correctly.

My version of this blog website is a more lightweight version of Miguel's Flask blog app as I assume a knowledge of SQL versus relying on any ORM model. I'm a big fan of reducing scaffolding as much as possible.

I would advise that you first consider the versions of all your software to ensure everything is compatible. That is a lesson I learned that hard way and cost me several hours to figure out why my blog app wasn't working with my web server. Keen on using a virtual environment of the latest Python version (3.5) I realized that Apache on Fedora 23 uses Python 3.4. So, before setting up your virtual environment make sure you are starting with the right version of Python!

Setting up the Database

I initially wanted to use MariaDB as Miguel's tutorial was based on a prototype using SQLite followed by deployment with MySQL. However as I was having trouble with the SQLAlchemy support on Python3 I decided I might as well try another database since I would be setting everything up using plain old SQL. I ended up settling on Postgres since it seemingly is popular with many Python web developers and has good Python3 support, and would be another learning experience for me as I had no prior experience with it.

The install was mostly straightforward. Don't forget to follow the instructions for setting up the firewall as well as SELinux as shown on the link. The only issue I had was with the initial login as su - postgres did not work for me. I ended up figuring out the initial login by doing further searching on the web and finding an Ubuntu solution that happens to work in Fedora as well: sudo -u postgres psql. I really can't say that I understand the difference between the two or why one works but the other doesn't. That's research for another day.

The basic commands for a new Postgres install are summarized below:

sudo dnf install postgresql-server postgresql-contrib

sudo systemctl enable postgresql

sudo postgresql-setup --initdb --unit postgresql

sudo systemctl start postgresql

firewall-cmd --permanent --add-port=5432/tcp

firewall-cmd --add-port=5432/tcp

Edit /var/lib/pgsql/data/pg_hba.conf to confirm user authentication- password for localhost is probably the simplest initially:

TYPE	DATABASE	USER	ADDRESS		METHOD

host	all		all	localhost	password

Finally, setup your Postgres user/password and database:

sudo -u postgres psql

Configuring the Web Server

Flask has a handy web server to help test the setup, but at some point you'll want to setup your production web server which in my case was Apache. I also installed mod_wsgi for Apache to interact with Flask. Note that any reference to myapp below is just a placeholder and should be replaced by your app name.

sudo dnf install python3-mod_wsgi

cd /var/www

sudo mkdir myapp

sudo vi myapp.wsgi

Enter the following code into the file:

activate_this = '/path/to/your/activate_this.py

with open(activate_this) as file_:

    exec(file_.read(), dict(__file__=activate_this))

 

from app import app as application

app in from app refers to the __init__.py file in the app folder that is in the same directory as the activate_this.py file. The app in import app is the app variable defined in __init__.py. More on this later.

A visual representation of the Apache file structure is shown below:

/

|

var/

|

www/

|

myapp/

|

myapp.wsgi

SELinux was probably the main consideration in setting up Apache

setsebool -P httpd_can_network_connect on

setsebool -P httpd_can_network_connect_db on

setsebool -P httpd_unified on

Setup the configuration file for the server using:

sudo vi /etc/httpd/conf.d/z_myapp.conf

The 'z' in front of the filename is to ensure that the configuration file runs after all the default configuration files as any conf files in conf.d are run in alphabetic order. The code for the .conf file is:

WSGISocketPrefix run/wsgi

WSGIScriptAlias /myapp /var/www/myapp/myapp.wsgi

WSGIRestrictStdout Off

    ServerName myapp.com

 

    WSGIDaemonProcess myapp user=username group=usergroup threads=5

    WSGIScriptAlias / /var/www/myapp/myapp.wsgi

 

    

        WSGIProcessGroup myapp

        WSGIApplicationGroup %{GLOBAL}

        Require all granted

    

Again, replace myapp with your app name as well as username and usergroup with your respective username and usergroup.

Start the web server:

sudo firewall-cmd --add-service:http

sudo firewall-cmd --permanent --add-service:http

sudo firewall-cmd --add-service:https

sudo firewall-cmd --permanent --add-service:https

sudo systemctl start httpd

sudo systemctl enable httpd

Finally setup the environment and paths in the activate_this file in the myapp folder of the virtual environment. This file ensures that the virtual environment is recreated within the wsgi module of the web server.

The Virtual Environment

I use Anaconda for my virtual environment and set it up minimally with the base conda install via the installer, then installed the following:

conda create --name web flask flask-wtf psycopg2

activate web

pip install flask-marshmallow

The psycopg2 package is for my Postgres database. I ended up installing my web app inside the conda envs directory structure for my virtual environment although I'm quite sure that isn't a best-practice. Consequently, trying to map Miguel's file structure to my own setup was a minor brain tease due to this decision as well as Miguel's tendency to name everything 'app', which I've attempted to disambiguate as much as possible. For clarity, I've included a visual summary of my directory structure below:

user home

|

miniconda3/

|

envs/

|

web/

|

bin/  conda-meta/  flask/  include/  lib/  share/  ssl/

|            

myapp/           

|            

activate_this.py  app/  config.py  run.py

|   

forms.py __init__.py static/ templates/ views.py

The activate_this.py is a simple script that mod_wsgi first runs within its Python instance. In my case I use it to set os.path['environ'] and sys.path to be the same as they would be if I just printed them in my virtual environment:

import os, sys

 

os.environ['PATH'] = '/your/path/number/one:/your/path/number/two:/etc/etc/etc'

 

sys.path = ['/sys/path/number/one', '/sys/path/number/two', '/etc/etc/etc']

Finally, to appease SELinux:

sudo chcon -R -t httpd_sys_content_t web

Deploying: Using a Domain Name

After some research I chose Namecheap to register my domain name. I hadn't tried anyother domain registry before so I had no point of comparison but my experience was inline with the reviews I had read and I had little trouble. I did have to go through their Knowledgebase a couple of times to figure out how to use DDNS but their interface is clean and relatively easy to navigate. Ultimately I downloaded their software to setup DDNS as my router didn't support a custom domain name server in its DDNS setup options.

Upgrade to Fedora 24

upgrade postgres, upgrade python virtual env to python3.5, upgrade wgsi/activate_this.py links,and reset SELinux permissions

The Initial Postgres Login Explained

Posted by: Salman Ali
Original Post Date: July 28, 2016
Last modified: July 28, 2016

So after a bit of research I believe that I understand why sudo -u postgres psql worked for me but su - postgres didn't. Initially Postgres is setup for ident/peer local authentication, which means that you need to login to Postgres using the postgres user account created on your machine during installation. However, this account is locked which means only the superuser can log into it.

Consequently, there are 2 ways of initially logging in to the database.

Method 1- The Long Way

  1. Login as root via su
  2. Then login as postgres using su postgres
  3. Login to the database using psql

Method 2- The Quick Way

Does the same thing in one line: sudo -u postgres psql. This works because sudo executes code as the superuser, in this case running psql as user postgres.

References:

  1. http://serverfault.com/questions/110154/whats-the-default-superuser-username-password-for-postgres-after-a-new-install
  2. http://www.cyberciti.biz/faq/linux-locking-an-account/
Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License