kubeone

Bare-Metal-Kubernetes mit KubeOne und Terraform

Abubakar Siddiq Ango
Abubakar Siddiq Ango Senior Developer Advocate
17. März 2026 19 Min. Lesezeit Mittelstufe
Bare-Metal Produktion Automatisierung

Voraussetzungen

  • 3 Server (Bare-Metal-Server oder VMs) mit Ubuntu 22.04+, jeweils mindestens 2 vCPU und 4 GB RAM
  • Zugriff auf alle Server über SSH-Schlüssel
  • Terraform 1.5+ ist installiert
  • KubeOne ist installiert – siehe „KubeOne installieren
  • Grundlegende Kenntnisse in Terraform

Wenn Sie Kubernetes auf Bare-Metal-Servern betreiben, erhalten Sie etwas, das Ihnen kein Cloud-Anbieter bieten kann: vollständige Kontrolle über Ihre Hardware ohne stündliche Rechenkosten. Es fallen keine Gebühren für Managed Services an, keine „Cloud-Steuer“ auf jeden CPU-Zyklus und keine überraschende Rechnung am Monatsende. Die Server gehören Ihnen, und Sie entscheiden selbst, wie genau sie konfiguriert werden.

Der Nachteil ist, dass Kubernetes auf Bare-Metal-Systemen bekanntermaßen sehr manuell ist. Ohne Integrationen von Cloud-Anbietern sind Sie selbst für Lastenausgleich, storage , Netzwerkkonfiguration und alle anderen Infrastrukturkomponenten verantwortlich, die bei Managed Services für Sie übernommen werden. Eine einzige falsch konfigurierte Firewall-Regel oder ein übersehener etcd-Port kann dazu führen, dass Sie stundenlang Fehler beheben müssen.

In diesem Tutorial werden Sie KubeOne zusammen mit Terraform verwenden, um einen hochverfügbaren Kubernetes-Cluster mit drei Knoten auf Bare-Metal-Servern bereitzustellen. KubeOne übernimmt die aufwendigen Aufgaben wie das Bootstrapping von kubeadm, die Konfiguration von etcd für Hochverfügbarkeit und die Bereitstellung des CNI-Plugins. Terraform stellt das strukturierte Ausgabeformat bereit, das KubeOne zur Erkennung Ihrer Infrastruktur nutzt. Am Ende verfügen Sie über einen produktionsreifen Cluster mit etcd-Redundanz über alle drei Control-Plane-Knoten hinweg und einen klaren Weg zum Hinzufügen von Worker-Knoten.

Dies ist kein Spielzeug-Cluster. Die Konfiguration, die Sie hier aufbauen werden, eignet sich für Produktions-Workloads und umfasst Audit-Protokollierung, knotenlokales DNS-Caching sowie eine Hochverfügbarkeitsarchitektur, die den Ausfall eines einzelnen Knotens toleriert.

Schritt 1: Planen Sie Ihre Netzwerkarchitektur

Bevor Sie irgendetwas installieren oder auch nur eine einzige Zeile Konfigurationscode schreiben, sollten Sie Ihr Netzwerk planen. Bei der Bare-Metal-Netzwerkkonfiguration werden Fehler nicht verziehen – es gibt keinen VPC-Assistenten, der Subnetze für Sie einrichtet, und eine falsche Firewall-Regel kann die Kommunikation zwischen den Knoten unbemerkt unterbrechen.

Privates Netzwerk

Alle drei Knoten der Steuerungsebene müssen über ein privates Netzwerk kommunizieren. Eine typische Wahl ist ein 10.0.0.0/24 Subnetz, aber jeder RFC-1918-Adressbereich ist geeignet. Entscheidend ist, dass der Datenverkehr zwischen den Knoten – also die etcd-Replikation, die Kubelet-Kommunikation und interne API-Aufrufe – nicht über das öffentliche Internet läuft. Befinden sich Ihre Server im selben Rechenzentrum, nutzen sie wahrscheinlich bereits ein privates Netzwerk. Befinden sie sich an verschiedenen Standorten, benötigen Sie ein VPN oder ein Overlay-Netzwerk zwischen ihnen.

Überprüfen Sie die private Netzwerkverbindung, bevor Sie fortfahren. Stellen Sie über SSH eine Verbindung zu jedem Knoten her und führen Sie einen Ping auf die privaten IP-Adressen der beiden anderen Knoten durch. Sollte einer dieser Pings fehlschlagen, brechen Sie den Vorgang an dieser Stelle ab und beheben Sie das Netzwerkproblem.

Öffentliche IP-Adressen

Jeder Knoten benötigt eine öffentliche IP-Adresse für zwei Zwecke: den SSH-Zugriff von Ihrem Arbeitsplatzrechner aus und den Zugriff auf die Kubernetes-API. Wenn Sie alles hinter einem VPN betreiben und keinen externen API-Zugriff benötigen, können Sie auf öffentliche IP-Adressen vollständig verzichten; in den meisten Produktionsumgebungen ist jedoch zumindest die Erreichbarkeit per SSH erforderlich.

