A (Complete) Guide to Running Django on Joyent Shared Accelerators using Virtualenv, pip, git, and NginX

In this guide we’ll go over the process of deploying a Django application on a Joyent Shared Accelerator (now called SmartMachines).

We’ll be using the following tools:

  • Virtualenv - to isolate our Python environment and manage software versions.
  • pip - to install packages.
  • git - for version control
  • NginX - to serve our Django site through FastCGI.

Getting started: setting up your development environment

Let’s setup our development environment on our local machine using Virtualenv and pip.

$ virtualenv --no-site-packages mysite
$ cd mysite/
$ . bin/activate
$ pip --version
pip 0.8.2 from /home/evan/mysite/lib/python2.6/site-packages (python 2.6) 

Inside your virtualenv upgrade pip.

pip install --upgrade pip

Install Django inside a virtualenv

Note: If your application is already inside a virtualenv with packages installed by pip you can skip down to the Deploying section below.

Let’s install the latest stable release of Django (1.2.4 as of today) using pip.

$ pip install Django
Downloading/unpacking Django
  Downloading Django-1.2.4.tar.gz (6.4Mb): 6.4Mb downloaded
  Running setup.py egg_info for package Django
Installing collected packages: Django
  Running setup.py install for Django
    changing mode of build/scripts-2.6/django-admin.py from 644 to 755
    changing mode of /home/evan/mysite/bin/django-admin.py to 755
Successfully installed Django
Cleaning up...

Create a new project, myproject, here. It should be in the same directory as bin, include, and lib that were created by virtualenv.

$ django-admin.py startproject myproject
$ cd myproject/
$ django-admin.py runserver
Error: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined.

Oops! We’re getting an error because Django cannot find and import our settings.py file. Let’s fix that by putting our project directory into the virtualenv’s site-packages directory with a symbolic link:

$ ln -s `pwd` ../lib/python2.6/site-packages/`basename \`pwd\`` 

If you are using a different version of Python change python2.6 to reflect that version.

And then defining the DJANGO_SETTINGS_MODULE environment variable. Also, let’s make it so this environment variable is set every time we activate the virtualenv.

$ export DJANGO_SETTINGS_MODULE=myproject.settings
$ echo "!!" >> ../bin/activate

Now let’s try the runserver command again.

$ django-admin.py runserver
Validating models...
0 errors found
 
Django version 1.2.4, using settings 'myproject.settings' 
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Great! Everything is working.

Create a git repository

We want to create our git repository inside the myproject directory to track our Django application.

$ git init .
Initialized empty Git repository in /home/evan/mysite/myproject/.git/
$ echo "My Django app for Joyent" > README
$ git add README
$ git commit -m "Initialzed repository and added README" 
[master (root-commit) 7d09bd1] Initialzed repository and added README
1 files changed, 1 insertions(+), 0 deletions(-) 
create mode 100644 README

Here you should create your application. I’ll just use the Django Tutorial for now.

Use requirements and pip freeze

We want to create a list of all the necessary packages for our project. The pip freezecommand can help us with this, but we can’t just use every package from the list because some packages like fabric and paramiko won’t successfully install on the Share Accelerator.

My REQUIREMENTS file is very short:

$ cat REQUIREMENTS 
Django==1.2.4
django-debug-toolbar==0.8.4
wsgiref==0.1.2

Great. Let’s get this app running on Joyent.

Deploying: getting your app on the server

