curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
Next we install Minikube:
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
Now we need to install virtual box to work with our local Kubernetes:
sudo apt-get install virtualbox
Now we can start minikube. For simplicity, we want our docker commands to use the docker running in Minikube. The following will start Minikube and set environment variables to make this happen:
minikube start
eval $(minikube docker-env)
To deploy our custom Dockerfiles, we want to use the Docker running in Minikube so we want to rebuild the images so they reside in the Minikube's repository. From the django directory we can build it like before but without sudo:
docker build -t simplesite .
And then the same from the mysql directory:
docker build -t mysqlmain .
To deploy our containers to Minikube we will create yaml files that will be read by the kubectl utility. The yaml allow us to define Pods - groupings of one or more containers that we want to run together, Services - which allow us to define how to access our pods internally or externally, and Replication Sets - controllers that allow us to define how many instances of a Pod we want to run.
Before we define our Pods and Services, we want to utilize the secret functionality of Kubernetes to hold our database credentials so we don't have to store them in plain text in the yaml. We start by getting base64 encodings of our username and password:
echo -n 'testuser' | base64
dGVzdHVzZXI=
echo -n 'password' | base64
cGFzc3dvcmQ=
echo -n 'rootpass' | base64
cm9vdHBhc3M=
Then we create a new file called secret.yaml with the base64 values:
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
data:
username: dGVzdHVzZXI=
password: cGFzc3dvcmQ=
root_password: cm9vdHBhc3M=
Now we can pass this file to kubectl to store the credentials internally for use with the containers we'll create next.
kubectl create -f secret.yaml
Next we need a yaml file to create a MySQL pod and expose it as a service so that our main site can access it. We'll call it mysql-main.yaml make use of the root password secret:
apiVersion: v1
kind: Pod
metadata:
labels:
name: mysqlmain
role: db
name: mysqlmain
spec:
containers:
- name: mysqlmain
image: mysqlmain
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root_password
ports:
- containerPort: 3036
volumeMounts:
- mountPath: /mysqldata
name: data
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
name: mysqlmain
role: service
name: mysqlmain
spec:
ports:
- port: 3306
targetPort: 3306
name: port1
selector:
role: db
Now we can create and start the pod and service:
kubectl create -f mysql-main.yaml
You can look up the service information here to ensure its running:
kubectl get services
Finally we can create the yaml for our website. We'll call it simplesite.yaml. This makes use of the username and password secrets we previously created:
apiVersion: v1
kind: Pod
metadata:
labels:
name: simplesite
role: master
name: simplesite
spec:
containers:
- name: simplesite
image: simplesite
imagePullPolicy: IfNotPresent
env:
- name: DJANGO_SETTINGS_FILE
value: "simplesite.settings_kubernetes"
- name: MYSQL_HOST_DNS
value: "mysqlmain.default.svc.cluster.local"
- name: MAIN_DB_NAME
value: "kubernetes_test"
- name: MAIN_DB_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: MAIN_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
name: simplesite
role: service
name: simplesite
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
name: port1
selector:
role: master
type: NodePort
Now we can create and start the pod and service:
kubectl create -f simplesite.yaml
And to see that it is running:
kubectl get services
Now we want to get the url of the running site. You can use the following minikube to retrieve the url it is listening on:
minikube service simplesite --url
In my case its http://192.168.99.100:30620
You might get an error about the host not being authorized. In this case we will need to update the site's hosts setting to allow the hosts root. I left this in to demonstrate how to connect to a running container. You can access the running container with the following:
kubectl exec -it simplesite -- /bin/bash
And then edit the /simplesite/simplesite/settings.py file:
ALLOWED_HOSTS = ['*']
Then restart our service:
service uwsgi reload
Now we can exit the container and refresh the page in the browser. The final thing we will do is set up replicas of our website. You'll want to update the ALLOWED_HOSTS in the source settings file and rebuild the docker image for simplesite before continuing. For simplicity, we will use the Kubernetes Deployment object which is the recommended way to control containers. We will create a new yaml called simplesite-deployment.yaml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: simplesite
labels:
name: simplesite
spec:
replicas: 3
template:
metadata:
labels:
name: simplesite
spec:
containers:
- name: simplesite
image: simplesite
imagePullPolicy: IfNotPresent
env:
- name: DJANGO_SETTINGS_FILE
value: "simplesite.settings_kubernetes"
- name: MYSQL_HOST_DNS
value: "mysqlmain.default.svc.cluster.local"
- name: MAIN_DB_NAME
value: "kubernetes_test"
- name: MAIN_DB_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: MAIN_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: simplesite
labels:
name: simplesite
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
name: simplesite
This file will create 3 containers of our site and a service to load balance access to them. Before running it we should stop the existing service:
kubectl delete services simplesite
kubectl delete pods simplesite
And now we can create our new containers:
kubectl create -f simplesite-deployment.yaml
We can again retrieve the url of the service with:
minikube service simplesite --url
We now have a load balanced site with a MySQL backend. Further steps to improve this would include using a volume to persist the MySQL data in case the container is re-created later.