It’s finally time to make a few notes on Kubernetes. We have already looked at Docker and Docker Compose, so we can use that knowledge to explore what Kubernetes is. If you want a great YouTube resource to get started, you should check out Docker and Kubernetes Tutorial. I will be pulling examples from that tutorial. The first two-thirds is all about Docker, and the last third is Kubernetes. It’s an excellent introduction tutorial. You should subscribe to TechWorld with Nana she produces fantastic content.
from kubernetes.io
Kubernetes is a portable, extensible, open-source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation. It has a large, rapidly growing ecosystem. Kubernetes services, support, and tools are widely available.
Rember how we used Docker Compose to launch multiple Docker instances. For example, we created a Docker Compose file that launched a single web server, connected it to an API server, and connected that to a MongoDB instance? Kubernetes performs a similar function but goes further. Kubernetes also manages scaling and failovers. It does this in a way that abstracts away vendor-specific implementations.
from kubernetes.io
Containers are a good way to bundle and run your applications. In a production environment, you need to manage the containers that run the applications and ensure that there is no downtime. For example, if a container goes down, another container needs to start. Wouldn’t it be easier if this behavior was handled by a system? That’s how Kubernetes comes to the rescue! Kubernetes provides you with a framework to run distributed systems resiliently. It takes care of scaling and failover for your application, provides deployment patterns, and more. For example, Kubernetes can easily manage a canary deployment for your system. Kubernetes provides you with:
First, we need a Kubernetes Cluster. For local development, we will use minikube. Here is how to install it on the macOS. It should also install kubectl
as a dependency.
brew update
brew install minikube
minikube
minikube status
minikube start
minikube dashboard
kubectl
In this example, we will launch a MongoDB and a Mongo Express UI.
Browser -> Mongo-Express -> MongoDB
To start MongoDB we need to create a YAML file that tells Kubernetes how to launch a docker instance of MongoDB.
mongo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
kind
is set to Deployment
because we are deploying a docker instance.
In the metadata
section, we are naming the deployment and tagging it to match it later.
In the spec
section, we tell Kubernetes how we want to deploy the docker instance. In this case, we only want a single instance to run, so replicas
is 1. The containers
section describes the Docker image and any variables that need to be set (see DockerHub for this information). In our case, we need to set the root username and password. We do this by referencing a Secrets file.
mongo-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mongodb-secret
type: Opaque
data:
mongo-root-username: dXNlcm5hbWU=
mongo-root-password: cGFzc3dvcmQ=
In this case, kind
is set to Secret.
The data
section is a list of key-value pairs where the value is base64 encoded. Note that Base64 is not an encryption tool. It’s no more secure than plain text. You can base64 encode a string like this.
echo -n 'username' | base64
echo -n 'password' | base64
minikube start
kubectl apply -f mongo-secret.yaml
kubectl get secret
kubectl apply -f mongo.yaml
kubectl get all
kubectl get pod
kubectl describe pod <your-pod-id>
kubectl get pod --watch
kubectl get pod -o wide
kubectl get all | grep mongodb
To let other Pods talk to our MongoDB Pod, we need to add a Service. We want to use an Internal Service in this case because we only want other pods to connect. We don’t want anyone on the internet poking at our MongoDB. We need another YAML file to do this. Since the Service and the Deployment are tightly coupled, it makes sense to append the Service definition to the Deployment file. It now looks like this.
mongo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
kubectl apply -f mongo.yaml
kubectl get service
We need to do pretty much the same thing.
mongo-express.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo-express
labels:
app: mongo-express
spec:
replicas: 1
selector:
matchLabels:
app: mongo-express
template:
metadata:
labels:
app: mongo-express
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_ADMINUSERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: ME_CONFIG_MONGODB_ADMINPASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
- name: ME_CONFIG_MONGODB_SERVER
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
---
apiVersion: v1
kind: Service
metadata:
name: mongo-express-service
spec:
selector:
app: mongo-express
type: LoadBalancer
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30000
Notice that we added a ConfigMap for the MongoDB URL.
mongo-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-configmap
data:
database_url: mongodb-service
The Service also changed. This time the service is of type LoadBalancer.
making it an external service so we can access it from the browser.
kubectl apply -f mongo-configmap.yaml
kubectl get configMap
kubectl apply -f mongo-express.yaml
kubectl logs <yout-pod-id>
minikube service mongo-express-service
Currently, if MongoDB stops, all our data is lost. We need a way to persist some data between Pod restarts. Persistent Volumes will do this for us. Let’s first see the problem.
minikube service mongo-express-service
In the Browser, create a new database called ‘Sample.’ Now redeploy the POD.
kubectl rollout restart deployment mongodb-deployment
Refreshing the Browser will show that our new ‘Sample’ database is no longer there.
Note that this is for demonstration purposes. A production configuration will use resources in your specific cloud provider.
Let’s add PersistentVolume and PersistentVolumeClaim. Then we need to link our MongoDB deployment like this.
mongo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
hostPath:
path: /data/mongoData
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-claim
spec:
storageClassName: ''
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
volumes:
- name: data-storage
persistentVolumeClaim:
claimName: data-claim
containers:
- name: mongodb
image: mongo
volumeMounts:
- name: data-storage
mountPath: /data/db
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
minikube stop
minikube delete
minikube start
kubectl apply -f mongo-secret.yaml
kubectl apply -f mongo-configmap.yaml
kubectl apply -f mongo.yaml
kubectl apply -f mongo-express.yaml
kubectl get pod
Wait for the pods to become avaiable…
minikube service mongo-express-service
In the Browser, create a new database called ‘Sample.’ Now redeploy the POD.
kubectl rollout restart deployment mongodb-deployment
Refreshing the Browser will show that our new ‘Sample’ database is still there.
Note: this code is in the branch called Using-Ingtress
The External Service is sufficient for development, but the better solution is to use an Ingress Service.
We first need to install Ingress Controller in your cluster
minikube stop
minikube delete
brew install hyperkit
minikube start --driver=hyperkit
minikube addons enable ingress
kubectl get pod -n kube-system
Now you need to change the mongo-express-service to be a, internal service.
mongo-express.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo-express
labels:
app: mongo-express
spec:
replicas: 1
selector:
matchLabels:
app: mongo-express
template:
metadata:
labels:
app: mongo-express
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_ADMINUSERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: ME_CONFIG_MONGODB_ADMINPASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
- name: ME_CONFIG_MONGODB_SERVER
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
---
apiVersion: v1
kind: Service
metadata:
name: mongo-express-service
spec:
selector:
app: mongo-express
ports:
- protocol: TCP
port: 8081
targetPort: 8081
Now create Ingress Service
mongo-ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: mongo-ingress
spec:
rules:
- host: mongo-express.com
http:
paths:
- backend:
serviceName: mongo-express-service
servicePort: 8081
kubectl apply -f mongo-secret.yaml
kubectl apply -f mongo-configmap.yaml
kubectl apply -f mongo.yaml
kubectl apply -f mongo-express.yaml
kubectl apply -f mongo-ingress.yaml
kubectl get ingress
The command kubectl get ingress
will eventually assign an address along with the port. You need to use this information to add an entry in you hosts file.
/etc/hosts add these lines with your Address
# Kubernetes post
<YOUR-IP_ADDRESS> mongo-express.com
Finally, open a browser to http://mongo-express.com
This is waht I did to test out a Helm Chart deployment of Prometheus.
minikube delete
minikube start
brew install helm
helm
helm repo add stable https://charts.helm.sh/stable
helm repo update
helm install prometheus stable/prometheus-operator
kubectl get all
Wait for everything to start
kubectl port-forward deployment prometheus-grafana 3000
open browser http://localhost:3000
kubectl port-forward prometheus-prometheus-prometheus-oper-prometheus-0 9090
open browser http://localhost:9090