Django deployment best practices
- 2. You mission, should you choose to accept it:
Make your application run with 1 click,
everywhere it counts, every time.
Make your application run exactly the same way,
everywhere it counts, every time.
Prove that the above are true.
Reproducibility
- 3. Reproducible Infrastructure
Reproducible Systems
Reproducible [Django] Applications
Dependencies
Configuration
Data
Deployment
Steps to Reproducibility
- 4. Systems: Virtual or Physical
Power Management
Console
Backup / Restore
Network Layer
Load Balancing
Security
Performance
Infrastructure
- 5. A Stable Foundation
Stable Operating System
Reproducible Installation
Kickstart Install
Base Image
Configuration Management
Puppet, Chef
Packaging
Systems
- 6. What Python?
How will you install packages?
How will you make it reproducible?
Database?
Backup / Restore
Failover
Performance
Caching?
HTTP
Objects
Queuing?
SSL?
Application Environment
- 7. It’s just code, right?
Probably Not
Application
- 8. Just use pip! lib/pypi-cache:
Django-1.4.3.tar.gz
Not so fast.
Lots of external requirements.txt:
dependencies Django==1.4.3
Eliminate them
with a local Type this:
pip install --no-index --find-links
cache
file://$(PWD)/lib/pypi-cache -r
Don’t’ forget requirements.txt
virtualenv!
Dependencies
- 9. settings.py settings.py:
No logic allowed! from urlparse import urljoin
‚unknown‛ from os import environ
import times import dj_database_url #courtesy of Heroku
Critical
Local_settings.py DATABASES =
More of the same dj_database.config(default=‚xxx‛)
BASE_URL = environ*‘DJANGO_BASE_URL’+
Consider pushing STATIC_URL = urljoin(BASE_URL, ‘static’)
configuration in MEDIA_URL = urljoin(BASE_URL, ‘media’)
externally via
environ, Run this:
configparser, etc DJANGO_BASE_URL=http://cdn.com/base/
django-admin.py runserver
Configuration
- 10. Fixtures. Consider fixtures/polls.py:
writing them in from ..models import Poll, Choice
def apply():
Python! p = Poll.objects.get_or_create(q=‘test q’,
Testable defaults=dict(pub_date=‘1980-01-01))
c1 = Choices.objects.get_or_create(poll=p,
More resilient to
choice_text=‘option 1’)
change
Less pk pain fixtures/tests/test_poll.py:
Class PollFixtureTestCase(TestCase):
def test_fixture_applied():
Migrations! self.assertEquals(Poll.objects.get(q=‘test q’)
Test them
Application: Database
- 11. management/__init__.py:
from django.db.models.signals import post_syncdb
from south.signals import post_migrate
from .. import models, fixtures
def update_poll_fixtures(verbose=True):
fixtures.poll.apply()
if verbose:
print "Updating dynamic fixtures for profile”
def update_fixtures_post_syncdb(sender, **kwargs):
update_poll_fixtures()
def update_fixtures_post_migrate(app, **kwargs):
if app == ’polls':
update_poll_fixtures()
post_syncdb.connect(fixtures_post_syncdb, sender=models)
post_migrate.connect(fixtures_post_migrate)
- 12. Interwebs
Back Up Everything!
Move files to Green
Fabric?
RPM?
Tar?
Git?
Rsync?
App App Deploy / Test
Update Stuff
syncdb
apply fixtures
Test Green!
Flip/Flop Blue/Green Data
(Roll back to Blue)
Deployment: Blue/Green
- 13. Erik LaBianca, WiserTogether, Inc.
erik.labianca@gmail.com
@easel
https://linkedin.com/in/eriklabianca/
https://slideshare.net/easel1/
https://github.com/easel/
https://github.com/WiserTogether/
Questions?
Editor's Notes
- ----- Meeting Notes (2/12/13 12:10) -----json fixtures: primary key problems, skips model.save, fixtures get of date when model changes