API-Server-Lastenausgleich

In einem hochverfügbaren Cluster läuft der Kubernetes-API-Server auf allen drei Control-Plane-Knoten. Sie benötigen einen Load Balancer vor diesen Knoten, damit kubectl und Ihre Anwendungen immer einen funktionsfähigen API-Server erreichen, selbst wenn ein Knoten ausfällt. Sie haben mehrere Möglichkeiten:

  • Externer Hardware- oder Software-Load-Balancer – HAProxy, Nginx oder ein F5-Gerät. Dies ist der zuverlässigste Ansatz.
  • DNS-Round-Robin – Erstellen Sie einen DNS-A-Eintrag mit allen drei IP-Adressen der Steuerungsebene. Dies ist einfach, bietet jedoch keine Funktionsüberwachung. Wenn ein Knoten ausfällt, schlagen etwa ein Drittel der API-Anfragen fehl, bis Sie den DNS-Eintrag aktualisieren.
  • keepalived mit HAProxy — Führen Sie keepalived auf zwei dedizierten Hosts (oder auf zwei der Control-Plane-Knoten) aus, um ein automatisches Failover einer virtuellen IP zu gewährleisten. Dies ist der Goldstandard für Bare-Metal-Hochverfügbarkeit.

In diesem Tutorial richten Sie HAProxy in Schritt 4 ein. Wenn Sie bereits über einen Load Balancer verfügen, überspringen Sie diesen Schritt und verwenden Sie die IP-Adresse Ihres vorhandenen Load Balancers als Adresse für den API-Server.

Firewall-Regeln

Zwischen den Knoten der Steuerungsebene müssen bestimmte Ports geöffnet sein. Wenn diese falsch konfiguriert sind, kann der Cluster nicht gebildet werden oder es kommt zu zeitweiligen Ausfällen, die schwer zu diagnostizieren sind.

HafenProtokollZweck
6443TCPKubernetes-API-Server
2379–2380TCPKommunikation zwischen etcd-Client und -Peer
10250TCPKubelet-API
10259TCPkube-scheduler
10257TCPkube-controller-manager

Warnung: Der Port 6443 (API-Server) muss von jedem Ort aus erreichbar sein, an dem Sie kubectl ausführen. Die Ports 2379–2380 (etcd) sollten nur zwischen den Knoten der Steuerungsebene erreichbar sein. Die Freigabe von etcd für das Internet stellt eine kritische Sicherheitslücke dar – jeder, der Zugriff auf etcd hat, kann alle Geheimnisse, Konfigurations-Maps und Ressourcen in Ihrem Cluster lesen und ändern.

Öffnen Sie diese Ports zwischen allen drei Control-Plane-Knoten im privaten Netzwerk. Wenn Sie ufw unter Ubuntu:

# Auf jedem Knoten der Steuerungsebene ausführen
sudo ufw allow from 10.0.0.0/24 to any port 6443 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 2379:2380 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 10259 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 10257 proto tcp

Erlaube außerdem SSH (Port 22) von deiner Arbeitsstation aus und Port 6443 von jedem Ort aus, an dem du Zugriff auf kubectl benötigst.

Schritt 2: Erstellen der Terraform-Konfiguration

Bei Cloud-Anbietern richtet Terraform die eigentliche Infrastruktur ein – VMs, Netzwerke, Load Balancer. Bei Bare-Metal-Servern sind Ihre Server bereits vorhanden. Hier hat Terraform eine andere Aufgabe: Es generiert die strukturierte JSON-Ausgabe, die KubeOne erwartet, und beschreibt darin, wo sich Ihre Server befinden und wie sie erreichbar sind.

Für Bare-Metal-Umgebungen mag das übertrieben erscheinen, bietet Ihnen jedoch zwei wesentliche Vorteile. Erstens ist Ihre Infrastrukturbeschreibung versionsverwaltet und reproduzierbar. Zweitens bleibt Ihr Workflow unverändert, wenn Sie später zu einem Cloud-Anbieter wechseln – lediglich die Terraform-Konfiguration ändert sich.

Erstelle ein Projektverzeichnis:

mkdir kubeone-bare-metal && cd kubeone-bare-metal

Erstellen main.tf mit folgendem Inhalt:

# For bare metal, Terraform generates the output format that KubeOne expects.
# It does not provision infrastructure — your servers already exist.

variable "cluster_name" {
  type    = string
  default = "production"
}

variable "control_plane_hosts" {
  type = list(object({
    public_address  = string
    private_address = string
    ssh_user        = string
  }))
}

variable "api_server_address" {
  type        = string
  description = "Load balancer or primary control plane IP for API server access"
}

variable "ssh_private_key_file" {
  type    = string
  default = "~/.ssh/id_rsa"
}

output "kubeone_api" {
  value = {
    endpoint = {
      host            = var.api_server_address
      port            = 6443
      connect_timeout = 60
    }
  }
}

