In the past few weeks, Rich from gun.io released a shim library that maps API Gateway requests to Python’s WSGI interface. Python’s WSGI is a specification that maps HTTP requests to an interface to be consumed by Python web apps. Django, Flask, Pyramid, and just about every other web framework depend on WSGI to talk to the outside world.
Zappa provides a WSGI interface to be run inside of the Lambda runtime and receive API Gateway requests.
In this post, we’ll take the Django version of Zappa for a spin with a demo app I wrote to compare the major Python web frameworks. Next time, we’ll add RDS and make our Lambda VPC-enabled.
Getting Started
To view the initial version of the application, clone the repo and view
the v1.0
tag.
$ git clone TODO
$ cd TODO
$ git checkout v1.0
Optionally, you can try running it locally to see how it works. There’s no magic here: it’s a form that takes your name and what you had for lunch.
$ pip install -r requirements.txt
$ python manage.py runserver
# go to the page at http://localhost:5000/
In your browser, you should see something like this.
The application is ready, and you have the dependencies, now let’s upgrade this to a project that can run without servers.
Using Django-Zappa
To work with django-zappa, we need to make a few changes to our app’s configuration, but no code changes will be necessary. In Amazon lingo, this is a “lift and shift” style migration.
First, we need the django-zappa module installed, to do so add django-zappa
to the requirements.txt
file and re-run pip install -r requirements.txt
.
Next we have to add the custom middleware at the head of the
queue so it gets the first crack at modifying incoming requests. This is
critical because no other middleware is going to be able to understand an
incoming API Gateway event, and it’s up to django-zappa to translate it into
the request object Django is used to reading. Replace the existing
MIDDLEWARE_SETTINGS
and INSTALLED_APPS
variables in
hello_django/settings.py
with this:
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'wut4lunch',
'django-zappa', # lets you use the django-zappa commands like `deploy`
)
MIDDLEWARE_CLASSES = (
'django_zappa.middleware.ZappaMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
The changes above make it possible to use django-zappa, but we still need to define environments where the app can be deployed to. For now, we’ll just define a “testing” environment. In a production application, you’ll want to have separate test, stage, and prod environments.
ZAPPA_SETTINGS = {
'testing': {
's3_bucket': 'tmp.serverlesscode.com',
'settings_file': '~/code/django-zappa-example/hello_django/settings.py',
}
}
We’re almost ready to deploy now, all we need is a database. The SQLite3 database I have committed to the repo should do - obviously it won’t survive in Lambda but that’s ok for now.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/var/task/test.db',
}
}
The /var/task
is where the deploy zipfile is extracted, so that’s where our
SQLite database will be by default.
Deploying
Now it’s time to deploy!
$ ./manage.py deploy testing
Creating ZappaLambdaExecution IAM..
Packaging project as zip..
Uploading zip (9.8MiB)...
Creating API Gateway routes..
Deploying API Gateway..
Your Zappa deployment is live!: https://REDACTED-api.us-east-1.amazonaws.com/testing
Open the URL in your browser and you should be greeted with a page to share what you had with your friends. Note how long it takes for that first page load, for me the first page load takes 1.6 seconds but subsequent calls are just 280 milliseconds. The slower first call is the time it takes for Lambda to warm up and get the new code.
The downside here is, of course, that the app doesn’t actually work. If you try to save your name and food, you get an error like this.
Next Steps
In the next post, we’ll cover using Lambda’s recent support for VPC connections and RDS to make our data persistent.
Get the code so far on the sqlite-db
repo branch and check out
Django-Zappa on Github. Keep up with future posts via RSS. If you have suggestions, questions, or comments
feel free to email me, ryan@serverlesscode.com
.