Deploying Serverless Django with Zeit and RDS Postgres

Deploying Serverless Django with Zeit-Now and AWS RDS Postgres

Deploying Serverless Django with Zeit and RDS Postgres

We will see how to deploy a Django application using Zeit Now and use RDS Postgres as the DB.

Zeit Now is a serverless deployment platform with its own CLI and a desktop app.

RDS Postgres is the open source relational database for Postgres by AWS.

Get Zeit Now

1) First we have to create an account in Zeit.

2) Then we have to install the Now CLI or the Now Desktop App(which includes CLI) .

we can download the Now Desktop which does not require Node.js. Now Desktop comes with Now CLI (our command line interface)

or we can install Now Cli using npm


$ npm install -g now

To check if Now CLI has been installed

$ now --version

Go to Django app

After installing Zeit Now, let us set up our django project, here we used Pollsapi (https://github.com/agiliq/building-api-django) as the django project.

Configure Django Settings

We have to add our host to the ALLOWED_HOSTS in the setting.py file

...
ALLOWED_HOSTS = [".now.sh"]  # add this subdomain

Configure Django for S3

We will use AWS S3 bucket to serve our static files, so let us configure Django for S3

$ pip install django-s3-storage

and also add it in the requirements.txt file.

...
django-s3-storage==0.12.4
...

Now update the settings.py file to add ‘django_s3_storage’ to INSTALLED_APPS

INSTALLED_APPS = (
          ...,
          'django_s3_storage',
     )

and also add these lines at the bottom

S3_BUCKET = "now-staticfiles1234"

STATICFILES_STORAGE = "django_s3_storage.storage.StaticS3Storage"

AWS_S3_BUCKET_NAME_STATIC = S3_BUCKET

STATIC_URL = "https://%s.s3.amazonaws.com/" % S3_BUCKET

Push the static files to the cloud
$ python manage.py collectstatic 

Setup now.json

Now go inside the pollsapi folder in this repo, and create a file named now.json, and add the following:

{
  "version": 2,
  "name": "django-pollsapi",
  "builds": [
    {
      "src": "index.py",
      "use": "@contextualist/python-wsgi",
      "config": { "maxLambdaSize": "60mb" }
    }
  ],
  "routes": [{ "src": "/.*", "dest": "/" }]
}
  • "version" Specifies the Now Platform version the deployment should use and to work with. Type is String.

  • "name" is used to organise the deployment into a project. Is is also used as the perfix for all new deployment instances. Type is Number.

  • "builds" Builders are modules that take a deployment’s source and return an output, consisting of either static files or dynamic Lambdas.

    The builds property is an array of objects where each object is a build step, including a src and a use property, at least. If our project has source files that require transformation to be served to users, Builders enable this ability when deploying.

    Builds object consists of:

    • "src" (String): A glob expression or pathname. If more than one file is resolved, one build will be created per matched file. It can include * and **.
    • "use" (String): A npm module to be installed by the build process. It can include a semver compatible version (e.g.: @org/proj@1).
    • "config" (Object): Optionally, an object including arbitrary metadata(like maxLambdaSize etc) to be passed to the Builder.

    We are using builder - "@contextualist/python-wsgi" as we want python with wsgi.

  • "routes" consists of a list of route definitions.

    • "src": A regular expression that matches each incoming pathname (excluding querystring).
    • "dest": A destination pathname or full URL, including querystring, with the ability to embed capture groups

Let us create a file named index.py, and copy all lines from wsgi.py to this file


import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pollsapi.settings")
app = get_wsgi_application()  # application = get_wsgi_application()

Now we have to rename application to app, as the builder will search for the app to run.

After this add these lines to the the index.py file

...
os.system("python manage.py migrate")
os.system("python manage.py runserver")

At present we cannot change the python version of the Zeit Now environment(which is python 3.4), but this feature will be added in the future.

Now deploy the app

$ now
> Deploying ~/building-api-django/pollsapi under [email protected]
> Using project django-pollsapi
> Synced 1 file (234B) [1s]
> https://django-pollsapi-4l2pyh2um.now.sh [v2] [in clipboard] [2s]
┌ index.py        Ready               [1m]
└── λ index.py (20.53MB) [sfo1]
> Success! Deployment ready [1m]

Now go to the url, we will see that our project is running

Now we have to link it with the Database

Linking with RDS Postgres

We are using AWS RDS Postgres as our Database.

So first create an RDS postgres instance (which also comes in Free tier) and copy the endpoint (which we will use to link in the DATABASES in settings.py file)

so let us add postgres adapter to our requirements.txt file

psycopg2==2.7.7

and change the settings.py file for postgres


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'nowdb', # dbname
        'USER': 'now_admin', # master username
        'PASSWORD': 'nowadmin', # master password
        'HOST': 'nowdb.chc62yjp9.us-east-2.rds.amazonaws.com', # Endpoint
        'PORT': '5432',
    }
}