output "kubeone_hosts" {
  value = {
    control_plane = {
      cluster_name = var.cluster_name
      hosts = [
        for i, host in var.control_plane_hosts : {
          public_address       = host.public_address
          private_address      = host.private_address
          ssh_user             = host.ssh_user
          ssh_private_key_file = var.ssh_private_key_file
          ssh_agent_socket     = ""
        }
      ]
    }
  }
}

Die kubeone_api Die Ausgabe teilt KubeOne mit, wo der API-Server erreichbar ist. Die kubeone_hosts Die Ausgabe enthält Angaben zu jedem Knoten der Steuerungsebene – seine öffentlichen und privaten Adressen, den SSH-Benutzer sowie den für die Authentifizierung zu verwendenden SSH-Schlüssel.

Erstellen Sie nun terraform.tfvars mit Ihren tatsächlichen Serverdaten. Ersetzen Sie die Beispiel-IP-Adressen durch Ihre tatsächlichen Adressen:

cluster_name = "production"

api_server_address = "203.0.113.10"  # Die IP-Adresse Ihres Load Balancers oder des primären Knotens

control_plane_hosts = [
  {
    public_address  = "203.0.113.10"
    private_address = "10.0.0.10"
    ssh_user        = "ubuntu"
  },
  {
    public_address  = "203.0.113.11"
    private_address = "10.0.0.11"
    ssh_user        = "ubuntu"
  },
  {
    public_address  = "203.0.113.12"
    private_address = "10.0.0.12"
    ssh_user        = "ubuntu"
  }
]

Wenn Sie einen dedizierten Load Balancer verwenden, legen Sie Folgendes fest: api_server_address auf die IP-Adresse des Load Balancers. Falls Sie noch keinen Load Balancer haben, verwenden Sie vorerst die öffentliche IP-Adresse des ersten Control-Plane-Knotens – Sie richten HAProxy in Schritt 4 ein.

Schritt 3: Terraform-Ausgabe generieren

Terraform initialisieren, die Konfiguration anwenden und die von KubeOne benötigte JSON-Ausgabe exportieren:

terraform init
terraform apply -auto-approve
terraform output -json > tf.json

Da keine tatsächlichen Cloud-Ressourcen erstellt werden müssen, terraform apply wird sofort ausgeführt. Es wertet einfach die Variablen aus und generiert die Ausgaben.

Überprüfen Sie, ob die Ausgabe korrekt aussieht:

cat tf.json

Sie sollten eine JSON-Struktur sehen, die Folgendes enthält: kubeone_api mit dem Endpunkt Ihres API-Servers und kubeone_hosts mit den Details für alle drei Knoten der Steuerungsebene. Falls IP-Adressen falsch sind oder die Struktur fehlerhaft erscheint, korrigieren Sie dies terraform.tfvars und erneut ausführen terraform apply.

Die tf.json Diese Datei wird von KubeOne gelesen, um Ihre Infrastruktur zu erfassen. Jedes Mal, wenn Sie Ihre Serverdaten ändern – beispielsweise einen Knoten austauschen oder eine IP-Adresse ändern –, aktualisieren Sie terraform.tfvars, führen Sie die oben genannten Befehle erneut aus und generieren Sie tf.json.

Schritt 4: Konfigurieren des Lastenausgleichs für den API-Server

Für einen HA-Cluster in der Produktion benötigen Sie einen Load Balancer vor den drei Control-Plane-Knoten auf Port 6443. Ohne einen solchen verweist Ihre Kubeconfig auf einen einzelnen Knoten, und wenn dieser Knoten ausfällt, verlieren Sie den API-Zugriff, obwohl die beiden anderen Knoten funktionsfähig sind.

HAProxy ist die Standardlösung für den Lastausgleich bei Kubernetes auf Bare-Metal-Servern. Es ist ressourcenschonend, praxiserprobt und unterstützt TCP-Proxying mit Zustandsprüfungen.

Installieren Sie HAProxy auf einem separaten Host. Falls Ihnen kein dedizierter Host zur Verfügung steht, können Sie es auf einem der Control-Plane-Knoten ausführen; ein separater Host ist jedoch vorzuziehen, um einen Single Point of Failure zu vermeiden.

sudo apt update && sudo apt install haproxy -y

Füge die folgende Konfiguration zu /etc/haproxy/haproxy.cfg. Falls die Datei bereits Inhalt enthält, füge dies am Ende an:

Frontend Kubernetes-API
    bind *:6443
    Modus tcp
    Standard-Backend kubernetes-control-plane

Backend kubernetes-control-plane
    Modus tcp
    Balance Round-Robin
    Option tcp-check
    Server cp-1 10.0.0.10:6443 prüfen
    Server cp-2 10.0.0.11:6443 check
    Server cp-3 10.0.0.12:6443 check

