Deploying Mezzanine: Fabric Git Vagrant Joy

Posted by: Ken Bolton 11 years, 11 months ago

(0 comments)

Preface

In this demonstration, we will install the python applications `virtualenv`, `virtualenvwrapper`, the excellent open source Mezzanine CMS, and the Django "web framework for perfectionists with deadlines". We will create a basic Mezzanine project populated with sample data, run the Django development server, and access and admin of the site at http://127.0.0.1/.

We will then build a Vagrant virtual machine running Ubuntu 12.04 LTS, Nginx, Gunicorn, Memcached, PostgreSQL, and Supervisor, and deploy the Mezzanine project to it from a git repository using Fabric. Changing the HOSTS variable in the included Fabric settings to your host's name or IP will allow you to build, install, and deploy your site with a single command. 

This is a work in progress, hastily written late at night for a friend while waiting for a client's code to deploy. Suggestions for improvement are welcome. YMMV. This article contains topics in advanced web software engineering and is not intended as an introduction to Django, Mezzanine, Python, etc. Matthew Krohn served as a test subject and provided lots of valuable feedback.

Deploying To Production Must Never Break Production

Deploying Django web applications can be a struggle. After years, I finally found the sweet spot. Mezzanine, the CMS built on Django by Stephen McDonald and many awesome contributors, gets it right. A bundled Fabric script, a settings.FABRIC dictionary, your code on a hosted git or mercurial repository, and an Ubuntu box – be it Vagrant, AWS, a hosted VPS, or dedicated hardware – and you can spin up a server like it was nothing. Following, please find my technique for starting and deploying Django projects before a single line of code gets written. These instructions will give you a deployed Ubuntu 12.04 box running PostgreSQL, Nginx, Gunicorn, Supervisord, and Memcached.

Assumptions

These instructions will work best on a GNU/Linux or MacOSX. At this writing, VirtualBox is required to run The Vagrant, as is Ruby. In Ubuntu, `sudo apt-get install ruby-dev`. Unless otherwise stated, everything is done on localhost.

Ingredients

The links below are to the installation instructions.

Virtualenv

Virtualenv is a way to create isolated python environments for your projects. This becomes exceptionally handy when you have to support Django 1.0 sites on the same development machine as Django 1.5 sites.

pip install virtualenv

Virtualenvwrapper

Virtualenvwrapper contains a bunch of convenience scripts for working with your virtualenvs.

pip install virtualenvwrapper

Before we continue, log out of your shell, then log back in so you have access to the virtualenvwrapper scripts.

mkvirtualenv <proj_name> --no-site-packages --distribute
workon <proj_name>
cdvirtualenv

Mezzanine

# Install from PyPI
$ pip install mezzanine
$ pip install fabric

# Create a project
$ mezzanine-project <myproject>
$ cd <myproject>

More instructions for working with Mezzanine can be found here.

Vagrant

You need Ruby on your system to get this to work. My Vagrantfile has can be used as an example.

$ gem install vagrant
$ vagrant box add precise64 http://files.vagrantup.com/precise64.box
$ vagrant init precise64
$ vagrant up

Git

On your favorite git host, create a repository for your project. Note the repository's URL. Make sure you have git installed on localhost, then in your <myproject> directory run:

$ git init
$ git remote add origin <repository_url>

I usually make these repositories private. If you do that, you will want to add a deploy key to your host repository.

$ vagrant ssh
$ ssh-keygen -t rsa -C "<email_address>"
# Hit return through the options.

$ cat ~/.ssh/id_rsa.pub
# Copy the output and add it as a new key for your host.
# Yes, I know about pbcopy. No, it does not work over ssh. 

More instructions on using ssh deploy keys can be found here for github or here for bitbucket.  

Add your <myproject> directory to git and push it on up to origin!

$ git add .
$ git commit -m 'Initial commit'
$ git push origin master

Git Alternative

Networks being what they are, relying on an outside resource like github can be risky. I usually come up with the most proper solutions when far removed from network access. By design, every git repository is created equal, so there is nothing special about a hosted repository versus your local repo. Built into git is the `git daemon` commnd. Simply issue the command

$ git daemon --verbose --export-all --base-path=/path/to/your/repositories

to start a git daemon. Instructions abound on the internet for GNU/Linux init.d scripts and Mac launchd plists. Modify your settings.FABRIC REPO_URL value appropriately. For example, my Vagrant guest expects the following value:

"REPO_URL": "git://10.0.2.2/<repo>"

Now I can update and commit my changes on localhost, then run a `fab deploy` to see my changes reflected on the guest OS.

Intermission

At this point, you should have a Mezzanine project running inside of a virtualenv and an Ubuntu 12.04 Precise Pangolin Vagrant up and running and your code committed to a git or mercurial repository.

Code Up

In your project's settings.py, uncomment FABRIC dictionary. Paste those lines into your local_settings.py. The production values can be put in your settings.py. In local_settings.py, let's put the values in for that Vagrant.

FABRIC = {
"SSH_USER": "vagrant", # SSH username
"SSH_PASS": "vagrant", # SSH password (consider key-based authentication)
"HOSTS": ['<host-name>', ], # List of host names or IPs.
"VIRTUALENV_HOME": "/home/vagrant", # Absolute remote path for virtualenvs
"PROJECT_NAME": "<project_name>", # Unique identifier for project
"REQUIREMENTS_PATH": "requirements/project.txt", # Path to pip requirements, relative to project
"GUNICORN_PORT": 8000, # Port gunicorn will listen on
"LOCALE": "en_US.UTF-8", # Should end with ".UTF-8"
"LIVE_HOSTNAME": "example.com", # Host for public site.
"REPO_URL": "<repository_url>",
"DB_PASS": "default", # Live database password
"ADMIN_PASS": "default", # Live admin user password
"SECRET_KEY": <secret_key>
}

Deploy

Mezzanine's excellent fabfile.py dropped in May 2012. For what it does, it remains quite lean. To deploy your server for the first time run:

$ fab all

This will install and update the required OS packages, create the virtualenv, initialize the database, the web server, and start the web application. After you make changes, commit them to your SCM, and push to your repository, `fab deploy` will pull your changes, make all the necessary updates, and restart the appropriate services.

Underkill?

Why don't I use Puppet or Chef? These tools are great for large web applications that span multiple systems. As a rule, I want to use the fewest and simplest tools possible to get the job done. If I needed to deploy a seperate database or load balancers, I would certainly go with Puppet. In fact, writing a pp file to facilitate scaling this up may be my next article. In all likelihood, though, a proper Puppet (or Chef, or Buildout) would rely heavily on Fabric or Capistrano for the final deploy. The conventional wisdom is that incremental Continuous Deployment is better than none. When a Mezzanine infrastructure needs to scale beyond a single host, you can address it confident in the knowledge that the single-host deploy is rock solid.

Current rating: 4.4


Comments