Deploying Django with Gunicorn and Nginx on Debian/Ubuntu


Deploying Django with Gunicorn and Nginx on Debian/Ubuntu

2016/08/09

Tags: Linux Adminstration SSH Linux Django Guincorn Nginx

This tutorial will setup a Django environment running with nginx as a reverse proxy for the app. A gunicorn server will run our Django app i.e. gunicorn will act as a WSGI server. To ensure that this server runs continously and on boot, we will use supervisor to control that gunicorn process, thus avoiding pesky screen tricks.

Login to your server with a normal user, say aatish. The user should have sudo privileges. If you are currently using root account, stop right now and create a new account with the following commands:

create a normal user (skip if you are not root)

adduser aatish
adduser aatish sudo
su django

Now on, we will use sudo when needed.

Install requirements

sudo apt-get update
sudo apt-get install nginx python-pip libjpeg-dev zlib1g-dev build-essential libpq-dev python-dev supervisor python-virtualenv git

Create a directory where the code will be kept

cd ~
mkdir -p webapps/example.com
cd webapps/example.com

We don’t want to install Python packages required by the app system-wide. So, let’s create a new virtualenv under it:

virtualenv venv

or if you are using python3

virtualenv -p python3 venv

Activate the created virtual environment with:

source venv/bin/activate

Now clone your app into the server with:

git clone https://github.com/Chive/django-poll-app

Substitute the URL with yours.

Edit settings.py inside the app if needed.

To install Python packages that your app requirements, do this inside the app: cd django-poll-app pip install -r requirements.txt

If you don’t have requirements.txt inside the app, create one and list all the dependencies. You can learn more about this at https://pip.readthedocs.io/en/1.1/requirements.html

PostgreSQL database (skip if not needed)

If you need to use PostgreSQL database, install it using:

sudo apt-get install libpq-dev python-dev postgresql postgresql-contrib python-psycopg2

Now, create a new database for use with Django with:

sudo su postgres
createdb djangodbname;
createuser -P djangodbuser;
psql
GRANT ALL PRIVILEGES ON DATABASE djangodbname TO djangodbuser;

Set your password when prompted and type \q to quit.

Update these settings inside your settings.py file. You will now need to run migrations with:

python manage.py migrate

If you are using Django Admin:

python manage.py createsuperuser

Using gunicorn

Running Django’s development server is not recommended in production. We are going to use Gunicorn for that. Install it using:

pip install gunicorn

Create a gunicorn_run.sh script under ~/webapps/example.com/. Paste the following contents inside it:

#!/bin/bash
# Modified from http://michal.karzynski.pl/blog/2013/06/09/django-nginx-gunicorn-virtualenv-supervisor/
NAME="django-poll-app"
DJANGODIR="/home/aatish/webapps/example.com/django-poll-app"
SOCKFILE="/home/aatish/webapps/example.com/gunicorn.sock"
PIDFILE="/home/aatish/webapps/example.com/gunicorn.pid"
ACTIVATE="/home/aatish/webapps/example.com/venv/bin/activate"
GUNICORN="/home/aatish/webapps/example.com/venv/bin/gunicorn"
USER=aatish
GROUP=aatish
NUM_WORKERS=3
DJANGO_SETTINGS_MODULE=mysite.settings      # which settings file should Django use
DJANGO_WSGI_MODULE=mysite.wsgi              # WSGI module name

echo "Starting $NAME as `whoami`"

# Activate the virtual environment
cd $DJANGODIR
source $ACTIVATE
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH


# Start your Django Unicorn
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec $GUNICORN ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE \
  --log-level=warning \
  --log-file=- \
--pid $PIDFILE

Make this script executable with:

chmod +x gunicorn.sh

Create a new file /etc/supervisor/conf.d/example.com.conf with the following contents:

[program:django_poll_app]
command = /home/aatish/webapps/example.com/gunicorn_run.sh  ; Command to start app
user = aatish                                     ; User to run as
stdout_logfile = /home/aatish/webapps/example.com/unicorn_supervisor.log   ; Where to write log messages
redirect_stderr = true                              ; Save stderr in the same log
environment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8     ; Set UTF-8 as default encoding

Ask supervisor to reread and start the app with:

supervisorctl reread
supervisorctl update

Create a file /etc/nginx/sites-available/example.com.conf with the following contents:

upstream django_poll_app_server {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response (in case the Unicorn master nukes a
  # single worker for timing out).

  server unix:/home/aatish/webapps/example.com/gunicorn.sock fail_timeout=0;
}

server {

    listen   80;
    server_name example.com; # you can use _ if you donot have a domain

    client_max_body_size 80M;

    access_log /home/aatish/webapps/example.com/nginx-access.log;
    error_log /home/aatish/webapps/example.com/nginx-error.log;

    location /static/ {
        alias   /home/aatish/webapps/example.com/django-poll-app/static/;
    }

    location /media/ {
        alias  /home/aatish/webapps/example.com/django-poll-app/media/;
    }

    location / {
        #   http://en.wikipedia.org/wiki/X-Forwarded-For
        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://django_poll_app_server;
            break;
        }
    }

    # Error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /home/aatish/webapps/example.com/django-poll-app/static;
    }
}

Add it to sites-enabled using:

cd /etc/nginx/
ln -s /etc/nginx/sites-available/example.com.conf sites-enabled/

Restart the nginx process using:

service nginx restart

Any log files should be available at ~/webapps/example.com. You should be able to browse your Django app at example.com.

That’s it. Enjoy.

Celery configuration

If you need to use celery, add it to requirements and create a file /home/aatish/webapps/example.com/celery_run.sh with the following contents:

#!/bin/bash
/home/aatish/example.com/venv/bin/celery worker --app=mysite -l warn

Also, create a supervisor configuration /etc/supervisor/conf.d/example.com_celery.conf with the contents:

[program:django_poll_app_celery]
command=/home/aatish/webapps/example.com/celery_run.sh
directory=/home/aatish/webapps/example.com/django-poll-app
user=aatish
numprocs=1
stdout_logfile=/home/aatish/webapps/example.com/celeryd.log
stderr_logfile=/home/aatish/webapps/example.com/celeryd.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=600

If you use RabbitMQ as broker, install it using:

sudo apt-get install rabbitmq-server

Restart supervisor with:

sudo supervisorctl reread
sudo supervisorctl update

Your celery server is now running. Enjoy async.