Ersetzen Sie das 10.0.0.x Adressen mit den privaten IP-Adressen Ihrer Control-Plane-Knoten. Die Option tcp-check Die Anweisung bewirkt, dass HAProxy überprüft, ob jeder Backend-Server TCP-Verbindungen auf Port 6443 akzeptiert, bevor Datenverkehr an ihn weitergeleitet wird.

Starten Sie HAProxy neu und aktivieren Sie es:

sudo systemctl restart haproxy
sudo systemctl enable haproxy

Überprüfen Sie, ob es läuft:

sudo systemctl status haproxy

Wenn HAProxy auf einem separaten Host läuft, aktualisieren Sie api_server_address in terraform.tfvars auf die IP-Adresse des HAProxy-Hosts verweisen, dann neu generieren tf.json.

Option B: DNS-Round-Robin (einfacher, weniger zuverlässig)

Wenn Sie eine einfachere Lösung wünschen und eine geringere Verfügbarkeit in Kauf nehmen können, erstellen Sie einen DNS-A-Eintrag, der auf alle drei IP-Adressen der Steuerungsebene verweist:

k8s-api.example.com  A  203.0.113.10
k8s-api.example.com  A  203.0.113.11
k8s-api.example.com  A  203.0.113.12

Satz api_server_address zu k8s-api.example.com in Ihren Terraform-Variablen.

Der Nachteil ist erheblich: Beim DNS-Round-Robin gibt es keine Zustandsprüfungen. Fällt ein Knoten aus, schlagen etwa ein Drittel der Verbindungen fehl, bis man die IP-Adresse des ausgefallenen Knotens manuell aus dem DNS-Eintrag entfernt. Aufgrund der DNS-TTLs kann der veraltete Eintrag minuten- oder stundenlang bestehen bleiben.

Tipp: Verwenden Sie in der Produktion HAProxy in Verbindung mit Keepalived, um ein automatisches Failover des Load Balancers selbst zu gewährleisten. DNS-Round-Robin ist für Entwicklungs- und Staging-Umgebungen akzeptabel, sollte jedoch nicht für Workloads verwendet werden, die eine hohe Verfügbarkeit erfordern.

Schritt 5: Erstellen Sie das KubeOneCluster-Manifest

Das KubeOneCluster-Manifest definiert die Kubernetes-Version, den Cloud-Anbieter, das CNI-Plugin und die Cluster-Funktionen. Erstellen kubeone.yaml:

apiVersion: kubeone.k8c.io/v1beta2
kind: KubeOneCluster
name: production

versions:
  kubernetes: "v1.30.2"

cloudProvider:
  none: {}

containerRuntime:
  containerd: {}

clusterNetwork:
  cni:
    canal: {}
  podSubnet: "10.244.0.0/16"
  serviceSubnet: "10.96.0.0/12"

features:
  nodeLocalDNS:
    deploy: true
  staticAuditLog:
    enable: true
    config:
      policyFilePath: ""
      logPath: /var/log/kubernetes/audit.log
      logMaxAge: 30
      logMaxBackup: 10
      logMaxSize: 100

systemPackages:
  configureRepositories: true

Hier ist eine Beschreibung der einzelnen Abschnitte:

cloudProvider.none Teilt KubeOne mit, dass es sich um eine Bare-Metal-Bereitstellung handelt. Es wird kein Cloud-Controller-Manager installiert, und es werden keine cloudspezifischen Integrationen (wie die automatische Bereitstellung von Load Balancern oder die Erstellung persistenter Festplatten) konfiguriert. Sie kümmern sich selbst um storage Lastenausgleich.

containerRuntime.containerd legt „containerd“ als Container-Laufzeitumgebung fest. Da die Docker-Unterstützung in Kubernetes ab Version 1.24 entfernt wurde, ist „containerd“ nun die Standardwahl. KubeOne übernimmt die Installation und Konfiguration automatisch.

clusterNetwork.cni.canal nutzt Canal als CNI-Plugin. Canal kombiniert Calico zur Durchsetzung von Netzwerkrichtlinien mit Flannel für Overlay-Netzwerke. Dies ist das Standard-CNI von KubeOne und funktioniert gut auf Bare-Metal-Systemen, da das VXLAN-Overlay von Flannel die Kommunikation zwischen Pods über Knoten hinweg abwickelt, ohne dass eine spezielle Netzwerkkonfiguration Ihrer Infrastruktur erforderlich ist.

clusterNetwork.podSubnet und clusterNetwork.serviceSubnet Legen Sie die IP-Bereiche für Pods und Dienste fest. Die hier angezeigten Standardwerte entsprechen den Standardwerten von Kubernetes. Achten Sie darauf, dass sich diese Bereiche nicht mit Ihrem Knotennetzwerk (in unserem Beispiel 10.0.0.0/24) oder untereinander überschneiden.