First off, login to your virtualmin page (my server, harbor, is at https://virtualmin.joyent.us/harbor/ )and create a virtual server for your new site if you haven’t yet.

Now you should create a database. I’d recommend creating a PostgreSQL database. If you’d like you can also create a separate user to access the database with. For simplicity, I’ll just use my main user account for now.

Install virtualenv on Joyent

To use virtualenv on your server you need to first get it on your server. We’ll download it from PyPi.

ssh in to your server and:

$ mkdir -p local/
$ cd local/
$ wget -O virtualenv-1.5.1.tar.gz http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.5.1.tar.gz
$ tar xzvf virtualenv-1.5.1.tar.gz

Let’s now create our virtualenv on our server. This is the same as on our local machine.

$ python virtualenv.py --no-site-packages ~/django_projects/mysite
$ cd ~/django_projects/mysite/
$ . bin/activate

Upload your application to the server

Now, get your project directory inside this newly created virtualenv. It really doesn’t matter how. If you’re using github you might want to create a deploy key These days, this seems to be the preferred way to deploy.

If you’re using github, clone your project with:

$ git clone git@github.com:carmi/mysite.git
Initialized empty Git repository in /users/home/carmi/django_projects/mysite/mysite/.git/

Great! Now we should have our project directory myproject inside our virtualenv on the same level as bin, lib and include. Make sure you’ve modified your settings file for production and updated the database settings to use your PostgreSQL database instead of sqlite3. My databases looks like:

DATABASES = { 
    'default': { 
        'ENGINE': 'postgresql_psycopg2', 
        'NAME': 'carmi_django_mysite_database', 
        'USER': 'carmi', 
        'PASSWORD': 'password', 
        'HOST': 'localhost', 
        'PORT': '5432', 
    } 
} 

Try running django-admin.py syncdb. If it works then your database is configured correctly.

Setup the server’s virtualenv.:

$ cd ~/django_projects/mysite/myproject/
$ ln -s `pwd` ../lib/python2.6/site-packages/`basename \`pwd\`` 
span class="nv">$ export DJANGO_SETTINGS_MODULE=myproject.settings
$ echo "!!" >> ../bin/activate

Install packages on the server with pip

Let’s install the packages in our REQUIREMENTS file.

$ pip install -r REQUIREMENTS

We will need flup and psycopg2 (for PostgreSQL). Let’s install those:

$ pip install flup psycopg2

Setup static media

Let’s assume you have some static media for your project in ~/django_projects/mysite/myproject/media/

For security reasons (but don’t trust me on this) we don’t want to serve static media (CSS, Javacsript) from inside our project directory. Instead, let’s create some other directories to serve static media from:

$ mkdir -p ~/django_projects/mysite/web/public

And then create a symbolic link from there to our media directory.

$ ln -s ~/django_projects/mysite/myproject/media/ ~/django_projects/mysite/web/public/media

Now let’s link Django’s contrib.admin media to this location:

$ ln -s ~/django_projects/mysite/lib/python2.6/site-packages/django/contrib/admin/media/ ~/django_projects/mysite/myproject/media/admin

And lastly let’s configure our settings.py to use these locations:

MEDIA_URL = '/media/' 
ADMIN_MEDIA_PREFIX = '/media/admin/' 

Setup NginX and FastCGI

Now let’s get NginX running.

Create a nginx.conf file inside your site directory:

$ mkdir -p ~/django_projects/mysite/etc/ 
$ vim ~/django_projects/mysite/etc/nginx.conf 

Edit your nginx.conf file to look like the following but with your own port number, domain, and username. In the example my port is 10071, my domain is django.joyeurs.com, and my username is carmi:

events { 
    worker_connections  24; 
} 
 
http { 
    include     /opt/local/etc/mime.types; 
    default_type  application/octet-stream; 
 
    server { 
        listen       10071; 
        server_name  django.joyeurs.com; 
 
        location /media { 
            root   /users/home/carmi/django_projects/mysite/web/public; 
        } 
 
        location / { 
            fastcgi_pass unix:/users/home/carmi/django_projects/mysite/myproject/myproject.socket; 
 
            # fastcgi parameters 
            fastcgi_param PATH_INFO $fastcgi_script_name; 
            fastcgi_param QUERY_STRING $query_string; 
            fastcgi_param REQUEST_METHOD $request_method; 
            fastcgi_param SERVER_PORT $server_port; 
            fastcgi_param SERVER_PROTOCOL $server_protocol; 
            fastcgi_param SERVER_NAME $server_name; 
            fastcgi_param CONTENT_TYPE $content_type; 
            fastcgi_param CONTENT_LENGTH $content_length; 
        }    
    }   
} 

Create an init.sh script in your project directory to start the Django FastCGI process that should look like:

#!/usr/local/bin/bash 
 
#Activate the virtualenv 
source /users/home/carmi/django_projects/mysite/bin/activate
 
PROJECT_NAME="myproject" 
PROJECT_DIR="/users/home/carmi/django_projects/mysite/myproject" 
PID_FILE="/users/home/carmi/django_projects/mysite/myproject/myproject.pid" 
SOCKET_FILE="/users/home/carmi/django_projects/mysite/myproject/myproject.socket" 
BIN_PYTHON="/users/home/carmi/django_projects/mysite/bin/python" 
DJANGO_ADMIN="/users/home/carmi/django_projects/mysite/bin/django-admin.py" 
OPTIONS="maxchildren=2 maxspare=2 minspare=1" 
METHOD="prefork" 
 
case "$1" in
    start) 
      # Starts the Django process 
      echo "Starting Django project" 
      $BIN_PYTHON $DJANGO_ADMIN runfcgi $OPTIONS method=$METHOD socket=$SOCKET_FILE pidfile=$PID_FILE 
  ;;  
    stop) 
      # stops the daemon by cating the pidfile 
      echo "Stopping Django project" 
      kill `/bin/cat $PID_FILE` 
  ;;  
    restart) 
      ## Stop the service regardless of whether it was 
      ## running or not, start it again. 
      echo "Restarting process" 
      $0 stop
      $0 start
  ;;  
    *)  
      echo "Usage: init.sh (start|stop|restart)" 
      exit 1
  ;;  
esac 

You’ll need to make this init.sh file executable:

$ chmod +x ~/django_projects/mysite/myproject/init.sh

Startup the Django FastCGI instance with:

$ ~/django_projects/mysite/myproject/init.sh start

This script also takes start, stop, and restart as parameters.

Now launch NginX with your configuration file:

$ /usr/local/sbin/nginx -p /users/home/carmi/ -c /users/home/carmi/django_projects/mysite/etc/nginx.conf

We should now having our Django application running. Go to http://domain.com:PORTNUBMER/ to see it. For my app, we can login to the admin interface by going to: http://django.joyeurs.com:10071/admin/

Now, in virtualmin create bootup actions to start the Django FastCGI process and NginX on server reboots.

Great! We’ve just covered setting up a Django application on a Joyent Shared Accelerator using virtualenv, pip, NginX, and git.

Please let me know if something is not working, or if you’d just like to leave some feedback.

Coming Soon

Wait a second! This is a long guide. Are we really all going to repeat the same steps over and over? Coming soon is a python script to automate this entire install process. Stay posted.

blog comments powered by Disqus