But before using postgres in our Django App,

we have to first download a custom compiled psycopg2 C-library for Python from https://github.com/jkehler/awslambda-psycopg2

Using psycopg2 via requirements.txt will not sufficient for lambda, as psycopg2 C library for Python is missing in default lambda.

As Zeit Now uses AWS Lambda to deploy our project, we need to use this custom pre-compiled library to use postgres.

First we have to download the repository and copy the folder psycopg2-3.6 to our project and in the same level as our now.json and rename the folder from psycopg2-3.6 to psycopg2.

this will make our app work with the Postgres-DB

After this we have to create an admin-user for our django-app so that we can access the admin


$ cd polls
$ mkdir management
$ cd management
$ touch __init__.py
$ mkdir commands
$ cd commands
$ touch __init__.py
$ touch create_admin_user.py
# polls/management/commands/create_admin_user.py
import sys
import logging

from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from django.conf import settings



class Command(BaseCommand):
    help = 'Creates the initial admin user'

    def handle(self, *args, **options):
        if User.objects.filter(username="admin").exists():
            print("admin exists")
        else:
            u = User(username='admin')
            u.set_password('adminpass')
            u.is_superuser = True
            u.is_staff = True
            u.save()
            print("admin created")
        sys.exit()

this command will create the admin user if it does not exists

let us update the index.py by adding the command to create the admin user below the migrate command

...
os.system("python manage.py migrate")
os.system("python manage.py create_admin_user")     # add this line
os.system("python manage.py runserver")

Now let us deploy the app with the updated database settings and the custom postgres library

$ now
> Deploying ~/building-api-django/pollsapi under [email protected]
> Using project django-pollsapi
> Synced 1 file (234B) [1s]
> https://django-pollsapi-1asdsdfum.now.sh [v2] [in clipboard] [2s]
┌ index.py        Ready               [1m]
└── λ index.py (20.53MB) [sfo1]
> Success! Deployment ready [1m]

we can check the logs of the deployment by adding /_logs after our url like https://django-pollsapi-1asdsdfum.now.sh/_logs

Let us check the url

https://django-pollsapi-1asdsdfum.now.sh

https://django-pollsapi-1asdsdfum.now.sh/admin

Now let us login to our admin

Now our Django app is linked to postgres and deployed using Zeit Now.


This is part 2 of Serverless Deployments for Django,

Check out part 1 Deploying completely serverless Django with Zappa and Aurora Serverless

Check out part 3 Deploying completely serverless Django with Apex Up and Aurora Serverless

Check out part 4 Deploying Django in AWS Fargate and using Aurora Serverless as the database

Thank you for reading the Agiliq blog. This article was written by anmol akhilesh on Feb 11, 2019 in Serverless DjangoZeitLambdaAWS RDSPostgres .

You can subscribe ⚛ to our blog.

We love building amazing apps for web and mobile for our clients. If you are looking for development help, contact us today ✉.

Would you like to download 10+ free Django and Python books? Get them here