Introduction
This tutorial explains the process of setting up a Kubernetes cluster across multiple regions on Hetzner Cloud using a tool called Claudie. Claudie is a platform for managing multi-cloud and hybrid-cloud Kubernetes clusters with support for nodepools across different cloud providers and on-premise data centers, including Hetzner Cloud.
This type of setup is resilient to regional cloud disruptions and also helps address resource shortages within a single or even multiple regions.
Prerequisites
- Hetzner Cloud account
- kubectl binary
- kind
- Container runtime, e.g. Docker
Step 1 - Install Claudie
Claudie needs to be installed on an existing Kubernetes cluster, referred to as the Management Cluster, which is used to manage the clusters it provisions. For this tutorial, an ephemeral cluster like kind can be used for simplicity. This step assumes that you have kind installed locally.
For testing, you can use ephemeral clusters like Minikube or kind. However, for production environments, it is recommended to use a more resilient solution since Claudie maintains the state of the infrastructure it creates.
-
Create a kind cluster and export the kubeconfig
holu@<your_host>:~# kind create cluster --name claudie-mgmt Creating cluster "claudie-mgmt" ... ✓ Ensuring node image (kindest/node:v1.35.0) :frame_with_picture: ✓ Preparing nodes :package: ✓ Writing configuration :scroll: ✓ Starting control-plane :joystick: ✓ Installing CNI :electric_plug: ✓ Installing StorageClass :floppy_disk: Set kubectl context to "kind-claudie-mgmt" You can now use your cluster with: kubectl cluster-info --context kind-claudie-mgmtholu@<your_host>:~# kind get kubeconfig -n claudie-mgmt > ~/.kube/claudie holu@<your_host>:~# export KUBECONFIG=~/.kube/claudie holu@<your_host>:~# kubectl get nodes NAME STATUS ROLES AGE VERSION claudie-mgmt-control-plane Ready control-plane 3m8s v1.35.0 -
Install cert-manager as it is a requirement for the Claudie Management cluster.
holu@<your_host>:~# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.0/cert-manager.yamlWait until cert-manager is ready before you continue with the next step:
kubectl get pods -n cert-manager -
Deploy Claudie
holu@<your_host>:~# kubectl apply -f https://github.com/berops/claudie/releases/download/v0.10.0/claudie.yaml -
Wait for all Pods to get into Ready state
holu@<your_host>:~# kubectl get pods -n claudie NAME READY STATUS RESTARTS AGE ansibler-594c67f799-p6rtp 1/1 Running 0 60s claudie-operator-56f4b6767f-smr2x 1/1 Running 0 60s kube-eleven-547645865f-4tls2 1/1 Running 0 60s kuber-7764ffb5df-d826z 1/1 Running 0 60s manager-59fc97fd48-n8cvj 1/1 Running 0 60s minio-0 1/1 Running 0 60s minio-1 1/1 Running 0 60s minio-2 1/1 Running 0 60s minio-3 1/1 Running 0 60s mongodb-6d59c69c99-gjrv2 1/1 Running 0 60s nack-5bbb756857-rhxx2 1/1 Running 0 60s nats-0 2/2 Running 0 60s nats-1 2/2 Running 0 60s nats-2 2/2 Running 0 60s terraformer-6c88c87f8c-b9gtn 1/1 Running 0 60s
Step 2 - Create Hetzner API Key
-
Login to your Hetzner Console to generate a new API token with Read & Write permissions. For more information you can follow this guide on creating an API Token.
-
Create a Kubernetes Secret that will contain the Hetzner API Token.
holu@<your_host>:~# kubectl create secret generic hetzner-secret --from-literal=credentials='YOUR_API_TOKEN' -n claudie
Step 3 - Create a Claudie manifest file
Now, let's create a Claudie manifest file that describes the Kubernetes cluster you want to create across multiple Hetzner regions. You can use the example manifest below as a starting point.
-
Create the file:
holu@<your_host>:~# nano inputmanifest.yml -
Add your content:
apiVersion: claudie.io/v1beta1 kind: InputManifest metadata: name: multi-cloud-k8s spec: providers: - name: hetzner-secret providerType: hetzner secretRef: name: hetzner-secret namespace: claudie nodePools: dynamic: - name: ctrl-hel providerSpec: name: hetzner-secret region: hel1 zone: hel1-dc14 count: 3 serverType: cx23 image: ubuntu-24.04 - name: cmpt-hel providerSpec: name: hetzner-secret region: hel1 zone: hel1-dc14 count: 2 serverType: cx23 image: ubuntu-24.04 storageDiskSize: 50 - name: cmpt-nbg providerSpec: name: hetzner-secret region: nbg1 zone: nbg1-dc3 autoscaler: min: 1 max: 2 serverType: cx23 image: ubuntu-24.04 storageDiskSize: 50 kubernetes: clusters: - name: hetzner-cluster version: v1.34.0 network: 192.168.2.0/24 pools: control: - ctrl-hel compute: - cmpt-hel - cmpt-nbg
This InputManifest will provision:
- 3 Kubernetes control plane nodes in the Helsinki region
- 2 Kubernetes worker nodes in the Helsinki region
- 1 to 2 auto-scaled Kubernetes worker nodes in the Nuremberg region, depending on workload demand
Let's break down some of the key fields:
spec.providers» Contains configurations for supported cloud providers. It is referencing access credentials (previously created in a Secret object) and terraform template files for Hetzner (see more)spec.nodePools.dynamic» Defines dynamic nodepools. Those are cloud provider VMs that Claudie is expected to create.spec.kubernetes.clusters» Describes the Kubernetes cluster that will be created.
Communication between regional nodes runs over the overlay network 192.168.2.0/24, which we defined in the InputManifest. All inter-node traffic within this network is encrypted using WireGuard, eliminating the need for a virtual network resource.
This infrastructure can be modified at any time. You can add a new region, scale nodes up or down within existing regions, or even attach physical machines to the cluster. For more in-depth understanding of the Claudie InputManifest definition, visit the documentation site.
Step 4 - Deploy the cluster
Apply the InputManifest to the Management Cluster:
holu@<your_host>:~# kubectl apply -f inputmanifest.yml -n claudie
inputmanifest.claudie.io/multi-cloud-k8s createdVerify that the infrastructure is being deployed:
holu@<your_host>:~# kubectl get inputmanifest -n claudie
NAME STATUS
multi-cloud-k8s IN_PROGRESSWait until you see WATCHING_FOR_CHANGES in the STATUS column. You can follow the deployment progress by checking the operator logs:
holu@<your_host>:~# kubectl logs -f -l app.kubernetes.io/name=claudie-operator -n claudieOnce deployment is complete:
holu@<your_host>:~# kubectl get inputmanifest -n claudie
NAME STATUS
multi-cloud-k8s WATCHING_FOR_CHANGESStep 5 - Access the Cluster
The kubeconfig will be saved to a Secret object in the claudie namespace.
Export the kubeconfig for the deployed cluster:
holu@<your_host>:~# kubectl get secrets -n claudie -l claudie.io/cluster=hetzner-cluster,claudie.io/output=kubeconfig -ojsonpath='{.items[0].data.kubeconfig}' | base64 -d > hetzner-cluster.yamlVerify the deployed cluster:
holu@<your_host>:~# export KUBECONFIG=hetzner-cluster.yaml
holu@<your_host>:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
cmpt-hel-vjh754v-01 Ready <none> 9m v1.34.0
cmpt-hel-vjh754v-02 Ready <none> 9m v1.34.0
cmpt-nbg-46oaf10-01 Ready <none> 9m v1.34.0
ctrl-hel-lkd2o3o-01 Ready control-plane 9m v1.34.0
ctrl-hel-lkd2o3o-02 Ready control-plane 9m v1.34.0
ctrl-hel-lkd2o3o-03 Ready control-plane 9m v1.34.0As can be seen from the output above, we have three control plane nodes and two worker nodes in the Helsinki region, and one worker node in the Nuremberg region which can be horizontally autoscaled up to two nodes.
Conclusion
In this tutorial we walked through the process of deploying a Kubernetes cluster across two Hetzner regions using Claudie to manage the infrastructure. We began by setting up Claudie in a local kind cluster, created a Hetzner API key for authentication, and defined a Claudie manifest. After applying the manifest, we successfully created a Kubernetes cluster on Hetzner Cloud.
Using Claudie gives us flexibility in determining how many nodes to deploy and where to place them. It also improves resilience against cloud outages, helps mitigate resource shortages within a region, and enhances overall high availability.