features.nodeLocalDNS richtet auf jedem Knoten im Cluster einen DNS-Cache ein. Ohne diesen werden alle DNS-Abfragen eines Pods an die CoreDNS-Pods weitergeleitet, was in großen Clustern zu einem Engpass führen kann. Mit dem knotenlokalen DNS werden Abfragen zunächst aus einem lokalen Cache bedient, wodurch die Latenz und die Auslastung von CoreDNS verringert werden.

features.staticAuditLog aktiviert die Audit-Protokollierung in Kubernetes. Jede API-Anfrage – wer sie gestellt hat, was geändert wurde, wann dies geschah – wird in /var/log/kubernetes/audit.log auf den Knoten der Steuerungsebene. Dies ist für die Einhaltung von Sicherheitsvorschriften und zur Fehlerbehebung bei unerwarteten Änderungen in Ihrem Cluster unerlässlich.

systemPackages.configureRepositories ermöglicht es KubeOne, während der Bereitstellung die erforderlichen Paket-Repositorys (für containerd und Kubernetes) auf jedem Knoten zu konfigurieren.

Schritt 6: Cluster bereitstellen

Sie haben nun drei Dateien in Ihrem Projektverzeichnis: main.tf, terraform.tfvarsund kubeone.yamlsowie die generierten tf.json. Führen Sie den Provisioning-Befehl aus:

kubeone apply --manifest kubeone.yaml --tfjson tf.json

KubeOne fordert Sie auf, die geplanten Aktionen zu bestätigen. Überprüfen Sie die Ausgabe – dort wird angezeigt, welche Knoten bereitgestellt und welche Komponenten installiert werden – und bestätigen Sie anschließend.

Hier ist eine schrittweise Beschreibung des Ablaufs während der Bereitstellung:

  1. Überprüft die Konnektivität. KubeOne stellt über SSH eine Verbindung zu jedem Host her, überprüft, ob das Betriebssystem unterstützt wird, kontrolliert, ob die erforderlichen Ports offen sind, und stellt sicher, dass die Knoten sich im privaten Netzwerk gegenseitig erreichen können.

  2. Installiert die Container-Laufzeitumgebung. Containerd wird auf allen drei Knoten installiert und konfiguriert. KubeOne übernimmt die Einrichtung des Repositorys, die Installation der Pakete und die Konfiguration des systemd-Dienstes.

  3. Installiert Kubernetes-Binärdateien. kubeadm, kubelet und kubectl werden auf allen Knoten in der in Ihrem Manifest angegebenen Version installiert (in diesem Fall v1.30.2).

  4. Startet den ersten Knoten der Steuerungsebene. KubeOne läuft kubeadm init auf dem ersten Knoten mit der Clusterkonfiguration, die aus Ihrem Manifest und der Terraform-Ausgabe abgeleitet wurde. Dadurch wird das erste etcd-Mitglied erstellt, die Cluster-Zertifikate werden generiert und der API-Server wird gestartet.

  5. Verbindet sich mit den verbleibenden Knoten der Steuerungsebene. Der zweite und der dritte Knoten treten dem Cluster bei, indem sie kubeadm join. Jeder Knoten verfügt über einen eigenen etcd-Member, eine eigene API-Server-Instanz, einen eigenen Controller-Manager und einen eigenen Scheduler.

  6. Konfiguriert etcd für Hochverfügbarkeit. Mit drei Control-Plane-Knoten arbeitet etcd als Cluster mit drei Mitgliedern. Dies gewährleistet Fehlertoleranz – der Cluster bleibt betriebsbereit, selbst wenn ein einzelnes etcd-Mitglied (und der zugehörige Knoten) ausfällt. Für das Quorum müssen zwei der drei Mitglieder betriebsbereit sein.

  7. CNI wird bereitgestellt. Canal wird im gesamten Cluster installiert und ermöglicht so die Vernetzung zwischen Pods sowie die Durchsetzung von Netzwerkrichtlinien.

  8. Stellt den Machine-Controller bereit. Der Kubermatic Machine-Controller wird im Cluster für die deklarative Verwaltung der Worker-Knoten installiert. Auf Bare-Metal-Systemen wird hierfür der statische Provider verwendet.

  9. Erstellt eine kubeconfig-Datei. A production-kubeconfig Die Datei wird in Ihrem aktuellen Verzeichnis erstellt. Diese Datei enthält die Anmeldedaten, die für den Zugriff auf Ihren Cluster mit kubectl erforderlich sind.

Der gesamte Vorgang dauert 5 bis 10 Minuten, je nach Ihrer Netzwerkgeschwindigkeit und der Serverleistung. Unterbrechen Sie ihn bitte nicht.

Warnung: Falls die Bereitstellung unterwegs fehlschlägt – etwa aufgrund eines Netzwerk-Timeouts, eines Fehlers beim Herunterladen eines Pakets oder einer unterbrochenen SSH-Verbindung – führen Sie folgenden Befehl aus: kubeone apply wieder mit denselben Argumenten. Der Befehl ist idempotent. Er erkennt, was bereits abgeschlossen wurde, und macht dort weiter, wo er aufgehört hat. Führen Sie den Befehl nicht aus kubeone zurücksetzen es sei denn, Sie möchten den gesamten Cluster bewusst abbauen und ganz von vorne beginnen.

