deploying your app to heroku

this is part of a series of posts i wrote for my software engineering course at university of texas.

in my summer software engineering course we're making a website of information about world crises using django. we were experiencing some trouble with deploying our apps. the original setup was a single university-hosted apache instance with mod_wsgi loaded and configured in our local directories using .htaccess files. it's difficult to administer a system in which you have limited privileges for several reasons:

one of my classmates was sharp enough to suggest we use heroku for future projects. as i've mentioned before, my professor is good at encouraging us to use modern technologies in this course, so he agreed. hopefully his decision is reinforced by a good experience with heroku.

reasons to use heroku

the trend these days seems to be towards hosted solutions. whether it's version control, continuous integration, or deployments, small companies especially have a lot to gain by focusing their efforts on their core business and spending their money on hosted solutions rather than devops personnel. the heroku-style of platform-as-a-service also seems to be popular, as other companies are following suit with platforms like openshift and nodejitsu (though not all of them experience the same hot drama).

if nothing else, using heroku on these projects will give you valuable experience, but i think it can also make your life easier. the pain points i outlined above are all addressed:

how to

our use-case is strait forward, so i found heroku's docs spot on. i'll end up repeating a lot of their (superior) tutorial below, but i'll try to customize it a bit for our class and maybe inject a bit of hopefully-unbiased opinions. we've all got our projects set up already, so we're not starting from scratch as their article presumes.

opinion #1: develop locally on your laptop -- if you're main computer is mac or linux this is especially blissful. either way you've probably already got your dev environment setup from the first project, so just stick with that because life is too short to spend it wrangling $PYTHONPATH's.

bootstrap

skip the mkdir hellodjango && cd hellodjango part in heroku's tutorial and just make your virtualenv. virtualenv is basically a local python installation for nothing but your project. that may seem weird but it's actually colossally awesome because it saves you from dependency hell which is a real place regardless of your religious inclinations. run the commands:

$ virtualenv venv --distribute
$ source venv/bin/activate

don't check the venv directory into your repo. in fact you may want to make a .gitignore file right now:

venv
*.pyc
staticfiles

install prerequisite dependencies

$ pip install django-toolbelt

this installs a bunch of python modules you need to run django apps on heroku to your newly created virtualenv. while you're at it, install the project dependencies:

$ pip install MySQL-python minixsv

pip install any other libraries you dig, and then record them for heroku by running:

$ pip freeze > requirements.txt

at this point you may notice that heroku uses postgresql by default instead of mysql. you should probably just use with postgres since it's free and default, and up to this point there hasn't been any reason to have written custom sql, rather you should have just been using django's database management facilities.

opinion #2: keep it database agnoistic -- i believe good dependency inversion principles (which we'll learn about later in the course) would instruct us to write an app that works with a mysql, postgres, or sqlite backend (downing if you're reading this i'm sorry if i'm misleading; i assume you'll have the chance to correct me in class). the thing is we're not doing anything novel and we're not going to need to scale these apps, so it would be awesome if we could just write some really robust code that works with a range of databases.

i ended up using 3 different backends for this assignment, which is stupid, but in-memory sqlite was fastest, so i typically used that for unit tests, mysql was a requirement, so i set that up initially, and postgres is free and default on heroku, so i deployed to that. like i mentioned, i didn't write a line of sql, it was all just a matter of tweaking django's settings.py file.

update settings.py

i slightly amended the heroku article's suggestions to this:

  
    if 'test' in sys.argv:
        # use in-memory db for unit testing b/c it's effin slow otherwise
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': 'crisis',
                'USER': 'crisisuser',
                'PASSWORD': 'bacon',
                'HOST': '',
                'PORT': '',
            }
        }
    elif 'PRODUCTION' in os.environ:
        # this is the heroku environment
        import dj_database_url
        DATABASES = { 'default': dj_database_url.config() }
    else:
        # and this is what you'll get when u run ./manage.py runserver
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',
                'NAME': 'crisis',
                'USER': 'crisisuser',
                'PASSWORD': 'tupac',
                'HOST': '',
                'PORT': '',
            }
        }

    # Honor the 'X-Forwarded-Proto' header for request.is_secure() (whatevr
    # that means)
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

    # Allow all host headers
    ALLOWED_HOSTS = ['*']

    # Static asset configuration
    import os
    STATIC_ROOT = 'staticfiles'
    STATIC_URL = '/static/'

    STATICFILES_DIRS = (
        os.path.join(PROJECT_PATH, 'static'),
        os.path.normpath(os.path.join(PROJECT_PATH, '../crisis_app/static')),
    )
  

you can see the 3 different database configurations. the cool thing about heroku is you can run the unit tests locally with sqlite or remotely in the heroku environment quite easily.

update wsgi.py

they also instruct you to update you wsgi.py file:

from django.core.wsgi import get_wsgi_application
from dj_static import Cling

application = Cling(get_wsgi_application())

deploying to heroku

this is where it actually happens:

$ git add .
$ git commit -m "added heroku stuff"
$ heroku create
$ git push heroku master
$ heroku config:set PRODUCTION=1

that was it. much easier than zweb, right? now you can turn on your app and visit the site:

$ heroku ps:scale web=1
# maybe wait a min?
$ heroku run python manage.py syncdb
$ heroku open

notice that running all of the django admin commands are the same as before except you prepend heroku run if you want them to run on your deployed app.

conclusion

awesome things happen when you make the right thing the easy thing, and heroku exemplifies this by baking rock-solid engineering principles into every aspect of website deployment and hosting. i'd definitely recommend using heroku for the following phases of the project.

blog comments powered by Disqus
© aaron stacy 2013, all rights reserved