In my last Devlog, I got my Django website hosted on Elastic Beanstalk by uploading a packaged zip of all the application code and the static files including my sample photos. While it was a success and it got the website live, it's not... comfortable.
My code has to be manually zipped up (along with a handful of huge jpegs which bloat out my repo). Then they get pushed manually to Elastic beanstalk via the AWS console.
Now look... no one's saying you must only ever use Terminal to get around, but the thought of clicking my mouse that many times just to push to production makes my little DevOps tummy hurt.
The Django tech stack ✨idea✨
Configuring prod data in dev
So this is just so I don't have to muck around setting up and spending money on a DB. Photo details are all stored in SQLite. That gets uploaded with my code to the repo. I then have Github Actions push that to Elastic Beanstalk, as my manager says, "auto-magically". No dev DB/prod DB disparity. It's just my website. For this moment, the data can be the same.
Now what about the photos and any other static files? I should move those to S3.
Firstly, I've set up a bucket with public read access, uploaded my photos there, set up Django to map the media URLs to the s3 bucket. Then I'll sit back and use all the spare time I now have googling ways I could've done it better.
MEDIA_URL = "/photo_files/"
if DJANGO_WEBSITE_ENVIRONMENT == "PROD" or DJANGO_WEBSITE_ENVIRONMENT == "BUILD":
AWS_STORAGE_BUCKET_NAME = "mountainbean-photos-bucket-shepherds-pie"
AWS_S3_REGION_NAME = "ap-southeast-2"
AWS_ACCESS_KEY_ID = getenv("S3_WRITE_USER_ACCESS_KEY")
AWS_SECRET_ACCESS_KEY = getenv("S3_WRITE_SECRET_ACCESS_KEY")
AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com"
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
"location": "media"
},
},
"staticfiles": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
"location": "static"
}
}
}
Using the django_storages library, I've configured the public S3 bucket as a backend with a service role that has write-access permission on the bucket. An admin with access to post a new photo on the website (me) now has permissions to upload to the bucket from the website and that photo can be viewed by any visitor via a url to the photo location in the public bucket.
So what's with the if() statement?
When the website is being hosted by elastic beanstalk, I want Django to give out the correct urls to the photos in the S3 bucket. Likewise, when Github actions is deploying the code (there the environment variable will be BUILD) and running collectstatic, I want django to send the static files to S3, not just collect them into a local directory. However, when I'm just working on this on my machine, I don't want those files going anywhere.
So I set: MEDIA_ROOT = BASE_DIR / "uploads"
in my Settings.py and:
if settings.DJANGO_WEBSITE_ENVIRONMENT != "PROD":
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
Map that directory to MEDIA_URL in my root urls.py so Django will send all my uploaded images there when I'm just in localhost.
The catch
I have one manual step left. When I make a commit or merge to master (triggering my Github workflow) I have to remember to copy my uploads folder to the public S3 bucket under the media key.
Lastly, I should address the issue of uploading my full-size images to the web. I've just shifted the problem from my repo to the S3 bucket. I still have very large JPEGs in my public S3 bucket that I (and any other visitor) will be requesting every time I visit my site.
AWS gives you a free 100GB of data transfer out of S3 per month. But if I'm not careful, my big fat JPEGs might use that up quickly. I'll look at resizing uploads next...