|
| 1 | +# Minikube |
| 2 | + |
| 3 | +Minikube is a tool for running a single-node kubernetes cluster inside of a virtual machine. It is a popular tool for developing Kubernetes applications locally. |
| 4 | + |
| 5 | +This topic will cover using `minikube` to set up the project Kubernetes locally. |
| 6 | + |
| 7 | +I'll be following [this guide](https://medium.com/@markgituma/kubernetes-local-to-production-with-django-1-introduction-d73adc9ce4b4) to get started. |
| 8 | + |
| 9 | +## Getting started |
| 10 | + |
| 11 | +### Start minikube |
| 12 | + |
| 13 | +To get started, bring up `minikube` with |
| 14 | + |
| 15 | +```bash |
| 16 | +minikube start |
| 17 | +``` |
| 18 | + |
| 19 | +Optionally, run `minikube delete`, and then `minikube start` to start with a clean cluster. |
| 20 | + |
| 21 | +I'll be using the following alias to use `kubectl`: |
| 22 | + |
| 23 | +```bash |
| 24 | +alias k='kubectl' |
| 25 | +``` |
| 26 | + |
| 27 | +## Build the Django server Deployment |
| 28 | + |
| 29 | +We need to build our `backend` image. In order for minikube to be able to use the image, we can set our docker client to point to the minikube docker host. To do this, run the following command: |
| 30 | + |
| 31 | +``` |
| 32 | +eval $(minikube docker-env) |
| 33 | +``` |
| 34 | + |
| 35 | +`$(minikube docker-env)` results in the following output: |
| 36 | + |
| 37 | +```bash |
| 38 | +export DOCKER_TLS_VERIFY="1" |
| 39 | +export DOCKER_HOST="tcp://192.168.99.100:2376" |
| 40 | +export DOCKER_CERT_PATH="/home/brian/.minikube/certs" |
| 41 | +# Run this command to configure your shell: |
| 42 | +# eval $(minikube docker-env) |
| 43 | +``` |
| 44 | + |
| 45 | +Notice that the `DOCKER_HOST` is pointing to the minikube VM on docker's default port `2376`. |
| 46 | + |
| 47 | +With these environment variables set, let's build the Django container image with the following command: |
| 48 | + |
| 49 | +```bash |
| 50 | +docker-compose build backend |
| 51 | +``` |
| 52 | + |
| 53 | +**`deployment.yml`** |
| 54 | + |
| 55 | +```yml |
| 56 | +apiVersion: apps/v1 |
| 57 | +kind: Deployment |
| 58 | +metadata: |
| 59 | + name: django-backend |
| 60 | + labels: |
| 61 | + app: django-backend |
| 62 | +spec: |
| 63 | + replicas: 1 |
| 64 | + selector: |
| 65 | + matchLabels: |
| 66 | + app: django-backend |
| 67 | + template: |
| 68 | + metadata: |
| 69 | + labels: |
| 70 | + app: django-backend |
| 71 | + spec: |
| 72 | + containers: |
| 73 | + - name: django-backend-container |
| 74 | + image: localhost:5000/backend |
| 75 | + command: ["./manage.py", "runserver"] |
| 76 | + ports: |
| 77 | + - containerPort: 8000 |
| 78 | +``` |
| 79 | +
|
| 80 | +Let's send this file to Kubernete API server with the following command: |
| 81 | +
|
| 82 | +``` |
| 83 | +kubectl apply -f kubernetes/django/deployment.yml |
| 84 | +``` |
| 85 | + |
| 86 | +Your pod for the deployment should be starting. Inspect the pods with `k get pods`. If there is an error with container startup, you might see something like this: |
| 87 | + |
| 88 | +``` |
| 89 | +k get pods |
| 90 | +NAME READY STATUS RESTARTS AGE |
| 91 | +django-backend-dd798db99-hkv2p 0/1 Error 0 3s |
| 92 | +``` |
| 93 | + |
| 94 | +If this is the case, inspect the logs of the container with the following command: |
| 95 | + |
| 96 | +I have intentionally cause the container to fail by not providing a `SECRET_KEY` environment variable (this is something that Django needs in order to start). |
| 97 | + |
| 98 | +Let's inspect the container logs to confirm this: |
| 99 | + |
| 100 | +```bash |
| 101 | +k logs django-backend-dd798db99-hkv2p |
| 102 | +Traceback (most recent call last): |
| 103 | + File "./manage.py", line 16, in <module> |
| 104 | + execute_from_command_line(sys.argv) |
| 105 | + File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line |
| 106 | + utility.execute() |
| 107 | + File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute |
| 108 | + self.fetch_command(subcommand).run_from_argv(self.argv) |
| 109 | + File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 323, in run_from_argv |
| 110 | + self.execute(*args, **cmd_options) |
| 111 | + File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 60, in execute |
| 112 | + super().execute(*args, **options) |
| 113 | + File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 364, in execute |
| 114 | + output = self.handle(*args, **options) |
| 115 | + File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 67, in handle |
| 116 | + if not settings.DEBUG and not settings.ALLOWED_HOSTS: |
| 117 | + File "/usr/local/lib/python3.7/site-packages/django/conf/__init__.py", line 79, in __getattr__ |
| 118 | + self._setup(name) |
| 119 | + File "/usr/local/lib/python3.7/site-packages/django/conf/__init__.py", line 66, in _setup |
| 120 | + self._wrapped = Settings(settings_module) |
| 121 | + File "/usr/local/lib/python3.7/site-packages/django/conf/__init__.py", line 176, in __init__ |
| 122 | + raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.") |
| 123 | +django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty. |
| 124 | +``` |
| 125 | +
|
| 126 | +We could either provide a fallback value in the Django settings (which would require rebuilding the image), or we could add an environment variable to the container definition in the Pod `spec` in `deployment.yml`: |
| 127 | +
|
| 128 | +```yml |
| 129 | + spec: |
| 130 | + containers: |
| 131 | + - name: backend |
| 132 | + imagePullPolicy: IfNotPresent |
| 133 | + image: backend:latest |
| 134 | + command: ["./manage.py", "runserver"] |
| 135 | + ports: |
| 136 | + - containerPort: 8000 |
| 137 | + env: |
| 138 | + - name: SECRET_KEY |
| 139 | + value: "my-secret-key" |
| 140 | +``` |
| 141 | +
|
| 142 | +This should work, but we will still see errors in logs because we Django will attempt to establish a connection with the Postgres database which we will be setting up next. |
| 143 | +
|
| 144 | +We can hit our public facing `hello-world` endpoint which should serve as a nice health check for the Django container. |
| 145 | +
|
| 146 | +Let's test this endpoint with `curl`. We haven't set up a Kubernetes `Service` yet, so we will have to curl the Django application from within the cluster. We can do this with: |
| 147 | +
|
| 148 | +``` |
| 149 | +k exec django-backend-757b5944d8-htssm -- curl -s http://172.17.0.5/api/hello-world/ |
| 150 | +``` |
| 151 | +
|
| 152 | +This gives us: |
| 153 | +
|
| 154 | +``` |
| 155 | +command terminated with exit code 7 |
| 156 | +``` |
| 157 | +
|
| 158 | +Our container has started, but the database connection has prevented the Django process from starting. In our Pod logs, we can see that no request have been received and the Django application is not listening on `0.0.0.0:8000`. |
| 159 | +
|
| 160 | +Let's come back to this once we have set up our Postgres database. |
| 161 | +
|
| 162 | +## Postgres |
| 163 | +
|
| 164 | +First, we create a `PersistentVolume` resource: |
| 165 | +
|
| 166 | +**`kubernetes/postgres/volume.yml`** |
| 167 | +
|
| 168 | +```yml |
| 169 | +kind: PersistentVolume |
| 170 | +apiVersion: v1 |
| 171 | +metadata: |
| 172 | + name: postgres-pv |
| 173 | + labels: |
| 174 | + type: local |
| 175 | +spec: |
| 176 | + storageClassName: manual |
| 177 | + capacity: |
| 178 | + storage: 2Gi |
| 179 | + accessModes: |
| 180 | + - ReadWriteOnce |
| 181 | + hostPath: |
| 182 | + path: /data/postgres-pv |
| 183 | +``` |
| 184 | +
|
| 185 | +::: warning storageClassName |
| 186 | +Clicking on the `storageClassName` gave a 404 error |
| 187 | +::: |
0 commit comments