Deploy Python web application on Ubuntu server

2016-10-08

Components used for this article:

Suppose that we already installed an Ubuntu image on a Linode plan.

Update packages




sudo apt-get update && apt-get upgrade

DNS resolution

Add a A record for the domain name of the application.

Nginx

Install Nginx




sudo apt-get install nginx

Configure Ngnix

Create a cofiguration file under /etc/nginx/conf.d/ with name [app_name].conf.




sudo nano /etc/nginx/conf.d/[app_name].conf

Configure without https

Template of /etc/nginx/conf.d/[app_name].conf:




upstream app_server_wsgiapp {



server localhost:8000 fail_timeout=0;



}



server {



listen 80;



# make sure to change the next line to your own domain name!



server_name appname.example.com;



access_log /var/log/nginx/appname.access.log;



error_log /var/log/nginx/appname.error.log info;



keepalive_timeout 5;



# nginx serve up static files and never send to the WSGI server



location /static {



autoindex on;



alias /home/username/appname/static;



}



location / {



proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;



proxy_set_header Host $http_host;



proxy_redirect off;



if (!-f $request_filename) {



proxy_pass http://app_server_wsgiapp;



break;



}



}



# this section allows Nginx to reverse proxy for websockets



location /socket.io {



proxy_pass http://app_server_wsgiapp/socket.io;



proxy_redirect off;



proxy_buffering off;



proxy_set_header Host $host;



proxy_set_header X-Real-IP $remote_addr;



proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;



proxy_http_version 1.1;



proxy_set_header Upgrade $http_upgrade;



proxy_set_header Connection "Upgrade";



}



}

Configure with https

(to add)

Restart nginx

Delete default website file:




sudo rm /etc/nginx/sites-enabled/default

Apply new configuration:




sudo service nginx restart

PostgreSQL

Install PostgreSQL




sudo apt-get install postgresql libpq-dev postgresql-client-common postgresql-client

then create database and new user




# switch to postgres user



sudo su - postgres



# create database



createdb dbname



# create a non-root database user and set password



createuser --superuser username



psql



ALTER USER username WITH PASSWORD 'password';

(For me, it seems that the last line would take effect, but we can change the password later.)

Press CTRL-D to quit psql, and CTRL-D again to quit postgres user.

Now database dbname is created and user username is able to access the database.




# Connect to database



psql dbname



# Show tables



\dt

Change password of current user:




\password

Redis

Install Redis




sudo apt-get install redis-server

Test using redis-cli.

Python application

Install pip, virtualenv




sudo apt-get install python3-pip



sudo apt-get install python3.4-venv



sudo python3 -m pip install virtualenv virtualenvwrapper

Note: python3.4-venv not python3-venv

Make virtualenv




# set an environment variable to where you want your virtual environment



export VENV=~/env



# create the virtual environment



python3 -m venv $VENV

Upgrade packages in virtualenv




$VENV/bin/pip install --upgrade pip setuptools

“Why use $VENV/bin/pip instead of source bin/activate, then pip?” From Pyramid docs:

>$VENV/bin/pip clearly specifies that pip is run from within the virtual environment and not at the system level.

>

>activate drops turds into the user’s shell environment, leaving them vulnerable to executing commands in the wrong context. deactivate might not correctly restore previous shell environment variables.

>

>Although using source bin/activate, then pip, requires fewer key strokes to issue commands once invoked, there are other things to consider. Michael F. Lamb (datagrok) presents a summary in Virtualenv’s bin/activate is Doing It Wrong.

>Ultimately we prefer to keep things clear and simple, so we use $VENV/bin/pip.

Pyramid

Install Pyramid




# install pyramid



$VENV/bin/pip install pyramid



# or for a specific released version



$VENV/bin/pip install "pyramid==1.7.3"

Demo app

We create a demo app using scaffold provided by Pyramid.




$VENV/bin/pcreate -s starter demo



cd demo



$VENV/bin/pip install -e .

WSGI server

Install Gunicorn




$VENV/bin/pip install gunicorn

Run the demo app:




cd ~/demo



$VENV/bin/gunicorn --paste development.ini -b :8000

8000 is the port gunicorn listens from the nginx reverse proxy, which we set in /etc/nginx/conf.d/demo.conf.

Open a browser and visit the URL and the app should be running.

Start gunicorn server with supervisor

Install supervisor




sudo apt-get install supervisor

Configure supervisor. Create a file /etc/supervisor/conf.d/[app_name].conf, and add lines: (e.g., for demo app, the file name should be demo.conf)




[program:demo]



environment=DEBUG=False



command=/home/[username]/venv/bin/gunicorn --paste [path/to/app/]development.ini -b :8000 --chdir [path/to/app]



directory=/home/[username]/demo



user=[username]



autostart=true



autorestart=true



redirect_stderr=True

Start supervisor service:




sudo service supervisor start

Visit the URL and the app should be running.