Schritt 7: Überprüfen Sie Ihren Cluster

Lege die kubeconfig-Datei an und überprüfe deine Knoten:

export KUBECONFIG=$(pwd)/production-kubeconfig
kubectl get nodes

Du solltest drei Knoten sehen, alle in Bereit Zustand, alles mit dem Steuerungsebene Rolle:

NAME    STATUS   ROLLEN           ALTER   VERSION
cp-1    Bereit    Steuerungsebene   5m    v1.30.2
cp-2    Bereit    Steuerungsebene   4m    v1.30.2
cp-3    Bereit    Steuerungsebene   4m    v1.30.2

Wenn ein Knoten anzeigt, dass Nicht bereit, überprüfen Sie die Kubelet-Protokolle auf diesem Knoten: journalctl -u kubelet -f.

Den Zustand des etcd-Clusters überprüfen

Der etcd-Cluster ist das Rückgrat Ihrer Kubernetes-Steuerungsebene. Stellen Sie sicher, dass alle drei Mitglieder fehlerfrei sind:

kubectl -n kube-system exec -it etcd-cp-1 -- etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
  member list -w table

Ersetzen etcd-cp-1 durch den tatsächlichen Namen eines Ihrer etcd-Pods (überprüfen Sie dies mit kubectl get pods -n kube-system | grep etcd). Es sollten drei Mitglieder angezeigt werden, alle mit begonnen Status.

System-Pods überprüfen

Überprüfen Sie, ob alle Systemkomponenten laufen:

kubectl get pods -A

Sie sollten Pods für den API-Server, den Controller-Manager, den Scheduler, etcd, CoreDNS, Canal, das lokale DNS des Knotens und den Maschinen-Controller sehen. Alle Pods sollten sich in Laufen oder Abgeschlossen Zustand. Jeder Pod in CrashLoopBackOff oder In Bearbeitung weist auf ein Problem hin – überprüfen Sie die Protokolle mit kubectl logs -n <namespace> <pod-name>.

Schritt 8: Worker-Knoten mit MachineDeployments hinzufügen

Ihr Cluster verfügt derzeit über drei Control-Plane-Knoten, jedoch über keine dedizierten Worker-Knoten. In der Produktion ist es in der Regel ratsam, die Aufgaben der Control Plane und der Worker voneinander zu trennen, damit die Anwendungs-Workloads nicht mit etcd und dem API-Server um Ressourcen konkurrieren.

In Bare-Metal-Umgebungen haben Sie zwei Möglichkeiten, Worker-Knoten hinzuzufügen.

Option A: Maschinenbereitstellung mit statischem Anbieter

Wenn Ihre Worker-Knoten über SSH-Zugriff von der Control Plane verfügen, können Sie den statischen Provider des Kubermatic-Maschinencontrollers verwenden. Erstellen Sie eine Datei mit dem Namen workers.yaml:

apiVersion: cluster.k8s.io/v1alpha1
Art: MachineDeployment
Metadaten:
  name: production-workers
  Namespace: kube-system
spec:
  Replikate: 3
  Auswahl:
    matchLabels:
      cluster: Produktion
  Vorlage:
    Metadaten:
      Labels:
        Cluster: Produktion
    Spezifikation:
      Anbieter-Spezifikation:
        Wert:
          cloudProvider: "none"
          cloudProviderSpec: null
          Betriebssystem: "ubuntu"
          Betriebssystemspezifikation:
            distUpgradeOnBoot: false
      Versionen:
        kubelet: "v1.30.2"

So wenden Sie es an:

kubectl apply -f workers.yaml

Tipp: Bei Bare-Metal-Systemen ohne Cloud-API übernimmt der statische Provider des Maschinencontrollers die Einbindung der Knoten, sofern Sie die SSH-Zugangsdaten bereitstellen. Bei reinen Bare-Metal-Konfigurationen, bei denen die Steuerungsebene keinen SSH-Zugriff auf die Worker-Knoten hat, ist die manuelle Einbindung jedoch einfacher und berechenbarer.

Option B: Manuelles Hinzufügen von Worker-Knoten

Dieser Ansatz ist unkompliziert und funktioniert in jeder Bare-Metal-Umgebung. Erstellen Sie auf der Steuerungsebene einen Join-Befehl:

kubeadm token create --print-join-command

Das ergibt etwa Folgendes:

kubeadm join 203.0.113.10:6443 --token abc123.xyz789 --discovery-token-ca-cert-hash sha256:def456...

Verbinden Sie sich per SSH mit jedem Worker-Knoten und führen Sie den angezeigten Befehl aus:

kubeadm join 203.0.113.10:6443 --token abc123.xyz789 --discovery-token-ca-cert-hash sha256:def456...

