Learning Kubernetes

Some time ago, I decided to build a nice way to deploy my projects in my homelab, which consists of 3 proxmox hosts and a NAS. I used to have a couple of VMs with docker installed and simply deployed it via docker compose, but that has one large problem: I often pull machines from my “rack” to tinker with them, and that means services are down.

Naturally, this calls for some kind of a system with replication, and having spent time on r/ProgrammerHumor, I immediately thought of Kubernetes. After reading and trying my way through the tutorial at kubernetes.io, I created 3 Ubuntu server VMs, one on each host. With 4 GB of RAM and 2 CPUs, they aren’t too heavy on my homelab. I then installed K3s on them, connecting them into a cluster.

The application we are deploying today is a stateless Jekyll site in a docker image.

First, let’s create a namespace, so that our configs and resources are separated: kubectl create namespace blog.

Since this is a docker image, we will need to get it from somewhere, and since that isn’t docker hub, we need to add credentials for our registry. This can be done with: kubectl create secret docker-registry regcred2 --docker-server=domain --docker-username=username --docker-password=password -n blog.

The -n blog, which you will undoubtedly see again, selects the namespace we created earlier.

Next, let’s create a deployment configuration file. This is a yaml file, that can be created in the repository, or anywhere else, since you can use it from your laptop with kubectl.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  namespace: blog
spec:
  replicas: 3 
  selector:
    matchLabels: { app: blog }
  template:
    metadata:
      labels: { app: blog }
    spec:
      imagePullSecrets:
        - name: regcred2
      containers:
        - name: blog
          image: registry-domain/lovrog05/blog:latest 
          ports: [{ containerPort: 80 }]
          resources:
            requests: { memory: 16Mi, cpu: 10m }

This can be applied with kubectl apply -f blog.yaml.

Notice the line replicas: 3, which tells us we want 3 instances of this application. Since we have 3 host VMs, k3s will try to automatically put one on each VM. However, how do we split the load between the pods?

For that, we need to create a Load balancer service, which can come with its own yaml config file:

apiVersion: v1
kind: Service
metadata:
  name: blog
  namespace: blog
  annotations:
    metallb.universe.tf/loadBalancerIPs: 10.169.250.4
spec:
  type: LoadBalancer
  selector: { app: blog }
  ports:
    - port: 80
      targetPort: 80

metallb.universe.tf/loadBalancerIPs: 10.169.250.4 comes into play since I also run MetalLB in place of ServiceLB, K3s default, a type of load balancer for bare-metal clusters, which sets LAN static IPs within a set range to each deployment’s LoadBalancer, allowing me to point my reverse proxy at each deployed app.

This can once again be applied with kubectl -n blog apply -f service.yaml.

So, now I can view my own blog on my local network even when I am working on one of my machines, which is pretty neat, I guess.