Managed Kubernetes – Besonderheiten

k0s #

 

Was ist k0s?

k0s ist eine leichtgewichtige, CNCF-zertifizierte Kubernetes-Distribution. k0s ist vollständig konform zur Kubernetes-API – alles, was Sie aus Standard-Kubernetes kennen, funktioniert auch hier.

 

Abweichungen zu kubeadm-basiertem Kubernetes

Der wichtigste Unterschied: k0s verwendet einen abweichenden kubelet-Pfad. Statt des üblichen /var/lib/kubelet nutzt k0s:

/var/lib/k0s/kubelet

Das bedeutet: Wenn Sie DaemonSets, HostPath-Volumes oder andere Ressourcen einsetzen, die auf den kubelet-Pfad verweisen, müssen Sie diesen Pfad berücksichtigen.

Beispiel für ein HostPath-Volume:

volumes:
- name: kubelet-data hostPath: path: /var/lib/k0s/kubelet type: Directory

Cilium (CNI) #

 

Warum Cilium?

Cilium ist ein eBPF-basiertes Container Network Interface und ersetzt herkömmliche iptables-basierte Netzwerkstacks. Vorteile:

  • Performant: eBPF arbeitet direkt im Linux-Kernel – schneller als iptables-basierte Lösungen
  • Kein kube-proxy: Cilium übernimmt die Service-Proxy-Funktion nativ über eBPF

 

Was bedeutet „kein kube-proxy“?

In einigen Kubernetes-Clustern übernimmt kube-proxy die Weiterleitung von Service-Traffic. In Ihrem Cluster erledigt Cilium diese Aufgabe über eBPF – effizienter und mit geringerer Latenz.

Für Sie ändert sich nichts bei der Nutzung von Services – alle Kubernetes-Service-Typen (ClusterIP, NodePort, LoadBalancer) funktionieren wie gewohnt.


Cluster-Autoscaler #

 

Voraussetzungen für effektives Autoscaling

Der Cluster-Autoscaler skaliert auf Basis von nicht-schedulbaren Pods – er reagiert also auf Ressourcenengpässe, nicht auf Auslastung. Damit Autoscaling zuverlässig funktioniert, sollten folgende Voraussetzungen erfüllt sein:

  • Horizontal Pod Autoscaler (HPA) nutzen: HPAs skalieren zuerst die Anzahl der Pods innerhalb der bestehenden Nodes. Erst wenn keine weiteren Pods geplant werden können, greift der Cluster-Autoscaler und fügt neue Nodes hinzu. HPAs sollten daher die erste Skalierungsebene sein.
  • Ressourcen korrekt setzen: Jedes Pod benötigt definierte requests und limits für CPU und Memory. Ohne requests kann der Autoscaler nicht einschätzen, ob eine Node ausgelastet ist oder neue Nodes benötigt werden.
  • TopologySpreadConstraints verwenden: Um Pods gleichmäßig auf Nodes zu verteilen, sollten topologySpreadConstraints gesetzt werden. Ohne diese Constraints kann es vorkommen, dass Pods auf einzelnen Nodes konzentriert werden, was Autoscaling-Entscheidungen verzerrt und die Ausfallsicherheit reduziert.

 

Verhalten bei Min/Max Scaling

Der Cluster-Autoscaler überwacht die Ressourcenauslastung Ihres Clusters und passt die Anzahl der Worker Nodes automatisch an:

SituationVerhalten
Pods pending wegen RessourcenmangelNeue Worker Nodes werden hinzugefügt (bis max erreicht ist)
Nodes werden nicht ausgelastetÜberflüssige Nodes werden entfernt (bis min erreicht ist)

 

Wie schnell skaliert der Cluster?

Das Hochskalieren (neue Worker Nodes hinzufügen) dauert in der Regel wenige Minuten, bis die neuen Nodes bereit sind. Das Herunterskalieren erfolgt bewusst verzögert und träger als das Hochskalieren, um zu vermeiden, dass Nodes zu früh entfernt werden und Pods neu platziert werden müssen.