Bevor Sie dies ausführen, stellen Sie sicher, dass auf jedem Worker-Knoten „containerd“, „kubeadm“ und „kubelet“ in derselben Version wie auf der Control Plane installiert sind. KubeOne verwaltet keine manuell hinzugefügten Worker-Knoten; daher sind Sie dafür verantwortlich, die Kubernetes-Version bei Upgrades auf dem neuesten Stand zu halten.

Überprüfen Sie nach dem Beitritt, ob die Worker-Knoten angezeigt werden:

kubectl get nodes

Sie sollten nun Ihre Worker-Knoten neben den drei Control-Plane-Knoten sehen, alle in Bereit Zustand.

Schritt 9: Eine Test-Workload bereitstellen

Stellen Sie auf den Worker-Knoten im Cluster eine einfache Workload bereit, um zu überprüfen, ob alles ordnungsgemäß funktioniert:

kubectl create deployment nginx--image=nginx:latest--replicas=6
kubectl get pods -o wide

Die sechs Nginx-Pods sollten auf Ihre Worker-Knoten verteilt sein. Falls alle Pods auf einem einzigen Knoten landen, überprüfen Sie, ob die anderen Knoten Bereit sich in einem gültigen Zustand befinden und keine Einschränkungen aufweisen, die eine Einplanung verhindern.

Um die Netzwerkverbindung zwischen den Pods zu überprüfen, wechsle in einen Pod und rufe dort den Befehl `curl` auf:

kubectl exec -it $(kubectl get pods -l app=nginx -o jsonpath='{.items[0].metadata.name}') -- curl -s -o /dev/null -w "%{http_code}" http://$(kubectl get pods -l app=nginx -o jsonpath='{.items[1].status.podIP}')

A 200 Die Antwort bestätigt, dass die Kommunikation zwischen den Pods über Canal ordnungsgemäß funktioniert.

Räumen Sie die Testumgebung auf, wenn Sie fertig sind:

kubectl delete deployment nginx

Schritt 10: Checkliste für die Produktionsbereinigung

Ihr Cluster ist funktionsfähig, aber „funktionsfähig“ und „einsatzbereit“ sind nicht dasselbe. Bevor Sie echte Workloads ausführen, gehen Sie diese Checkliste durch:

  • API-Server-Load-Balancer mit Zustandsprüfungen — HAProxy mit Keepalived für automatisches Failover, nicht nur einfaches Round-Robin
  • storage für persistenten storage — Longhorn für verteilten storage, Rook-Ceph für S3-kompatiblen storage oder NFS für einfachere Konfigurationen
  • Überwachungsstapel — Prometheus für die Erfassung von Metriken und die Benachrichtigung, Grafana für Dashboards
  • Protokollzusammenfassung — Loki (Lightweight) oder der EFK-Stack (Elasticsearch, Fluentd, Kibana) für die zentralisierte Protokollierung
  • Backup-Lösung — Velero für Cluster-, System- und Ressourcen-Backups sowie separate Backup-Prozesse für persistente Volumes
  • Netzwerkrichtlinien — Standardmäßig alle Zugriffe verweigern in jedem Namespace, mit expliziten Zulassungsregeln für erforderliche Datenverkehrsströme
  • Sicherheitsstandards für Pods — Durchsetzen der eingeschränkt oder Ausgangswert Pod-Sicherheitsstandard auf Namespace-Ebene
  • Zertifikatsrotation — kubeadm übernimmt die automatische Zertifikatsrotation; überprüfen Sie jedoch regelmäßig die Ablaufdaten der Zertifikate, um sicherzustellen, dass dies funktioniert
  • Notfallplan — Dokumentieren Sie das Verfahren zur Erstellung und Wiederherstellung von etcd-Snapshots und testen Sie es mindestens einmal, bevor Sie es im Notfall benötigen

Jeder dieser Punkte verdient eine eigene eingehende Betrachtung, doch der von Ihnen aufgebaute Cluster bildet die Grundlage für alle diese Punkte.

Fehlerbehebung

Terraform-State-Drift

Wenn sich Ihre Bare-Metal-Infrastruktur außerhalb von Terraform ändert – ein Server wird ausgetauscht, eine IP-Adresse ändert sich oder Sie fügen einen neuen Knoten hinzu –, aktualisieren Sie terraform.tfvars mit den neuen Angaben und erneut:

terraform apply -auto-approve
terraform output -json > tf.json

Führen Sie dann Folgendes aus kubeone apply erneut, um den Cluster-Status mit der neuen Infrastrukturbeschreibung abzugleichen.

Verlust des etcd-Quorums während der Bereitstellung

Falls KubeOne beim Hinzufügen des zweiten oder dritten Control-Plane-Knotens fehlschlägt, befindet sich der etcd-Cluster möglicherweise in einem unvollständigen Zustand. Führen Sie kubeone apply erneut – es erkennt den unvollständigen Zustand und versucht, die Konvergenz wiederherzustellen. KubeOne ist darauf ausgelegt, dieses Szenario reibungslos zu bewältigen.

