title: Hosting Django Apps in Kubernetes [WIP] --- pub_date: 2022-06-03 --- author: ungleich django team --- twitter_handle: ungleich --- _hidden: no --- _discoverable: yes --- abstract: Django running on top of k8s --- body: ## Situation At ungleich we are hosting quite a lot of Django applications. As of 2022-06-03, most of them are still deployed on a traditional VM based setup. We are using this blog entry to document a possible blueprint and the progress of migration at ungleich. ## General design Our Kubernetes clusters usually use [ArgoCD](https://argo-cd.readthedocs.io/en/stable/) for deployments, so Django applications should potentially be defined the same way. Most of our kubernetes applications are defined in helm charts and thus the "general django application" should probably also be defined in a helm chart. ## Freedom of choice While as a hoster it might be tempting to define a specific image that Django applications should be using (like [python](https://hub.docker.com/_/python)), but we want to give us and our customers the freedom to choose the image they use themselves. It might potentially even come from a private registry. ## Interface definition All of our Django applications are using Postgresql for storing data. Postgresql is used by quite some other applications that we deployed in k8s, so this is a no-brainer. Django hosting at ungleich, even in k8s, will be based on Postgresql. Static data of Django applications can easily be stored on a PVC. This has the drawback that filesystem PVCs based on ceph block devices are usually RWO and thus in case of restart, there will be a short downtime. This is, generally speaking probably accepted, like a deploy would have caused a short downtime on a VM as well. However alternatives would be a shared filesystem (such as NFS/CephFS), but they are usually slower than dedicated block devices - so reliability can be traded against speed. Maybe we offer both options or add an NFS server as an option to our Django Hosting. ## Django startup / processes On startup, Django will need to ensure the database schema has been upgraded to the latest version. so something like `python manage.py migrate` should probably be called in an InitContainer for most apps. We could specify that the customer provided container supports multiple commands: - /init - anything that needs to be done *once* on startup - /run - something that runs the actual site Some django apps however utilise Celery or [Django Q](https://django-q.readthedocs.io/en/latest/) for async tasks. We don't know which system is used, but it would be easy to add a flag to the hosting whether or not a third container should be utilised. Thus we could define: - If async container is defined, enable it and run /async (or similar) ## Secrets Django applications usually have some kind of secrets and most Django applications have DIFFERENT types of secrets. Thus defining a specific environment variable does not seem to be a smart idea. Instead, we should probably offer to store secrets in something like [SealedSecrets](https://github.com/bitnami-labs/sealed-secrets). Database connection information is provided by default and is cluster/app specific. ## Deployment We are likely going with a git-ops style deployment in which everything is defined for the Django app. This repository is read write for each client/customer. This probably includes: - (Possible encrypted) Secrets - Image definitions - Maybe even (part of) a pod definition? Some parameters are likely to be stored in a different, ungleich only writable repository, such as: - Size of RAM - Number of CPUs - Storage size (Postgresql, Static files, ...) ## Status This document is still WIP and will be used as a basis for deploying our own Django apps first.