After we have building the k8s kubernetescluster with the Raspberry Pi's (see link), it's time to deploy some applications so we can actually can use the kubenetes cluster in our home environment. The goal of this blogpost is to get acquainted with the deployment of applications via yaml files (manifests). I am planning to write 3 blog posts about application deployments in kubernetes. In this blog we will deploy the application heimdall, which is an application dashboard which make it very easy to organise you webpages and webapplication on a dashboard.
We will start with deploying heimdall since it is a very straightforward installation. In the next blog I will describe how to deploy a Plexserver (which has some more advanced networking settings) and Nextcloud in which we will deploy a application and a database container.
I personally prefer to use yaml files to deploy application on my kubernetes cluster. Deploying applications via helm or rancher on kubernetes is very easy, but with yaml file you really can learn and understand how kubernetes operates.
I will first split up the yaml in the specific configuration, and add the end I will merge them together, so you will have one deployment yaml file for the application Heimdall.
The first thing we do when deploying a namespace. You can compare a namespace on kubernetes with a directory on your pc. It's basically organizing a group of files or objects that belong to eachother. You're not obliged to create a seperate namespace, but you will experience that putting everything in the default namespace will become unclear after deploying several applications.
The creation a namespace in a yaml file is very simple. You basically instruct the kubernates api to create a namespace called heimdall.
apiVersion: v1 kind: Namespace metadata: name: heimdall
The second thing we need to do is to create a persistant volume. As mentioned in my previous blogs no data will be stored in the pods, so I store all my data on my Synology nas via NFS. Therefore we need to create a persistant volume.
apiVersion: v1 kind: PersistentVolume metadata: name: heimdall-pv-nfs # < name of the persistent volume ("pv") in kubenetes namespace: heimdall # < namespace where place the pv spec: storageClassName: "" capacity: storage: 1Gi # < max. size we reserve for the pv accessModes: - ReadWriteMany # < Multiple pods can write to storage persistentVolumeReclaimPolicy: Retain # < The persistent volume can reclaimed mountOptions: # < Mount options specific for nfs 4.1, remove if version < 4.1 nfs server is used - hard - nfsvers=4.1 nfs: server: xxx.xxx.xxx.xxx # < IP number of your NFS server path: "/volume1/kubedata/heimdall" # < Name of your NFS share with subfolder readOnly: false
The "persistantVolumReclaimPolicy: Retain" means the persistant volume can be "reclaimed" by another persistant volume claim if the original persistant volume claim has been removed.
The third yaml we create will be the persistant volume claim. With the persistant volume claim a pod can reserve (claim) the storage it will use.
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: heimdall-pvc # < name of the persistent volume claim ("pvc'") namespace: heimdall # < namespace where place the pvc
spec: storageClassName: "" volumeName: heimdall-pv-nfs # < the pv it will "claim" to storage. Created in the previous yaml. accessModes: - ReadWriteMany # < Multiple pods can write to storage. Same value as pv volumeMode: Filesystem resources: requests: storage: 1Gi # < How much data can the pvc claim from pv
Two things to keep in mind when configuring the persistant volume claim.
- The accesmode must always be same as the Persistant Volume (example ReadWriteOnce)
- The storagesize of the pvc (example 1Gi) must always be lower or the same at the persitanct volume.
apiVersion: apps/v1 kind: Deployment metadata: name: heimdall # < name of the deployment namespace: heimdall # < namespace where place the deployment and pods labels: app: heimdall # < label for tagging and reference spec: replicas: 1 # < number of pods to deploy selector: matchLabels: app: heimdall strategy: rollingUpdate: maxSurge: 0 # < The number of pods that can be created above the desired amount of pods during an update maxUnavailable: 1 # < The number of pods that can be unavailable during the update process type: RollingUpdate # < New pods are added gradually, and old pods are terminated gradually template: metadata: labels: app: heimdall spec: volumes: - name: nfs-heimdall # < linkname of the volume for the pvc persistentVolumeClaim: claimName: heimdall-pvc # < pvc name we created in the previous yaml - name: heimdall-ssl secret: secretName: heimdall-mydomain-tls # < the name ssl certificate, will be created in the ingress yaml containers: - image: ghcr.io/linuxserver/heimdall # < the name of the docker image we will use name: heimdall # < name of container imagePullPolicy: Always # < always use the latest image when creating container/pod env: # < the environment variables required (see container documentation) - name: PGID value: "100" # < group "user" - name: PUID value: "1041" # < user "docker" - name: TZ value: Europe/Amsterdam ports: # < the ports required (see container documentation) - containerPort: 80 name: http-80 protocol: TCP - containerPort: 443 name: https-443 protocol: TCP volumeMounts: # < the volume mount in the container. Look at the relation volumelabel->pvc->pv - mountPath: /config # < mount location in the container name: nfs-heimdall # < volumelabel configured earlier in the yaml file subPath: config # < subfolder in the nfs share to be mounted
In the fourth yaml we wil configure the service. A service is required to expose a pod to the cluster. As you see the selector app value connect the service to heimdall app we created in the deployment yaml. This yaml file basically instructs kubernetes to expose port 80 and 443 to the rest of the kubernetes cluster, so it can communicate with other pods and services within the kubernetes cluster.
apiVersion: v1 kind: Service metadata: name: heimdall-service # < name of the service namespace: heimdall # < namespace where place the deployment and pods spec: selector: app: heimdall # < reference to the deployment (connects with this deployment) ports: - name: http-80 protocol: TCP port: 80 - name: https-443 protocol: TCP port: 443
The fifth and last yaml file we create an entry in the ingress controller. A service is required to expose the application within the cluster, but to expose an application (pods) to the outside world we need to create an ingress, loadbalancer or a nodeport entry.
From heimdall we create an ingress entry so we can also have the cert-manager create valid Let's Encrypt ssl certificate. Please keep in mind configure port 80 via NAT on your router to the IP of the ingress controller, and create a public DNS entry for your application host name pointing to public address of your router. (for example heimdall.mydomain.com). Let's Encrypt always checks if it can connect to the hostname on port 80 before releasing the ssl certificate.
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: heimdall-ingress # < name of ingress entry namespace: heimdall # < namespace where place the deployment and pods annotations: kubernetes.io/ingress.class: "nginx" # < use the nginx ingress controller nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # < communicate with the backend (service/pod) cert-manager.io/cluster-issuer: "letsencrypt-prod" # < use letsencrypt-prod application in kubernetes to generate ssl certificate spec: rules: - host: heimdall.mydomain.com # < hostname to access the heimdall http: paths: - path: / backend: serviceName: heimdall-service # < connect the ingress entry to service created earlier servicePort: 443 tls: # < placing a host in the TLS config will indicate a cert should be created - hosts: - heimdall.mydomain.com # < hostname to access the heimdall secretName: heimdall-mydomain-tls # < cert-manager will store the created certificate in this secret.
So when these five yaml files have been created, you can merge them into one. This very easy. Just cut and past them underneath in a text editor seperated by 3 hyphens "---". The end result should look someting like this
If you have adjusted and saved the file. You can apply it to your kubenetes cluster via the command
kubectl apply -f deploy-heimdall-ssl-example.yml
You can check if every is installed ok via the command:
kubectl get all -n heimdallYou should see something like this
kubectl get ingress -n heimdall