Welcome back to part 4 of the series! Previously we dived into some details of building the backend. In this post we will cover the usage of Elastic Beanstalk (EB), an AWS service that provides an easy way to deploy Django web applications.
It takes care of provisioning, load balancing and setting up a whole host of other things. You can think of it as an alternative to Heroku.
Table of Contents
- Pre-requisite Knowledge
- Caveats
- Deployment Overview
- Building the React
- Configuring Django
- Deploying to Elastic Beanstalk
- Bitbucket Pipelines
- Trailer
Pre-requisite Knowledge
You can read/watch a whole bunch of tutorials about AWS, but EB is likely not one of the first services mentioned in introductory tutorials. Despite this, I came to quite like it.
I spent a bit of time trying to configure logging for the application but couldn’t quite get it to work. Eventually I just didn’t have enough time left to figure it out.
That being said, its a good way to git push -u deploy-instantly
without too much hassle.
A basic understanding of AWS makes EB easier to follow - it’s is fairly independent of others services. You can add CloudWatch (logging) and RDS (database) services to it but thats totally optional.
Caveats
The main caveat to Elastic Beanstalk is that the documentation isn’t updated. We’re told to use the latest version, but much of the documentation is actually for the older version.
The logging documentation I followed didn’t quite work for me. There were also some other guides I read through but had to tweak to make it work.
Deployment Overview
At a high level, we build the React code and package it inside the backend code. Then we render the /
root as a “template” inside Django.
Building the React
Inside frontend/package.json
we add the following command:
{
...
"scripts": {
...
"relocate:prod": "env-cmd -f .env.prod react-scripts build && rm -rf ../backend/build && mv -f build ../backend && mkdir -p ../backend/fe_assets/ && cp public/*.png ../backend/fe_assets/",
},
...
}
Here we build the codebase for deployment using env-cmd
to ensure that we set the right environment variables (see part 2).
Then we move these build files into backend
. The last part is copying any public images across so that they are rendered in the application.
backend/
├─ content/
│ ├─ views.py
│ ├─ ...
├─ Makefile
├─ build/ (from frontend)
│ ├─ index.html
│ ├─ css/
│ ├─ js/
├─ app_name/
│ ├─ settings/
│ │ ├─ base.py
│ │ ├─ dev.py
│ │ ├─ ...
Configuring Django
So now the build files are integrated into the backend, we need to point the Django root to the React root.
# /backend/app_name/urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, re_path, include
from django.shortcuts import render
def render_react(request):
# Note to reader: This index.html is in /build!
return render(request, "index.html")
urlpatterns = [
path("admin/", admin.site.urls),
...
path("api/app_user/", include("app_user.urls")),
path("api/content/", include("content.urls")),
# Serving React as static index.html
re_path(r"^$", render_react),
re_path(r"^(?:.*)/?$", render_react),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
And set Django settings to read from the /build
path.
# /backend/app_name/settings.py/prod.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
# Tell Django where to find Reacts index.html file
"DIRS": [os.path.join(BASE_DIR, "build")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
STATICFILES_DIRS = [
# Tell Django where to look for React's static files (css, js)
# TEMPLATES already tells Django where to find the index.html
os.path.join(BASE_DIR, "build/static"),
os.path.join(BASE_DIR, "fe_assets"),
]
So now you can check if things work locally by running
python3 manage.py runserver --settings="app_name.settings.prod"
Deploying to Elastic Beanstalk
Now that we’re ready to go, we can configure our repo for deployment to elastic beanstalk.
Before you start, make sure you register for an AWS account and create a role that allows you to deploy to Elastic Beanstalk, and setup your IAM permissions on your computer.
If you haven’t worked with IAM before, it might be good to watch a few tutorials to familiarise yourself.
Before you cdeploy settings up an SSH connection to your EB environment is very useful for connecting to the prod EC2 to debug post-deployment.
Once you set this stuff up, you can
# 1) Create the artifact
cd ./frontend && CI=false npm run-script relocate:prod && cd ../backend
# 2) Initiate the app
eb init --platform python-3.7 app-name --region ap-southeast-2
# 3) Create the environment
eb create app-name-env
# 4) Deploy! The artifact specified will be sent.
eb deploy
# 5) If you need to rebuild the environment
aws elasticbeanstalk rebuild-environment --environment-name app-name-env --region ap-southeast-2
From there, EB will build the environment automatically and provide a URL for you to check the site!
Bitbucket Pipelines
For the project, we actually used bitbucket
pipelines as our CI/CD to deploy to dev and prod.
The deployment is defined in a yaml
file and is similar in flow to what you would see in Github actions.
Don’t forget to add a domain name to the website!
Trailer
In the closing chapter, we’ll discuss the key takeaways from this project and whether I would recommend this tech stack.
Stay tuned for the final part!
UPDATE: You can read the final part here