{"id":18803891,"url":"https://github.com/yvsssantosh/django-on-k8s","last_synced_at":"2026-03-07T16:31:43.932Z","repository":{"id":96340050,"uuid":"141138701","full_name":"yvsssantosh/django-on-k8s","owner":"yvsssantosh","description":"An end to end tutorial to run a Django Web Application having a PostgreSQL database in Kubernetes","archived":false,"fork":false,"pushed_at":"2018-07-27T13:09:26.000Z","size":2357,"stargazers_count":39,"open_issues_count":0,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-13T18:45:26.906Z","etag":null,"topics":["django","django-rest-framework","gke-cluster","google-cloud-sdk","helm","ingress","kubernetes","kubernetes-deploy","kubernetes-deployment","kubernetes-ingress","kubernetes-pods","kubernetes-setup","pollsapp","postgresql"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yvsssantosh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-16T12:56:30.000Z","updated_at":"2025-02-04T00:20:59.000Z","dependencies_parsed_at":"2023-04-28T17:47:48.724Z","dependency_job_id":null,"html_url":"https://github.com/yvsssantosh/django-on-k8s","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/yvsssantosh/django-on-k8s","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yvsssantosh%2Fdjango-on-k8s","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yvsssantosh%2Fdjango-on-k8s/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yvsssantosh%2Fdjango-on-k8s/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yvsssantosh%2Fdjango-on-k8s/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yvsssantosh","download_url":"https://codeload.github.com/yvsssantosh/django-on-k8s/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yvsssantosh%2Fdjango-on-k8s/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30221507,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T14:02:48.375Z","status":"ssl_error","status_checked_at":"2026-03-07T14:02:43.192Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","django-rest-framework","gke-cluster","google-cloud-sdk","helm","ingress","kubernetes","kubernetes-deploy","kubernetes-deployment","kubernetes-ingress","kubernetes-pods","kubernetes-setup","pollsapp","postgresql"],"created_at":"2024-11-07T22:37:21.288Z","updated_at":"2026-03-07T16:31:43.894Z","avatar_url":"https://github.com/yvsssantosh.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Django, PostgreSQL on Kubernetes (GKE)\n\nThis tutorial should help to deploy a django application on a Kubernetes Cluster. Before starting this tutorial, the user is expected to have basic knowledge of GKE, Django, PostgreSQL and Docker\n\n## Understanding Kubernetes\n\nBefore we jump into the tutorial, lets have a basic understanding of what kubernetes is and how will it be useful for us to deploy our django application.\n\n### What is Kubernetes?\n\n**Kubernetes**, at its basic level, is a system for running \u0026 co-ordinating containerized applications across a cluster of machines. It is a platform designed to completely manage the life cycle of containerized applications and services using methods that provide predictability, scalability, and high availability.\n\nTo know more about kubernetes, visit [here](https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes)\n\nMoving on, as a part of this tutorial we'll be deploying Polls API, from [here](https://www.github.com/yvsssantosh/django-polls-rest)\n\n### Local Deployment of Polls API\n\nLet's first clone our sample django application from \n\n```\ngit clone https://github.com/yvsssantosh/django-polls-rest.git\n```\n\nJust to make sure we're on master branch, run the command `git checkout master`\n\nTo test the application locally, let's create a virtual environment, and test the server\n```\n# Creating a virtual environment\nmkvirtualenv pollsapi\n\n# Installing current project requirements\npip install -r requirements.txt\n\n# Connect to postgres\nexport POSTGRES_USER=pollsdb\nexport POSTGRES_DB=polls_admin\nexport POSTGRES_PASSWORD=polls_password\nexport POLLSAPI_PG_HOST=127.0.0.1\n\n# Running migrations\npython manage.py migrate\n\n# Start the local server\npython manage.py runserver 0.0.0.0:8000\n```\n\nNow that we have tested on local server, lets create a new kubernetes cluster and deploy our application on it.\n\n### Setting up Google Cloud SDK\n\nFor instructions to setup Google Cloud SDK navigate to https://cloud.google.com/sdk\n\nRead the installation instructions and setup accordingly. Once done, check the installation status by running the command\n```\n$ gcloud -v\n```\nIt should show an output similar to this:\n\n![](./images/check_installation.png)\n\n### Setting up kubectl command line\n\nTo manage and maintain a kubernetes cluster from our desktop/laptop we need to setup kubectl command line. It can be done using the command\n\n```\ngcloud components install kubectl\n```\n\nOnce the installation finishes, we should see something like this:\n![](./images/kubectl_install.png)\n\n### Creating a kubernetes cluster\n\nNavigate to `https://console.cloud.google.com`. Select an existing project or create a new one, based on requirement.\n\nThen click on Kubernetes Engine from the navigate menu which would result in the following page\n\n![](./images/before_creation.png)\n\nCreate a new cluster. I've created a cluster based on the following settings\n```\nCluster name        :   pollsapi\nLocation            :   Zonal\nZone                :   asia-south1-a\nCluster Version     :   1.9.7-gke.3 (default)\nMachine Type        :   Small (1 shared CPU with 1.7GB Memory)\nNode Image          :   Core OS (cos)\nSize                :   2\nBoot Disk Size      :   20GB per Node\n\n#######################################################################################\n## Only for testing purposes as preemptible nodes are NOT recommended for Production ##\n#######################################################################################\n\nPreemptible Nodes   :   Yes\n```\n\n`Please be patient as it takes some time to create the cluster`\n\nOnce the cluster is created, we'll be able to see a tick mark beside the name of the cluster.\n\n![](./images/after_creation.png)\n\nNow click on `Connect` and copy the command shown, and paste it in terminal.\n\n![](./images/connect_success.png)\n\nOnce connected run the command `kubectl get all`.\n\n![](./images/kubectl_success.png)\n\nNow that the cluster is up and running, lets package our application into a containerized one using docker.\n\n### Setting up Google Container Registry using Docker\n\nConfiguring docker with gcloud:\n```sh\ngcloud auth config-docker\n```\n![](./images/docker_configure.png)\n\nOnce docker is configured, we are ready to build the image.\n\n```sh\n# Build the image\n# Common format to push an image to google container registry is gcr.io/$PROJECT_ID/$IMAGE_NAME:$TAG\n\nexport PROJECT_ID=YOUR_PROJECT_ID_HERE\nexport IMAGE_NAME=YOUR_IMAGE_NAME_HERE\nexport TAG=YOUR_IMAGE_TAG (optional, default is `latest`)\n\n# In my case, giving the tag as v1.0.0 (default is latest)\ndocker build -t gcr.io/test-gcp-208915/pollsapi:v1.0.0 . \n# (Note the . in the end)\n\n# Push the image\ndocker push gcr.io/test-gcp-208915/pollsapi:v1.0.0\n```\n\nOnce the image has been pushed, paste the push URL in browser. It will ask you to sign in into google account which has been used to configure this cluster (if not already signed in).\n\n![](./images/docker_success_push.png)\n\nSince our image has been uploaded sucessfully, we need to setup the database next.\n\n### Setting up Helm Package Manager\n\nThe simplest way of setting up PostgreSQL on kubernetes is with the help of [Helm Package Manager](https://github.com/kubernetes/helm)\n\nFor mac users, the command to install helm (using brew) is:\n```sh\n# Install Helm\nbrew install kubernetes-helm\n\n# Setup Helm\nhelm init\n```\n\n* `Note`: Often during package installation i.e., `helm install --name MY_RELEASE stable/PACKAGE_NAME` a common error is generated explaining tiller not having access to create cluster role bindings. This usually happens if the user logged inside Google Cloud SDK doesn't have proper access to create role bindings or issues with helm installation.\n\nIf that error occurs, then run the following commands:\n\n```sh\n# Completely uninstall helm\nhelm reset --force\n\n# Remove directories created by helm\nsudo rm -r ~/.helm\n```\n\nOnce helm is completely removed, create a clusterrolebinding and a serviceaccount for helm using the code below\n\n`rbac-config.yaml`\n\n```yaml\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: tiller\n  namespace: kube-system\n---\napiVersion: rbac.authorization.k8s.io/v1beta1\nkind: ClusterRoleBinding\nmetadata:\n  name: tiller\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: cluster-admin\nsubjects:\n  - kind: ServiceAccount\n    name: tiller\n    namespace: kube-system\n```\n\nCreate a file named rbac-config.yaml and run the following using kubectl command line\n```sh\n# Creating Service Account and ClusterRoleBinding Tiller\nkubectl create -f rbac-config.yaml\n```\n\nOnce this is sucessfully done, initialize helm using\n```sh\nhelm init --service-account tiller\n```\n\nAnd then run the command to install Postgresql in our cluster, as previously mentioned.\n\n### Setting up PostgreSQL\n\nBefore we setup PostgreSQL, lets create a namespace `databases`\n```sh\n# Why create namespace databases?\n# This command is totally optional, but this is prefered this because I place all the \n# databases created in a single namespace so that they'll be easy to access.\nkubectl create namespace databases\n\n# Before creating PostgreSQL using helm, lets understand few basics.\n\n# Default command of creation enables Persistent Volume Claim (PVC)\n# Instead of default postgres username, we are setting custom user.\n# So replace YOUR_POSTGRES_USER with desired username, in my case polls_admin \u0026\n# MY_RELEASE_NAME, which in my case is pollsdb \u0026\n# MY_DATABASE_NAME, which in my case is pollsdb\n\n# helm install --name MY_RELEASE_NAME stable/postgresql --set postgresUser=YOUR_POSTGRES_USER,postgresDatabase=MY_DATABASE_NAME --namespace databases\n\nhelm install --name pollsdb stable/postgresql --set postgresUser=polls_admin,postgresDatabase=pollsdb --namespace databases\n\n# If user wishes not to have a separate namespace then just ignore the last two words\n# i.e. --namespace databases\n```\nFor more options on customizing postgres with custom parameters, see [here](https://github.com/kubernetes/charts/tree/master/stable/postgresql)\n\n![](./images/postgres_success.png)\n**DO NOT FORGET to take a note of PGPASSWORD as seen in the NOTES section (above image) once postgres has been created**\n\n```sh\n# Saving password of PostgreSQL into environemt varialble $PGPASSWORD\nPGPASSWORD=$(kubectl get secret --namespace databases pollsdb-postgresql -o jsonpath=\"{.data.postgres-password}\" | base64 --decode; echo)\n\n# Why save the password?\n# Since we have created a separate namespace for databases, secrets from one namespaces cannot be accessed from another\n# So in order to access the postgres password in the default namespace, we must create a new secret\n# Let's first convert our password into base64 encoding.\n\necho -n $PGPASSWORD | base64\n\n# MUST DO : Copy the generated value and replace it with `YOUR_ENCODED_PASSWORD` in the `polls-password-secret.yml`. Then create the secret.\n\nkubectl create -f pollsdb-password-secret.yml\n\n# Now that the secret has been setup, lets migrate the data.\nkubectl create -f polls-migration.yml\n\n# Wait for a minute and check the status of the migration using folling commands.\nkubectl get jobs\n\n# In order to check the logs, identify the pod running the pod running migration.\nkubectl get pods --show-all\n\n# Check the logs of the pod\n# kubectl logs POD_NAME\nkubectl logs polls-migration-5tf8z\n\n# Since the jobs have passed, there is no need for them to exist.\n# We can just delete the jobs using\nkubectl delete -f polls-migration.yml\n\n```\n\n![](./images/migration_success.png)\n\n\n### Serving Static Files\n\nNow that we have the database up and running with our migrations, lets setup our static files. Rather than setting up a separate NGINX server to serve static files, it'd be much simpler, secure \u0026 faster to use Google Cloud Storage as a provider to serve static files.\n\nLet's first create a bucket in Google Cloud Storage. Visit \u003chttps://console.cloud.google.com/storage\u003e\n\n`Make sure to the check if the right project is selected.`\n\nI've created a bucket using the following settings:\n\n```\nName of Bucket          :   pollsapi-storage\nDefault Storage Class   :   Regional\nLocation                :   asia-south1 (Closest to my location)\n```\n\n![](./images/creating_bucket.png)\n\nOnce the bucket is created, navigate to the settings icon as shown below\n\n![](./images/settings_navbar.png)\n\nIn the interoperability tab, create a new key. This key is required to let our django application send static files to our bucket.\n\n![](./images/interoperability_key.png)\n\nNow that we have ACCESS_KEY, ACCESS_SECRET and BUCKET_NAME, lets create a secrets file in kubernetes, so that we can directly use these as environment variables in our django application.\n\n```sh\n# Lets first encode our secrets into base64 format\n\necho -n 'YOUR_SECRET_ACCESS_KEY_ID_HERE' | base64\n\n## Repeat the same for SECRET_ACCESS_KEY and BUCKET_NAME\n```\nOnce we have the three generated values, replace them in `cloud-storage-secrets.yml`. After replacing the values with appropriate ones, lets create our secret in kubernetes.\n\n```sh\n# Creating cloud storage secret\n\nkubectl create -f cloud-storage-secrets.yml\n```\nNow that the secrets are setup sucessfully, lets run the Job `polls-collect-static.yml` in order to collect static files.\n\n```sh\nkubectl create -f polls-collect-static.yml\n\n# Note : It will take some time to collect the static files, as they are being uploaded\n# to our bucket from the batch job which we created just now. \n# We can just check the status of static files by either checking the logs\n# or by checking the job status itself\n```\n![](./images/collect_static_success.png)\n![](./images/collect_static_job_status.png)\n\nWe have sucessfully setup static files in our application. But the major question is:\n\n`How are the static files being served?`\n\nTo answer that question, lets see a small code snippet below\n\n```py\n# First, the packages Boto \u0026 Django Storages are required. Lets install them\n# These packages help us to connect to Google Cloud Storage\npip install boto django-storages\n\n# Check the following snippet now (from settings.py file under the STATIC_FILES_SETTINGS)\nDEFAULT_FILE_STORAGE = 'storages.backends.gs.GSBotoStorage'\nSTATICFILES_STORAGE = 'storages.backends.gs.GSBotoStorage'\n\nGS_ACCESS_KEY_ID = os.environ.get('GS_ACCESS_KEY_ID', None)\nGS_SECRET_ACCESS_KEY = os.environ.get('GS_SECRET_ACCESS_KEY', None)\nGS_BUCKET_NAME = os.environ.get('GS_BUCKET_NAME', None)\n\n# Here we are configuring Google Cloud Storage as our default storage provider.\n# So whenever we run python manage.py collectstatic, all the static files\n# will be uploaded/updated in our Google Cloud Storage.\n# This also makes sure that all the static files (when required), will be served\n# from the location specified.\n\n# GS_ACCESS_KEY_ID, GS_SECRET_ACCESS_KEY and GS_BUCKET_NAME are the environment\n# variables which were created in `cloud-storage-secrets.yml` and are passed to our \n# application when the yaml file has been created.\n```\n### Setting up Django Application\n\nNow that we have the database ready with migrations, collected staticfiles, lets start our application.\n\n```sh\n# Start application\nkubectl create -f pollsapi.yml\n```\n![](./images/creating_api.png)\n\n`Note: `\n1. Usually, we run the server using `python manage.py runserver`. This is `NOT RECOMMENDED` for production purposes because of security concerns and extra memory usage. More on this can be found [here](https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/gunicorn/) Keeping that in mind, this tutorial uses `gunicorn` server to run the application.\n\n2. The service type is NodePort for our application, which means that we'll be able to access our application once we expose it using an Ingress.\n\n### Exposing our Application\n\nLets create an Ingress to expose our application.\n\n```sh\nkubectl create -f pollsapi-ingress.yml\n```\n\n`Note that creating an ingress may take atleast 5 minutes, or sometimes even more. Please be patient while an ingress is being created`\n\nTo check the status of the ingress, see below\n![](./images/ingress_success.png)\n\nAs expected, it took around `10 minutes` for the ingress to setup properly.\nNavigate to the ingress address generated i.e. http://35.241.42.232/ in order to access our application.\n\n![](./images/setup_success.png)\n\n### Documentation for Polls API\n![](./images/api_docs.png)\n\n\nOnce this is done, let's setup CI \u0026 CD for this project starting [here](https://gitlab.com/yvsssantosh/django-gitlab-ci/)\n```\nFor any queries, please create an issue.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyvsssantosh%2Fdjango-on-k8s","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyvsssantosh%2Fdjango-on-k8s","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyvsssantosh%2Fdjango-on-k8s/lists"}