Falls wiederholt kubeone apply Wenn die Versuche fehlschlagen, lässt sich der etcd-Zustand möglicherweise nur durch einen vollständigen Reset wiederherstellen. Führen Sie in diesem Fall kubeone reset --manifest kubeone.yaml --tfjson tf.json um den Cluster vollständig zu entfernen, und führen Sie anschließend kubeone apply um neu anzufangen. Dadurch werden alle Clusterdaten gelöscht; führen Sie diesen Vorgang daher nur bei der Ersteinrichtung durch.

Zeitüberschreitungen bei SSH-Verbindungen

Falls KubeOne keine Verbindung zu Ihren Servern herstellen kann, überprüfen Sie Folgendes:

  1. Ihr SSH-Schlüssel ist korrekt und verfügt über die richtigen Berechtigungen (chmod 600 ~/.ssh/id_rsa).
  2. Der in terraform.tfvars ist auf jedem Server vorhanden und verfügt über passwortloses sudo.
  3. Die Firewall-Regeln lassen SSH-Verbindungen (Port 22) von Ihrem Arbeitsplatzrechner zu jedem Server zu.
  4. Es gibt keinen zwischengeschalteten NAT oder Bastion-Host, der die Verbindung beeinträchtigt.

Manuell mit ausführlicher Ausgabe testen:

ssh -v -i ~/.ssh/id_rsa ubuntu@203.0.113.10

Pods stecken im Container fest

Dies ist fast immer ein CNI-Problem. Überprüfen Sie, ob die Canal-Pods auf jedem Knoten laufen:

kubectl get pods -n kube-system -l k8s-app=canal

Falls die Canal-Pods nicht laufen, überprüfen Sie deren Protokolle auf Fehler. Die häufigste Ursache ist, dass die Knoten sich im privaten Netzwerk nicht erreichen können. Stellen Sie sicher, dass die Schnittstellen des privaten Netzwerks aktiv sind und dass die Firewall-Regeln den Datenverkehr über den VXLAN-Port (UDP 8472) zwischen allen Knoten zulassen.

API-Server nach der Einrichtung nicht erreichbar

Falls kubectl nach der Bereitstellung keinen Kontakt zum API-Server herstellen kann:

  1. Verwendung eines Load Balancers: Stellen Sie sicher, dass HAProxy auf allen drei Knoten läuft und den Datenverkehr an Port 6443 weiterleitet. Überprüfen Sie sudo systemctl status haproxy und die HAProxy-Protokolle überprüfen.
  2. Verwendung von DNS-Round-Robin: Überprüfen Sie, ob alle drei IP-Adressen im DNS-Eintrag enthalten sind und ob der Eintrag übernommen wurde. Führen Sie dig k8s-api.example.com um zu überprüfen.
  3. Firewall: Stellen Sie sicher, dass Port 6443 von Ihrem Arbeitsplatzrechner zum Load Balancer oder direkt zu den Control-Plane-Knoten offen ist.
  4. Kubeconfig: Vergewissern Sie sich, dass die Server Stellen Sie sicher, dass das Feld in Ihrer kubeconfig auf die richtige Adresse und den richtigen Port verweist.

Nächste Schritte

Wenn Ihr Bare-Metal-Cluster läuft, sollten Sie sich diese weiteren Tutorials ansehen:

Zusammenfassung

Sie haben mithilfe von KubeOne und Terraform einen hochverfügbaren Kubernetes-Cluster mit drei Knoten auf Bare-Metal-Servern bereitgestellt. Der Cluster nutzt etcd, das auf alle drei Control-Plane-Knoten verteilt ist, verwendet Canal für das Pod-Netzwerk und die Netzwerkrichtlinien und verfügt standardmäßig über Audit-Protokollierung sowie ein knotenlokales DNS.

Das Wichtigste, was man bei dieser Konfiguration beachten muss, ist, dass kubeone apply ist Ihr einziger Befehl sowohl für die Erstkonfiguration als auch für die laufende Wartung. Wenn Sie Kubernetes aktualisieren müssen, ändern Sie die Version in kubeone.yaml und ausführen kubeone apply erneut. Wenn ein Knoten ausfällt und Sie ihn ersetzen, aktualisieren Sie terraform.tfvars, regenerieren tf.jsonund ausführen kubeone apply. Mit demselben Befehl lassen sich Installation, Aktualisierungen und Reparaturen durchführen.

„Bare-Metal-Kubernetes“ erfordert zwar einen höheren anfänglichen Aufwand als ein Managed Service, bietet jedoch erhebliche Kontrollvorteile und Kosteneinsparungen. Sie verfügen nun über einen Cluster, der vollständig Ihnen gehört, ohne laufende Rechenkosten und ohne Bindung an einen bestimmten Anbieter. Die Checkliste zur Produktionssicherung in Schritt 10 dient Ihnen als Leitfaden, um daraus eine vollständig produktionsreife Plattform zu machen.