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.