Affordable Lab Kubernetes Cluster with Hetzner Cloud

So I was investigating how to setup a Kubernetes cluster that is both highly available and cheap. After some investigation I discovered that it is actually possible using the cheapest VMs you can find at Hetzner Cloud and their LB offering.

  • CX11 x 3 = 8.88 EUR
  • LB11 = 5.83 EUR

This is more expensive than a regular VM, but still within a reasonable price all things considered. I am not getting this because I need to, but because I work with k8s daily and I like to have a private cluster to play around with (without the typical RPI cluster). I did my first experiment using a floating ip instead of a dedicated LB, but it required (what I felt) a hacky solutions using keepalived or hcloud-fip-controller with metallb, among other options. I tried several solutions, but the failover was never smooth enough and the traffic is not load balanced attaching the external IP to a single host. There is also some complications to setup the ingress to actually read the client IPs correctly. So I decided to pay the extra few bucks to get a true HA solution and avoid the hassle.

Since the VMs are pretty low on resources I decided to install k3s which is a breeze to install even in HA mode with etcd running in a cluster on all three nodes.

On the first node:

curl -sfL https://get.k3s.io | sh -s - \
--disable traefik \
--cluster-init \
--token 123456 \
--node-ip 10.0.0.7 \
--tls-san 10.0.0.7 \
--advertise-address 167.x.x.x \
--node-external-ip 116.x.x.x \

and the following nodes:

curl -sfL https://get.k3s.io | sh -s - \
--disable traefik \
--server https://10.0.0.7:6443 \
--token 123456 \
--node-ip 10.0.0.8 \
--tls-san 10.0.0.8 \
--advertise-address 167.x.x.x \ 
--node-external-ip 116..x.x.x \

That is all you need to get the cluster up and running. Just remember to configure the LB service (6443) so the nodes can communicate with the loadbalanced API endpoint.

I also setup the servers with unattended-upgrades with different reboot times. There should now be little reason to ever ssh to the nodes except upgrading k3s.

But before we can deploy our first webapps we need a few extra components.
For L7 http ingress traffic I will use the official Nginx Ingress Controller. It's what I use at work and already familiar with its configuration. Also the reason I disable traefik in k3s.

nginx ingress traffic

kubectl create namespace ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install -n ingress-nginx ingress-nginx ingress-nginx/ingress-nginx

Remember to enable the proxy protocol in your LB and nginx so you can log the real client ips correctly:

kubectl -n ingress-nginx edit configmap ingress-nginx-controller

and add:

compute-full-forwarded-for: "true"
use-forwarded-headers: "true"
use-proxy-protocol: "true"

Since we want encrypted/https traffic we will also install cert-manager to deal with renewing our letsencrypt certificates. It also support dns01 via Cloudflare, my personal DNS provider.

cert-manager

kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager --namespace cert-manager     --set installCRDs=true

And a cluster is never really HA unless you solve the persistent storage problem. Rancher has developed a great distributed block storage that works flawlessly to solve this problem easily.

longhorn

# Remember to install open-iscsi on your distro first. 
kubectl create namespace longhorn-system
helm repo add longhorn https://charts.longhorn.io
helm install longhorn longhorn/longhorn --namespace longhorn-system

And this is all the basics you need to deploy your stateful webapp in a HA environment.

I will of course recommend that you add some monitoring with tools like prometheus and grafana.