Version Upgrades #

Version-Upgrades werden automatisch durch uns durchgeführt. Sie müssen nicht eingreifen – weder manuell upgraden noch Cluster-Konfigurationen anpassen.


Empfohlene Einstellungen für Hochverfügbarkeit #

 

Stateless Multi-Replica oder Application-Level HA

Für hochverfügbare Anwendungen empfehlen wir:

  • Stateless Workloads: Mindestens 2 Replicas deployen – so bleibt die Anwendung verfügbar, wenn ein Pod oder eine Node ausfällt
  • Stateful Workloads: Application-Level HA einsetzen (z.B. Datenbank-Replikation) – ein einzelner Pod auf einer Node bietet keinen Ausfallschutz

 

PodDisruptionBudget (PDB)

Ein PDB stellt sicher, dass bei geplanten Wartungsarbeiten (z.B. Node-Drain) immer eine Mindestanzahl an Pods verfügbar bleibt:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: name: example-app-pdb namespace: example-namespace
spec: minAvailable: 1 selector: matchLabels: app: example-app

 

TopologySpreadConstraints oder Node Anti-Affinity

Um Pods auf verschiedene Nodes zu verteilen, stehen zwei Mechanismen zur Verfügung:

Pod Anti-Affinity:

spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: example-app topologyKey: kubernetes.io/hostname

TopologySpreadConstraints – präzisere Kontrolle über die Verteilung:

spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: example-app

Worker Gruppen mit Labels #

Jede Worker Gruppe erhält automatisch ein Label basierend auf ihrem Namen:

k8s.at/node-group: <worker-gruppen-name>

Mit diesem Label können Sie Workloads gezielt auf bestimmten Worker Gruppen platzieren:

spec: nodeSelector: k8s.at/node-group: memory-optimized

Node Failure #

 

Automatische Node-Ersetzung (Node Reconciliation)

Wenn eine Worker Node ausfällt, wird diese automatisch erkannt und durch eine neue Node ersetzt. Sie müssen nicht manuell eingreifen.

 

Ephemeral Storage geht verloren

Wichtig: Bei einem Node-Ausfall geht aller lokaler Speicher (ephemeral storage) auf dieser Node unwiderruflich verloren. Das betrifft:

  • EmptyDir-Volumes
  • HostPath-Volumes
  • Container-Dateisystem (nicht in Persistent Volumes gespeicherte Daten)

Für persistente Daten verwenden Sie bitte Persistent Volume Claims (siehe Storage).


Long-lived Kubeconfig #

Wir empfehlen kurzlebige Tokens zu verwenden, welche über das Control Center erstellt werden können. Diese laufen automatisch ab und reduzieren das Risiko.

Sollte dennoch eine langlebige Kubeconfig notwendig sein, kann ein ServiceAccount mit dauerhaftem Token manuell erstellt werden:

# ServiceAccount erstellen
kubectl create serviceaccount long-lived-admin -n kube-system
# ClusterRoleBinding mit cluster-admin erstellen
kubectl create clusterrolebinding long-lived-admin \ --clusterrole=cluster-admin \ --serviceaccount=kube-system:long-lived-admin
# Secret für den ServiceAccount erstellen
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata: name: long-lived-admin-token namespace: kube-system annotations: kubernetes.io/service-account.name: long-lived-admin
type: kubernetes.io/service-account-token
EOF
# Token auslesen
kubectl get secret long-lived-admin-token -n kube-system -o jsonpath='{.data.token}' | base64 -d

Dieser Token kann anschließend den kurzlebigen Token der aus dem Control Center heruntergeladenen Kubeconfig ersetzen.

Hinweis: Dieser Token verfällt nicht automatisch. Stellen Sie sicher, dass er sicher aufbewahrt und bei Bedarf (z.B. Mitarbeiterwechsel) manuell widerrufen wird:

kubectl delete secret long-lived-admin-token -n kube-system
kubectl delete clusterrolebinding long-lived-admin
kubectl delete serviceaccount long-lived-admin -n kube-system