Kubernetes + Hetzner Cloud + Loadbalancer + Nginx Ingress + Cert-Manager
Get 20€ credit with Hetzner using my link :)
https://hetzner.cloud/?ref=Vi4UjGTcwywL
Today i tried to setup a Kubernetes cluster on the Hetzner cloud using their loadbalancer to route my external traffic to the nginx ingress controller as well as issuing Letsencrypt certificates with cert-manager.
It was quite a ride but eventually i got a working version.
These are my steps working with K8S v.1.24.x (could be working on 1.25 but i have chosen 1.24 in order to support longhorn). Use my versions if you want a guaranteed success.
- Create a new project (or use existing)
2. Add your SSH key.
3. Create an internal network
4. Create 1 master server (e.g CX21. Do not use less than 2 CPUs) with Ubuntu 22.04 and attach it to your internal network.
5. Add as many worker nodes with Ubuntu 22.04 as you want with a bit more power (CX21 and above). You can automatically let them attach to the network.
Server Setup
Run this on all your servers
1. Update/Upgrade your Server
sudo apt update
sudo apt -y full-upgrade
[ -f /var/run/reboot-required ] && sudo reboot -f
2. Disable Swap:
swapoff -a
sed -i '/swap/d' /etc/fstab
3. Enable "br_netfilter" kernel module:
cat > /etc/modules-load.d/k8s.conf << EOF
br_netfilter
EOF
modprobe br_netfilter
4. Enable routing:
cat > /etc/sysctl.d/kubernetes.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
5. Import keys + add repos: (there could be an updated repo in the future but currently (December '22) there is only Xenial available)
sudo apt install curl apt-transport-https -y
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/k8s.gpg
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
6. Install Kubernetes packages: (im using a fixed version 1.24 here but you can use the default packages if you need a newer one).
apt update
apt install -y containerd.io kubeadm=1.24.8-00 kubectl=1.24.8-00 kubelet=1.24.8-00
apt-mark hold kubelet kubeadm kubectl
#this stops apt from upgrading to a newer version later on
7. Adjust containerd config
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sed -i -e "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml
systemctl restart containerd
systemctl restart kubelet
8. Configure "kublet" service to enable Hetzner Cloud Provider:
cat > /etc/systemd/system/kubelet.service.d/20-hcloud.conf << EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--cloud-provider=external"
EOF
systemctl daemon-reload
systemctl restart kubelet
9. Edit /etc/hosts and add your servers. E.g:
10.255.255.2 k8s-master
10.255.255.3 k8s-worker-1
10.255.255.4 k8s-worker-3
10.255.255.5 k8s-worker-2
Initialize Kubernetes Cluster
- ONLY on the master node:
kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=0.0.0.0 \
--upload-certs \
--control-plane-endpoint=k8s-master
2. Copy the kube config
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
3. Join your worker nodes with the master
kubeadm token create --print-join-command
This should output something like this:
Run this command on every worker/node
kubeadm join k8s-master:6443 --token xxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxx
All next steps are only on the master!
Network Controller
Install Flannel Network Controller:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
Note: When running "kubectl get nodes" now, the status of all nodes should be "Ready".
root@master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 103s v1.24.8
worker-1 Ready <none> 36s v1.24.8
worker-2 Ready <none> 35s v1.24.8
worker-3 Ready <none> 33s v1.24.8
Hetzner Cloud Controller
1. Create Secrets: (create an API Token within hetzner cloud for this)
kubectl -n kube-system create secret generic hcloud \
--from-literal=token=<$HETZNER_API_TOKEN> \
--from-literal=network=<$CLUSTER_NETWORK_ID>
Note: To get <$CLUSTER_NETWORK_ID> you need to check the URL on the Hetzner Cloud site and grab the ID there. E.g:
2. Install Hetzner Cloud Controller:
kubectl -n kube-system apply -f https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/download/v1.13.2/ccm-networks.yaml
CSI Driver (optional for volumes)
1. Create Secret:
kubectl -n kube-system create secret generic hcloud-csi \
--from-literal=token=<$HETZNER_API_TOKEN>
2. Install CSI Driver:
kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/v2.1.0/deploy/kubernetes/hcloud-csi.yml
Ingress Controller
Install Nginx Ingress Controller as a DaemonSet:
wget -q https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml -O /tmp/nginx.yaml
sed -i -e "s/kind: Deployment/kind: DaemonSet/g" /tmp/nginx.yaml
sed -i -e '/^kind: ConfigMap.*/i \ \ compute-full-forwarded-for: \"true\"\n \ use-forwarded-headers: \"true\"\n \ use-proxy-protocol: \"true\"' /tmp/nginx.yaml
kubectl apply -f /tmp/nginx.yaml
Load Balancer
Create and connect Ingress Controller to Hetzner Load Balancer:
kubectl -n ingress-nginx annotate services ingress-nginx-controller \
load-balancer.hetzner.cloud/name="k8s-lb" \
load-balancer.hetzner.cloud/location="nbg1" \
load-balancer.hetzner.cloud/use-private-ip="true" \
load-balancer.hetzner.cloud/uses-proxyprotocol="true" \
load-balancer.hetzner.cloud/hostname="<$CLUSTER_LB_HOSTNAME>"
Note: <$CLUSTER_LB_HOSTNAME> needs to be a valid DNS record that points to the Load Balancers’ public IP address.
The controller should have created a Loadbalancer and attached all nodes to it (might take a minute).
Cert-Manager
1. Install cert-manager using helm (took a few mins to run for me):
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.10.1 \
--set installCRDs=true
2. Create issuer:
Do this for every namespace you want to issue certificates from (replace the namespace in the end. e.g default). Change the email to your own.
kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/production-issuer.yaml -n default
3. Create an example service
kubectl apply -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/deployment.yaml
# expected output: deployment.extensions "kuard" created
kubectl apply -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/service.yaml
# expected output: service "kuard" created
4. Add ingress to example service
Replace all "example.example.com" with your own domain. MUST BE pointing to your Loadbalancers IP!
kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress-tls-final.yaml
If anything is unclear or you want to add something please register an account and comment under this post. Im happy to help!
Credits:
- https://docs.j7k6.org/hetzner-cloud-kubernetes-cluster/
- https://computingforgeeks.com/install-kubernetes-cluster-ubuntu-jammy/