Wiki workshop · Γερμανικά

Provisionierung von Kubernetes in nativen VMs

Diese Datei dokumentiert die Provisionierung von Kubernetes in virtuellen Maschinen für den Kubernetes-Workshop. Sie deckt lokale VMs mit VirtualBox oder VMware, Remote-VMs bei einem Provider, Vagrant-basierte VM-Labs sowie leichtgewicht…

Παραδείγματα workshop σε ZIP
Πρόοδος μάθησης στη web app

Σημειώστε κεφάλαια ως αναγνωσμένα, δείτε την πρόοδο ανάγνωσης και ολοκληρώστε πρακτικές ενότητες αφού ενεργοποιήσετε την προαιρετική web app. Η πρόοδος του workshop υπολογίζεται από τα κεφάλαια όλων των σελίδων τεκμηρίωσης. Οι επιλογές μένουν σε αυτή τη συσκευή· δεν υπάρχει λογαριασμός ή συγχρονισμός με server. Δεν απαιτείται εγκατάσταση. Η ανάκληση στις Ρυθμίσεις απορρήτου διαγράφει την πρόοδο.

Πρόοδος ανάγνωσης0%
0%
0 / 40 κεφάλαια που διαβάστηκαν
Άλλη γλωσσική έκδοσηΑγγλικά ↗

Provisionierung von Kubernetes in nativen VMs

1. Zweck

Diese Datei dokumentiert die Provisionierung von Kubernetes in virtuellen Maschinen für den Kubernetes-Workshop. Sie deckt lokale VMs mit VirtualBox oder VMware, Remote-VMs bei einem Provider, Vagrant-basierte VM-Labs sowie leichtgewichtige Alternativen wie kind und minikube ab.

Du nutzt dieses Dokument, wenn du eine reproduzierbare Lab-Umgebung brauchst, in der echte Systemdienste, Netzwerkpfade, Container Runtime, kubelet, CNI, Storage, Ingress und Security-Kontrollen nachvollziehbar sind.

Die Datei ersetzt eine reine Kurzbeschreibung der Ubuntu-VM durch eine vollständige Provisionierungsreferenz. Die Sprache ist bewusst praktisch: Du sollst nach jedem Abschnitt wissen, welche Variante du wählen solltest, welche Kommandos nötig sind, welche Validierung dazugehört und welche Fehlerbilder typisch sind.

01 / 40

2. Zielbild

Eine VM-basierte Kubernetes-Umgebung soll drei Ziele erfüllen:

  1. Reproduzierbarkeit: Du kannst die Umgebung löschen und mit denselben Parametern wieder aufbauen.
  2. Realitätsnähe: Du arbeitest mit Linux, systemd, Container Runtime, kubelet, Netzwerkadaptern, Firewalls, Zertifikaten und echten Cluster-Komponenten.
  3. Sicherheit: Du trennst Lab, Host und produktive Identitäten. Du nutzt keine echten Kundendaten, keine produktiven Tokens und keine ungeprüften Cluster-Zugänge.
flowchart TD
    Host[Dein Host: Linux, macOS oder Windows] --> LocalHypervisor[Lokaler Hypervisor]
    Host --> RemoteVM[Gemietete Remote-VM]
    LocalHypervisor --> VirtualBox[VirtualBox]
    LocalHypervisor --> VMware[VMware Workstation oder Fusion]
    LocalHypervisor --> Vagrant[Vagrant als Automatisierungsschicht]
    VirtualBox --> KubeadmLocal[kubeadm-Cluster]
    VMware --> KubeadmLocal
    Vagrant --> KubeadmLocal
    Host --> Kind[kind: Kubernetes in Containern]
    Host --> Minikube[minikube: lokale oder Remote-VM]
    RemoteVM --> KubeadmRemote[kubeadm, k3s, RKE2, k0s oder minikube SSH]
    KubeadmLocal --> Labs[Workshop-Labs]
    Kind --> Labs
    Minikube --> Labs
    KubeadmRemote --> Labs

Für Grundlagen, kurze Übungen und CI-nahe Tests reichen kind oder minikube. Für Cluster-Lifecycle, kubelet, kubeadm, CNI, Storage, Node-Wartung, Firewall und HA brauchst du echte VMs.

02 / 40

3. Abgrenzung

Dieses Dokument beschreibt VM-basierte Lab- und Trainingsumgebungen. Es ersetzt nicht:

  • eine produktive Plattformarchitektur,
  • eine vollständige Cloud-IaC-Referenz,
  • ein Security-Hardening nach Unternehmensstandard,
  • eine Anbieter-spezifische Managed-Kubernetes-Dokumentation,
  • ein komplettes Disaster-Recovery-Konzept.

Trotzdem werden produktionsnahe Muster verwendet. Gerade in Remote-VMs musst du die Umgebung so behandeln, als wäre sie exponiert: Firewall, SSH-Härtung, kontrollierter API-Zugriff und konsequenter Rückbau gehören zur Basis.

03 / 40

4. Entscheidungsmodell

Wähle den Bereitstellungsweg anhand des Lernziels.

Ziel Empfohlener Weg Warum
Kubernetes-Objekte verstehen kind oder minikube Schnell, reproduzierbar, wenig Infrastruktur
Deployments, Services, Ingress üben kind, minikube, kubeadm-Single-Node Gute Balance aus Geschwindigkeit und Realitätsnähe
kubeadm, kubelet, containerd, CNI lernen kubeadm in VMs Echte Node- und Bootstrap-Erfahrung
Multi-Node-Scheduling üben kubeadm mit 1 Control Plane und 2 Workern oder kind Multi-Node Du siehst Node-Auswahl, Taints und Ressourcengrenzen
HA-Control-Plane verstehen kubeadm HA mit 3 Control-Plane-VMs Realitätsnahes Modell mit etcd-Mehrheit
Remote-Betrieb simulieren gemietete VMs mit privatem Netzwerk Firewall, Latenz, öffentliche IPs und SSH werden sichtbar
Wiederholbare Workshop-VMs bauen Vagrant Ein Befehl erzeugt mehrere gleich konfigurierte VMs
Edge- oder Homelab-Variante testen k3s, RKE2, k0s oder Talos Alternative Distributionen mit anderem Betriebsmodell
Zertifikate, Ingress und LoadBalancer testen VM-Cluster plus MetalLB oder minikube tunnel Externe Erreichbarkeit wird nachvollziehbar
flowchart TD
    Start[Was willst du lernen?] --> Objects[Kubernetes-Objekte]
    Start --> NodeOps[Node- und Clusterbetrieb]
    Start --> Remote[Remote-Betrieb]
    Start --> Repeat[Reproduzierbare lokale VMs]
    Objects --> Fast[Wähle kind oder minikube]
    NodeOps --> Kubeadm[Wähle kubeadm in VMs]
    Remote --> Provider[Wähle gemietete VMs mit privatem Netz]
    Repeat --> Vagrant[Wähle Vagrant mit VirtualBox oder VMware]
    Kubeadm --> HA{HA nötig?}
    HA -->|Nein| SingleOrMulti[Single-Node oder 1+2]
    HA -->|Ja| ThreeCP[3 Control Planes plus Worker]
04 / 40

5. Referenzarchitekturen

5.1 Lokaler Single-Node-Cluster

Diese Variante ist für Grundlagen, Manifest-Übungen, kubectl, Probes, Services und erste Ingress-Labs geeignet.

flowchart TD
    Laptop[Laptop oder Workstation] --> VM[Ubuntu VM]
    VM --> Runtime[containerd]
    VM --> Kubelet[kubelet]
    VM --> ControlPlane[API Server, Scheduler, Controller Manager, etcd]
    VM --> Pods[Workshop-Pods]
    VM --> CNI[CNI]
    Laptop --> Kubectl[kubectl]
    Kubectl --> ControlPlane

5.2 Lokaler Multi-Node-Cluster

Diese Variante ist für Scheduling, Node-Wartung, CNI, NetworkPolicies, Storage und Rollout-Verhalten besser geeignet.

flowchart LR
    Host[Host] --> CP[VM cp1: Control Plane]
    Host --> W1[VM worker1]
    Host --> W2[VM worker2]
    CP --> ETCD[etcd]
    CP --> API[API Server]
    W1 --> Pods1[Pods]
    W2 --> Pods2[Pods]
    API --> W1
    API --> W2
    W1 <-->|Pod-Netz| W2

5.3 Remote-VM-Cluster

Diese Variante ist für realistischere Netzwerk- und Firewall-Übungen geeignet.

flowchart TD
    Admin[Dein Rechner] -->|SSH 22| BastionOderCP[Bastion oder Control Plane]
    Admin -->|kubectl 6443, eingeschränkt| API[API Server]
    subgraph Provider[Provider-Netz]
        API --> CP1[Control Plane VM]
        CP1 --> PrivateNet[Privates Netzwerk]
        PrivateNet --> W1[Worker VM]
        PrivateNet --> W2[Worker VM]
        W1 --> Workloads[Workloads]
        W2 --> Workloads
    end
    Internet[Internet] -->|80/443 optional| Ingress[Ingress oder LoadBalancer]
    Ingress --> Workloads

5.4 kind-Cluster

kind nutzt Container als Kubernetes-Nodes. Das ist keine VM, aber für viele Workshop-Labs sehr effizient.

flowchart TD
    Host[Host mit Docker oder Podman] --> DockerNet[Container-Netzwerk]
    DockerNet --> KindCP[Container: control-plane]
    DockerNet --> KindW1[Container: worker]
    DockerNet --> KindW2[Container: worker]
    KindCP --> APIServer[API Server]
    KindW1 --> Pods1[Pods]
    KindW2 --> Pods2[Pods]
    Host --> Kubectl[kubectl Kontext kind-workshop]
    Kubectl --> APIServer

5.5 minikube-Cluster

minikube kann als VM, Container, Bare-Metal- oder SSH-basierte Umgebung laufen. Für diesen Workshop sind Docker-, VirtualBox-, VMware- und SSH-Treiber relevant.

flowchart LR
    Host[Host] --> Profile[minikube Profil]
    Profile --> Driver{Treiber}
    Driver --> Docker[Docker oder Podman]
    Driver --> VM[VirtualBox, VMware, KVM, QEMU]
    Driver --> SSH[Remote-VM per SSH]
    Docker --> Cluster[Kubernetes]
    VM --> Cluster
    SSH --> Cluster
    Cluster --> Addons[Ingress, Dashboard, Metrics Server, Registry]
05 / 40

6. Begriffe und Rollen

Begriff Bedeutung
Host Dein physischer Rechner oder die Workstation, auf der lokale VMs laufen
Gast Die VM, in der Linux und Kubernetes-Komponenten laufen
Control Plane Kubernetes-Steuerungsebene mit API Server, Scheduler, Controller Manager und etcd
Worker Node, auf dem Workload-Pods laufen
CNI Netzwerk-Plugin für Pod-Netzwerk und häufig NetworkPolicy-Enforcement
CSI Storage-Plugin-Schnittstelle für Kubernetes-Volumes
NAT-Netz Gast kommt ins Internet, ist vom Host oder LAN aber nur eingeschränkt erreichbar
Host-only-Netz Nur Host und VMs sprechen miteinander; ideal für Lab-Cluster
Bridged-Netz VM hängt wie ein eigener Rechner im LAN; praktisch, aber abhängig vom Netzwerk
Private Provider-Netz Internes Netzwerk eines Cloud- oder VM-Providers zwischen gemieteten VMs
API-Endpunkt Adresse, über die kubectl, kubelet und Control-Plane-Komponenten den API Server erreichen
Pod-CIDR IP-Bereich für Pods
Service-CIDR IP-Bereich für Kubernetes-Services
06 / 40

7. Mindestanforderungen

7.1 Host-Anforderungen

Variante Host-CPU Host-RAM Host-Disk Bemerkung
kind Single-Node 2 Kerne 4 GB 20 GB Docker oder Podman erforderlich
kind Multi-Node 4 Kerne 8 GB 30 GB Gut für schnelle Multi-Node-Labs
minikube 2 bis 4 Kerne 4 bis 8 GB 20 bis 40 GB Abhängig vom Treiber
kubeadm Single-Node VM 4 Kerne 8 GB 40 GB Realistische Systemdienste
kubeadm 1+2 VMs 6 bis 8 Kerne 16 bis 24 GB 120 GB Empfehlenswert für Workshop-Trainer
kubeadm HA lokal 12 Kerne 32 GB 200 GB Auf Laptops oft zu schwer
Remote-VM 1+2 Providerabhängig 3 VMs mit 4 GB bis 8 GB je 40 GB Privates Netzwerk bevorzugt

7.2 VM-Anforderungen

Rolle vCPU RAM Disk Empfehlung
Single-Node 2 bis 4 4 bis 8 GB 40 GB Für Grundlagen ausreichend
Control Plane 2 bis 4 4 bis 8 GB 40 bis 80 GB etcd braucht zuverlässige Disk-I/O
Worker 2 bis 4 4 bis 8 GB 40 bis 80 GB Mehr RAM für Workload-Labs
HA-Control-Plane 2 bis 4 je VM 4 bis 8 GB je VM 60 bis 100 GB Ungerade Anzahl Control-Plane-VMs

7.3 Betriebssystem

Empfohlen:

  • Ubuntu Server LTS 24.04 oder neuer für Standardlabs,
  • Debian Stable für schlanke Labs,
  • Rocky Linux, AlmaLinux oder RHEL für Red-Hat-nahe Umgebungen,
  • Talos Linux, wenn du ein Kubernetes-spezifisches, unveränderliches OS demonstrieren willst.

Für Workshop-Standardisierung ist Ubuntu Server LTS am einfachsten, weil Paketnamen, Cloud-Images und Vagrant-Boxen sehr gut verfügbar sind.

07 / 40

8. Planungscheckliste

Bevor du VMs erzeugst, klärst du diese Punkte:

  • Welche Cluster-Variante brauchst du: kind, minikube, kubeadm, k3s, RKE2, k0s oder Talos?
  • Wie viele Nodes brauchst du?
  • Welche IP-Adressen bekommen Control Plane und Worker?
  • Welcher API-Endpunkt wird in Zertifikaten und kubeconfig verwendet?
  • Welche Pod- und Service-CIDRs nutzt du?
  • Welche Netzbereiche sind im Host, VPN, Büro-LAN oder Provider-Netz bereits belegt?
  • Brauchst du Ingress über 80/443?
  • Brauchst du LoadBalancer-Services?
  • Brauchst du lokale Registry oder Image-Mirror?
  • Welche Add-ons werden installiert?
  • Wie baust du die Umgebung zurück?
  • Wo liegen kubeconfigs, SSH-Keys und temporäre Tokens?
  • Sind produktive Secrets ausgeschlossen?

Empfohlene Standardwerte für Labs:

Zweck Wert
Host-only-Netz lokal 192.168.56.0/24
Control Plane 192.168.56.10
Worker 1 192.168.56.21
Worker 2 192.168.56.22
Pod-CIDR 10.244.0.0/16 oder CNI-spezifisch
Service-CIDR 10.96.0.0/12
NodePort Kubernetes-Default 30000-32767
Kubernetes Minor Workshop-Variable, z. B. v1.36
08 / 40

9. Netzdesign für VM-Cluster

VM-Netzwerk ist der häufigste Grund für fehlerhafte Labs. Trenne bewusst:

  1. Management-Zugriff: SSH, kubectl, Host-to-VM.
  2. Node-to-Node: kubelet, CNI, Pod-Verkehr, Control-Plane-Kommunikation.
  3. Internet-Egress: Paketinstallation und Image-Pulls.
  4. Externe Workload-Erreichbarkeit: Ingress, NodePort, LoadBalancer.
flowchart TD
    Host[Host] -->|SSH und kubectl| Mgmt[Management-Netz]
    subgraph Cluster[VM-Cluster]
        Mgmt --> CP[cp1]
        Mgmt --> W1[worker1]
        Mgmt --> W2[worker2]
        CP <-->|Cluster-Kommunikation| W1
        CP <-->|Cluster-Kommunikation| W2
        W1 <-->|Pod-Verkehr| W2
    end
    Cluster -->|Image Pulls und Pakete| Internet[Internet]
    Client[Browser oder curl] -->|80/443 oder NodePort| Ingress[Ingress/NodePort]
    Ingress --> W1
    Ingress --> W2

9.1 NAT

NAT ist gut für Internetzugriff aus der VM. NAT allein ist für Multi-Node-Kubernetes schlecht, weil VMs sich nicht automatisch über stabile interne Adressen erreichen.

Nutze NAT für Paketdownloads, aber nicht als einziges Cluster-Netz.

9.2 Host-only

Host-only ist für lokale Labs ideal. Dein Host und die VMs sprechen in einem isolierten Netz. Das Büro-LAN bleibt unbeeinflusst.

Typischer Aufbau:

  • Adapter 1: NAT für Internetzugriff.
  • Adapter 2: Host-only für Cluster-Kommunikation.

9.3 Bridged

Bridged bindet VMs direkt ins physische LAN ein. Das ist praktisch, wenn andere Geräte im Netzwerk Services erreichen sollen. Es ist aber abhängig von WLAN, DHCP, Firmenrichtlinien und Netzsegmentierung.

Nutze Bridged nur, wenn du die Auswirkungen kennst.

9.4 Remote-Provider-Netz

Bei gemieteten VMs brauchst du ein privates Netzwerk zwischen Nodes. Ohne privates Netzwerk läuft Node-to-Node-Verkehr über öffentliche IPs. Das ist für Labs möglich, aber unsauber und sicherheitlich riskant.

Empfehlung:

  • Public IP nur für SSH, API-Zugriff und optional Ingress.
  • Private IP für kubelet, etcd, CNI und Node-to-Node-Kommunikation.
  • Firewall/Security Group restriktiv konfigurieren.

9.5 Ports

Für kubeadm-ähnliche Cluster brauchst du typischerweise:

Port Richtung Zweck
22/TCP Admin zu VM SSH
6443/TCP Admin, Nodes zu Control Plane Kubernetes API Server
2379-2380/TCP Control Plane untereinander etcd
10250/TCP Control Plane zu Nodes kubelet API
10257/TCP lokal/Control Plane Controller Manager
10259/TCP lokal/Control Plane Scheduler
30000-32767/TCP/UDP Clients zu Nodes NodePort, falls genutzt
CNI-spezifisch Node zu Node Overlay, Routing, Health
80/443/TCP Clients zu Ingress HTTP/HTTPS

Öffne nicht pauschal alle Kubernetes-Ports ins Internet. In Remote-Umgebungen solltest du 6443 nur von deiner Admin-IP oder über VPN/Bastion erlauben.

09 / 40

10. Host-Vorbereitung

10.1 Gemeinsame Host-Prüfung

bash
uname -a
whoami
id
free -h
df -h
ip addr || ifconfig

Prüfe zusätzlich:

  • Hardware-Virtualisierung ist im BIOS/UEFI aktiviert.
  • Bei Windows sind Hyper-V, WSL2, VirtualBox und VMware nicht unkontrolliert im Konflikt.
  • Auf macOS mit Apple Silicon nutzt du ARM-kompatible Images oder Tools.
  • VPN-Netze kollidieren nicht mit Pod-, Service- oder Host-only-CIDRs.
  • Genügend freier Speicher für VM-Disks und Container-Images ist vorhanden.

10.2 Linux-Host

bash
sudo apt update
sudo apt install -y git curl ca-certificates gnupg lsb-release jq yq make openssh-client

Optional für lokale Container-Cluster:

bash
sudo apt install -y docker.io
sudo usermod -aG docker "$USER"
newgrp docker

Alternativ nutzt du Podman, wenn deine Distribution und deine Tool-Kette sauber damit funktionieren.

10.3 macOS-Host

Mit Homebrew:

bash
brew install git curl jq yq kubectl helm kind minikube vagrant

Für VirtualBox oder VMware installierst du den Hypervisor separat. Beachte die CPU-Architektur: Nicht jedes x86-Image läuft auf Apple Silicon.

10.4 Windows-Host

Empfohlen:

  • Windows Terminal,
  • OpenSSH Client,
  • Git,
  • kubectl,
  • WSL2 für Linux-nahe Shells,
  • VirtualBox, VMware Workstation oder Docker Desktop je nach Lab-Ziel.

Achte auf Treiberkonflikte zwischen Hyper-V, VirtualBox, VMware und Docker Desktop. Für einen Workshop solltest du eine Variante standardisieren und nicht mehrere Hypervisoren gleichzeitig voraussetzen.

10 / 40

11. VM-Basisstandard

Jede VM im Workshop sollte reproduzierbar die gleichen Basiseigenschaften haben.

11.1 Hostname und Namensauflösung

bash
sudo hostnamectl set-hostname cp1
hostnamectl

Beispiel für /etc/hosts in lokalen Labs:

text
192.168.56.10 cp1
192.168.56.21 worker1
192.168.56.22 worker2

In Remote-Umgebungen nutzt du private Provider-IP-Adressen.

11.2 Benutzer und SSH

bash
sudo adduser workshop
sudo usermod -aG sudo workshop
sudo mkdir -p /home/workshop/.ssh
sudo cp ~/.ssh/authorized_keys /home/workshop/.ssh/authorized_keys
sudo chown -R workshop:workshop /home/workshop/.ssh
sudo chmod 700 /home/workshop/.ssh
sudo chmod 600 /home/workshop/.ssh/authorized_keys

Empfehlung:

  • SSH nur mit Key.
  • Passwort-Login deaktivieren.
  • Root-Login deaktivieren.
  • SSH-Zugriff bei Remote-VMs auf Admin-IP beschränken.

11.3 Basispakete

bash
sudo apt update
sudo apt install -y \
  apt-transport-https \
  bash-completion \
  ca-certificates \
  conntrack \
  curl \
  dnsutils \
  git \
  gnupg \
  iproute2 \
  iptables \
  jq \
  lsb-release \
  net-tools \
  nfs-common \
  openssl \
  socat \
  software-properties-common \
  tar \
  tcpdump \
  traceroute \
  vim \
  wget

11.4 Kernel-Module und sysctl

bash
cat <<'EOF' | sudo tee /etc/modules-load.d/kubernetes.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system

11.5 Swap

Für klassische kubeadm-Labs deaktivierst du Swap:

bash
sudo swapoff -a
sudo sed -i.bak '/\sswap\s/s/^/#/' /etc/fstab
free -h

11.6 Zeitsynchronisation

bash
timedatectl
sudo timedatectl set-ntp true

Zeitdrift verursacht Zertifikats-, Token- und etcd-Probleme. Prüfe die Uhr besonders bei suspendierten Laptop-VMs.

11 / 40

12. Bereitstellungswege im Überblick

flowchart LR
    Requirement[Anforderung] --> Fast[Sehr schnell]
    Requirement --> Real[Realitätsnah]
    Requirement --> Repeatable[Reproduzierbar]
    Requirement --> Remote[Remote]
    Fast --> Kind[kind]
    Fast --> Minikube[minikube]
    Real --> Kubeadm[kubeadm in VMs]
    Repeatable --> Vagrant[Vagrant]
    Remote --> CloudVM[gemietete VM]
    CloudVM --> Kubeadm
    CloudVM --> K3s[k3s]
    CloudVM --> RKE2[RKE2]
    CloudVM --> K0s[k0s]
Weg Stärke Schwäche Empfehlung
kind Schnell, Multi-Node leicht, ideal für CI Keine echten VMs, Node-Betrieb abstrahiert Standard für schnelle Labs
minikube Einsteigerfreundlich, Add-ons, viele Treiber Profil-/Treiber-Verhalten kann variieren Standard für lokale Lernumgebungen
kubeadm in VMs Realitätsnah, offizieller Bootstrap-Pfad Mehr Aufwand Standard für Admin- und Plattformmodule
Vagrant Reproduzierbare lokale Multi-VMs Hypervisorabhängig Sehr gut für Trainer-Setups
k3s Schnell, leichtgewichtig Andere Defaults als kubeadm Gut für Edge, Homelab, Remote-Single-Node
RKE2 Härtungs- und Enterprise-Fokus Schwerer als k3s Gut für produktionsnahe Rancher/SUSE-Kontexte
k0s Schlank, wenig Abhängigkeiten Weniger verbreitet als kubeadm/k3s Gut für alternative Distributionen
Talos Immutable, API-gesteuerter Node-Betrieb Anderes Betriebsmodell, Lernkurve Gut für moderne Plattformkonzepte
12 / 40

13. Lokale VM mit VirtualBox

VirtualBox eignet sich für Workshops, wenn du eine kostenlose, weit verbreitete lokale VM-Umgebung brauchst.

13.1 Zieltopologie

flowchart TD
    Host[Host] --> Nat[NAT Adapter]
    Host --> HostOnly[Host-only Adapter]
    Nat --> Internet[Internet]
    HostOnly --> CP[cp1 192.168.56.10]
    HostOnly --> W1[worker1 192.168.56.21]
    HostOnly --> W2[worker2 192.168.56.22]
    CP <-->|Cluster-Netz| W1
    CP <-->|Cluster-Netz| W2

13.2 VirtualBox-Netz vorbereiten

Beispiel auf einem Host mit VBoxManage:

bash
VBoxManage list hostonlyifs
VBoxManage hostonlyif create
VBoxManage hostonlyif ipconfig vboxnet0 --ip 192.168.56.1 --netmask 255.255.255.0
VBoxManage list hostonlyifs

Je nach VirtualBox-Version wird das Host-only-Netz über die grafische Oberfläche verwaltet. Wichtig ist nicht das exakte Kommando, sondern ein stabiles isoliertes Netz für die VMs.

13.3 VM-Parameter

VM Adapter 1 Adapter 2 CPU RAM Disk
cp1 NAT Host-only 192.168.56.10 2 bis 4 4 bis 8 GB 40 GB
worker1 NAT Host-only 192.168.56.21 2 bis 4 4 bis 8 GB 40 GB
worker2 NAT Host-only 192.168.56.22 2 bis 4 4 bis 8 GB 40 GB

13.4 Installation

  1. Erstelle jede VM mit Ubuntu Server LTS.
  2. Aktiviere zwei Netzwerkadapter: NAT und Host-only.
  3. Installiere OpenSSH Server.
  4. Setze Hostname und statische IP oder DHCP-Reservation.
  5. Prüfe Internetzugriff und Host-to-VM-SSH.
bash
ssh workshop@192.168.56.10
ssh workshop@192.168.56.21
ssh workshop@192.168.56.22

13.5 VirtualBox-Fallen

Symptom Ursache Lösung
VM hat Internet, aber Nodes sehen sich nicht Nur NAT aktiv Host-only oder Bridged ergänzen
Host kann VM nicht per SSH erreichen Keine Host-only-IP oder Firewall IP prüfen, SSH und Firewall prüfen
Pod-Netz kollidiert Pod-CIDR überschneidet sich mit Host-only/VPN CIDRs neu planen
Cluster wird nach Suspend instabil Zeitdrift NTP prüfen, VM neu starten
Performance schlecht Zu wenig RAM oder Disk-I/O RAM erhöhen, SSD nutzen
13 / 40

14. Lokale VM mit VMware Workstation oder Fusion

VMware Workstation und VMware Fusion eignen sich für stabile lokale VM-Labs, insbesondere wenn VMware im Unternehmen bereits Standard ist.

14.1 Netzvarianten

VMware-Netz Einsatz
NAT Internetzugriff aus der VM
Host-only Isoliertes Lab-Netz
Bridged VM direkt im LAN
Custom VMnet Definierte Lab-Segmente

Empfohlene Lab-Struktur:

  • Ein NAT-Adapter für Paketinstallation.
  • Ein Host-only- oder Custom-VMnet-Adapter für Kubernetes-interne Kommunikation.

14.2 VM-Vorlage

Erstelle eine Basis-VM:

  • Ubuntu Server LTS,
  • OpenSSH Server,
  • Cloud-init optional,
  • alle Updates installiert,
  • Snapshot base-clean,
  • keine kubeadm-Tokens,
  • keine produktiven SSH-Keys außer Lab-Key.

Danach klonst du cp1, worker1 und worker2 aus dieser Vorlage.

14.3 open-vm-tools

bash
sudo apt update
sudo apt install -y open-vm-tools
sudo systemctl enable --now vmtoolsd || true

14.4 VMware-Fallen

Symptom Ursache Lösung
Doppelte MAC oder DHCP-Konflikt VM wurde kopiert statt geklont MAC neu generieren, DHCP-Lease löschen
API-Zertifikat passt nicht IP oder DNS nach kubeadm init geändert Cluster neu erstellen oder Zertifikate bewusst erneuern
SSH auf falsche VM DHCP-Adressen wechselnd Statische IPs oder DHCP-Reservation nutzen
Bridged über WLAN instabil WLAN/Netzwerk blockiert zusätzliche MACs Host-only nutzen
14 / 40

15. VMware vSphere oder ESXi

Für produktionsnahe Labs kannst du vSphere, ESXi oder eine Unternehmensvirtualisierung nutzen. Der Ablauf ist ähnlich, aber die VM-Erstellung erfolgt über Templates, Content Library, Terraform, PowerCLI, govc oder manuelle Klone.

15.1 Zielbild

flowchart TD
    Admin[Admin-Rechner] --> vCenter[vCenter oder ESXi]
    vCenter --> Template[Ubuntu Template]
    Template --> CP1[cp1]
    Template --> CP2[cp2 optional]
    Template --> CP3[cp3 optional]
    Template --> W1[worker1]
    Template --> W2[worker2]
    CP1 --> Cluster[Kubernetes]
    W1 --> Cluster
    W2 --> Cluster

15.2 Template-Standard

Das Template sollte enthalten:

  • aktuelles OS,
  • OpenSSH,
  • open-vm-tools,
  • Cloud-init oder vergleichbarer Initialisierungsmechanismus,
  • eindeutige Machine-ID nach dem Klonen,
  • keine persistenten Host-Keys, wenn du daraus viele VMs erzeugst,
  • keine kubeconfigs oder Tokens.

Nach dem Klonen prüfst du:

bash
hostnamectl
cat /etc/machine-id
ip addr
ip route
systemctl status ssh

Wenn geklonte VMs dieselbe Machine-ID haben, erzeugst du sie neu:

bash
sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo systemd-machine-id-setup
sudo reboot

15.3 Remote-Konsole vs SSH

Nutze die vSphere-Konsole nur für Bootstrap-Fehler. Der normale Betrieb läuft über SSH und GitOps/Automation.

15 / 40

16. Gemietete Remote-VM

Eine gemietete Remote-VM eignet sich, wenn dein lokaler Rechner zu schwach ist oder du Netzwerk- und Betriebsrealität stärker abbilden willst.

16.1 Varianten

Variante Beschreibung Empfehlung
Eine Remote-VM mit kind Docker auf der VM, Kubernetes-Nodes als Container Gut für schnelle Remote-Labs
Eine Remote-VM mit minikube minikube mit Docker- oder SSH-Kontext Gut für Einsteigerlabs
Eine Remote-VM mit k3s Leichter Single-Node-Cluster Sehr gut für öffentliche Demo-VM
Eine Remote-VM mit kubeadm Realistischer, aber schwerer Gut für kubeadm-Bootstrap
Mehrere Remote-VMs mit kubeadm Echtes Multi-Node-Lab Gut für Plattformmodule
Remote-VMs mit RKE2/k0s/Talos Alternative Betriebsmodelle Gut für Vertiefung

16.2 Sicherheitsmodell

Remote bedeutet: Du gehst von Internet-Exposition aus.

Minimalregeln:

  • SSH nur von deiner IP oder VPN.
  • API Server 6443/TCP nur von deiner IP, VPN oder internem Netz.
  • etcd niemals öffentlich öffnen.
  • kubelet 10250/TCP nicht öffentlich öffnen.
  • NodePort-Range nicht pauschal ins Internet öffnen.
  • Keine produktiven Secrets verwenden.
  • Nach dem Workshop Umgebung löschen oder hart deaktivieren.
flowchart TD
    Internet[Internet] -->|blockiert| ETCD[etd/etcd Ports]
    AdminIP[Deine Admin-IP] -->|22/TCP| SSH[SSH]
    AdminIP -->|6443/TCP optional| API[Kubernetes API]
    Internet -->|80/443 wenn Demo nötig| Ingress[Ingress]
    subgraph VM[Remote-VM oder Provider-Netz]
        SSH --> Node[Linux Node]
        API --> Node
        Ingress --> Workload[Demo Workload]
    end

16.3 Remote-Single-Node mit kubeadm

Nutze diese Variante, wenn du kubeadm zeigen willst, aber nur eine VM verfügbar ist.

Empfehlung:

  • VM: 4 vCPU, 8 GB RAM, 60 GB Disk.
  • OS: Ubuntu Server LTS.
  • Firewall: SSH und API nur für Admin-IP, 80/443 optional.
  • API-Endpunkt: DNS-Name oder feste öffentliche IP.
  • CNI: Cilium, Calico oder Flannel.
  • Ingress: NGINX Ingress oder Gateway-Controller.

Wenn du die API per öffentlichem DNS-Namen nutzen willst, füge den Namen bei kubeadm init über apiServer.certSANs hinzu.

16.4 Remote-Multi-Node mit kubeadm

Empfohlenes Netz:

Node Public IP Private IP Rolle
cp1 optional 10.10.0.10 Control Plane
worker1 optional 10.10.0.21 Worker
worker2 optional 10.10.0.22 Worker

Nutze die privaten IPs für --apiserver-advertise-address, kubelet Node IP und Node-to-Node-Kommunikation.

bash
# Auf jedem Node, falls mehrere Interfaces vorhanden sind:
echo 'KUBELET_EXTRA_ARGS=--node-ip=<PRIVATE_IP>' | sudo tee /etc/default/kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet || true

16.5 Remote-Firewall-Beispiel mit ufw

Auf cp1:

bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from <ADMIN_IP>/32 to any port 22 proto tcp
sudo ufw allow from <ADMIN_IP>/32 to any port 6443 proto tcp
sudo ufw allow from 10.10.0.0/24 to any port 6443 proto tcp
sudo ufw allow from 10.10.0.0/24 to any port 2379 proto tcp
sudo ufw allow from 10.10.0.0/24 to any port 2380 proto tcp
sudo ufw allow from 10.10.0.0/24 to any port 10250 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Auf Workern:

bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from <ADMIN_IP>/32 to any port 22 proto tcp
sudo ufw allow from 10.10.0.0/24 to any port 10250 proto tcp
sudo ufw allow from 10.10.0.0/24
sudo ufw enable
sudo ufw status verbose

Passe CNI-spezifische Ports an. Cilium, Calico, Flannel und andere Plugins haben unterschiedliche Anforderungen.

16.6 Cloud-init für Remote-VMs

Beispiel für eine minimale Vorbereitung. Dieses Snippet installiert noch kein Kubernetes, sondern schafft eine saubere Basis.

yaml
#cloud-config
package_update: true
package_upgrade: true
users:
  - name: workshop
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAA...dein-lab-key
packages:
  - ca-certificates
  - curl
  - git
  - gnupg
  - jq
  - openssh-server
  - vim
runcmd:
  - timedatectl set-ntp true
  - swapoff -a
  - sed -i.bak '/\sswap\s/s/^/#/' /etc/fstab
  - modprobe overlay
  - modprobe br_netfilter
  - printf 'overlay\nbr_netfilter\n' > /etc/modules-load.d/kubernetes.conf
  - printf 'net.bridge.bridge-nf-call-iptables = 1\nnet.bridge.bridge-nf-call-ip6tables = 1\nnet.ipv4.ip_forward = 1\n' > /etc/sysctl.d/99-kubernetes.conf
  - sysctl --system
16 / 40

17. Kubernetes mit kubeadm in VMs

kubeadm ist der Standardweg, wenn du Kubernetes-Bootstrap, echte Nodes und Cluster-Lifecycle lernen willst.

17.1 kubeadm-Workflow

sequenceDiagram
    participant Admin as Du
    participant Node as VM/Node
    participant Runtime as containerd
    participant Kubeadm as kubeadm
    participant API as API Server
    participant CNI as CNI
    Admin->>Node: OS vorbereiten
    Admin->>Runtime: containerd installieren und konfigurieren
    Admin->>Node: kubelet, kubeadm, kubectl installieren
    Admin->>Kubeadm: kubeadm init
    Kubeadm->>API: Control Plane erzeugen
    Admin->>CNI: CNI installieren
    Admin->>Kubeadm: kubeadm join auf Workern
    Admin->>API: Cluster validieren

17.2 Gemeinsame Variablen

bash
export K8S_MINOR="v1.36"
export K8S_VERSION="v1.36.1"
export POD_CIDR="10.244.0.0/16"
export SERVICE_CIDR="10.96.0.0/12"
export CONTROL_PLANE_ENDPOINT="192.168.56.10:6443"

Passe die Version vor jedem Workshop an und pinne alle Nodes auf dieselbe Minor-Version.

17.3 containerd installieren

Ubuntu/Debian:

bash
sudo apt update
sudo apt install -y containerd

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

sudo systemctl enable --now containerd
sudo systemctl restart containerd
sudo systemctl status containerd --no-pager

Validierung:

bash
sudo crictl info || true
sudo ctr version

Wenn crictl noch keine Runtime findet, legst du eine Konfiguration an:

bash
cat <<'EOF' | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF

17.4 Kubernetes-Pakete installieren: Ubuntu/Debian

bash
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
sudo mkdir -p /etc/apt/keyrings

curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/ /
EOF

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet

Validierung:

bash
kubeadm version
kubectl version --client
systemctl status kubelet --no-pager || true

Der kubelet-Dienst darf vor kubeadm init oder kubeadm join fehlerhaft wirken. Er wartet noch auf Cluster-Konfiguration.

17.5 Kubernetes-Pakete installieren: RHEL/Rocky/Alma

bash
export K8S_MINOR="v1.36"

cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/rpm/repodata/repomd.xml.key
EOF

sudo dnf install -y kubelet kubeadm kubectl
sudo systemctl enable --now kubelet

Firewall, SELinux und Runtime-Konfiguration hängen stärker von Distribution und Unternehmensstandard ab. Für Workshops dokumentierst du jede Abweichung explizit.

17.6 kubeadm-Konfiguration

Beispiel kubeadm-config.yaml für lokale VMs:

yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 192.168.56.10
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/containerd/containerd.sock
  kubeletExtraArgs:
    - name: node-ip
      value: 192.168.56.10
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.36.1
controlPlaneEndpoint: 192.168.56.10:6443
networking:
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/12
apiServer:
  certSANs:
    - 192.168.56.10
    - cp1
    - kubernetes.local
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd

Für Remote-VMs nutzt du private IPs und ergänzt den öffentlichen DNS-Namen nur dann als SAN, wenn kubectl wirklich von außen direkt auf den API Server zugreifen soll.

17 / 40

18. Single-Node kubeadm

18.1 Wann diese Variante passt

Single-Node kubeadm passt für:

  • Grundlagen,
  • API Server, etcd und kubelet auf einer Maschine,
  • kubeadm-Bootstrap,
  • Ingress- und Storage-Grundlagen,
  • Remote-Demo-VMs,
  • kleine Homelab- oder Trainingsumgebungen.

Nicht passend ist sie für HA, echtes Scheduling über mehrere Nodes oder Ausfallverhalten.

18.2 Initialisierung

Auf der VM:

bash
sudo kubeadm init --config kubeadm-config.yaml

Kubeconfig für den Lab-Benutzer:

bash
mkdir -p "$HOME/.kube"
sudo cp /etc/kubernetes/admin.conf "$HOME/.kube/config"
sudo chown "$(id -u):$(id -g)" "$HOME/.kube/config"

18.3 Control-Plane-Node für Workloads freigeben

Single-Node-Cluster brauchen Workloads auf der Control Plane:

bash
kubectl taint nodes --all node-role.kubernetes.io/control-plane- || true
kubectl get nodes -o wide

18.4 CNI installieren

Ohne CNI bleibt der Node typischerweise NotReady.

Beispiel mit einem CNI-Manifest aus deinem Repository:

bash
kubectl apply -f addons/cni/flannel.yaml
# oder
kubectl apply -f addons/cni/calico.yaml
# oder Cilium über Helm/CLI nach Workshop-Standard

Validierung:

bash
kubectl get nodes -o wide
kubectl get pods -A
kubectl get events -A --sort-by=.lastTimestamp | tail -50

18.5 Test-Deployment

bash
kubectl create namespace demo
kubectl create deployment web -n demo --image=nginx:stable --replicas=2
kubectl expose deployment web -n demo --port=80 --target-port=80
kubectl get deploy,rs,pod,svc -n demo -o wide
kubectl run curl -n demo --rm -it --image=curlimages/curl --restart=Never -- curl -I http://web.demo.svc.cluster.local
18 / 40

19. Multi-Node kubeadm

19.1 Zieltopologie

flowchart LR
    CP[cp1 192.168.56.10] --> API[API Server]
    CP --> ETCD[etcd]
    CP --> CM[Controller Manager]
    CP --> SCHED[Scheduler]
    W1[worker1 192.168.56.21] --> Kubelet1[kubelet]
    W2[worker2 192.168.56.22] --> Kubelet2[kubelet]
    API --> Kubelet1
    API --> Kubelet2
    W1 <-->|CNI Pod-Netz| W2

19.2 Vorbereitung auf allen Nodes

  • Hostname eindeutig setzen.
  • IP-Adressen prüfen.
  • Swap deaktivieren.
  • Kernel-Module und sysctl setzen.
  • containerd installieren.
  • kubelet, kubeadm und kubectl installieren.
  • Zeiten synchronisieren.
  • Firewalls passend konfigurieren.

Validierung auf jedem Node:

bash
hostname -f || hostname
ip addr
ip route
sudo systemctl is-active containerd
kubeadm version

19.3 Control Plane initialisieren

Auf cp1:

bash
sudo kubeadm init --config kubeadm-config.yaml
mkdir -p "$HOME/.kube"
sudo cp /etc/kubernetes/admin.conf "$HOME/.kube/config"
sudo chown "$(id -u):$(id -g)" "$HOME/.kube/config"

Join-Kommando erzeugen:

bash
kubeadm token create --print-join-command

19.4 Worker beitreten lassen

Auf worker1 und worker2:

bash
sudo kubeadm join 192.168.56.10:6443 \
  --token <TOKEN> \
  --discovery-token-ca-cert-hash sha256:<HASH>

Wenn mehrere Interfaces existieren, setze die Node-IP vorher über kubelet:

bash
echo 'KUBELET_EXTRA_ARGS=--node-ip=192.168.56.21' | sudo tee /etc/default/kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet

19.5 Validierung

bash
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl describe node worker1
kubectl get events -A --sort-by=.lastTimestamp | tail -80

Erwartung:

  • alle Nodes sind Ready,
  • CoreDNS läuft,
  • CNI-Pods laufen auf allen Nodes,
  • Workload-Pods werden auf Worker verteilt.
19 / 40

20. Hochverfügbare kubeadm-VM-Cluster

HA lohnt sich, wenn du etcd-Mehrheit, Control-Plane-Endpoints, Zertifikate, Load Balancer und Wartungsabläufe demonstrieren willst.

20.1 Topologie mit gestapeltem etcd

flowchart TD
    LB[Control Plane Endpoint: 192.168.56.100:6443] --> CP1[cp1]
    LB --> CP2[cp2]
    LB --> CP3[cp3]
    CP1 <-->|etcd peer| CP2
    CP2 <-->|etcd peer| CP3
    CP3 <-->|etcd peer| CP1
    CP1 --> W1[worker1]
    CP2 --> W2[worker2]
    CP3 --> W3[worker3 optional]

20.2 Voraussetzungen

  • Drei Control-Plane-VMs für etcd-Mehrheit.
  • Ein stabiler controlPlaneEndpoint vor kubeadm init.
  • Load Balancer oder virtuelle IP vor den API Servern.
  • Zeit synchronisiert.
  • Gleiche Kubernetes-Minor-Version auf allen Nodes.
  • Private Node-to-Node-Kommunikation.

20.3 Load-Balancer-Optionen

Option Einsatz
HAProxy + Keepalived Klassisch, gut verständlich
kube-vip Kubernetes-nahe VIP-Variante
externer Provider-Load-Balancer Gut bei Remote-VMs, wenn verfügbar
DNS-Round-Robin Für echte HA nur eingeschränkt geeignet

20.4 kubeadm-HA-Init

Beispielauszug:

yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.36.1
controlPlaneEndpoint: 192.168.56.100:6443
networking:
  podSubnet: 10.244.0.0/16
apiServer:
  certSANs:
    - 192.168.56.100
    - k8s-api.local

Ersten Control-Plane-Node initialisieren:

bash
sudo kubeadm init --config kubeadm-ha.yaml --upload-certs

Weitere Control-Plane-Nodes beitreten lassen:

bash
sudo kubeadm join 192.168.56.100:6443 \
  --token <TOKEN> \
  --discovery-token-ca-cert-hash sha256:<HASH> \
  --control-plane \
  --certificate-key <CERTIFICATE_KEY>

Worker beitreten lassen:

bash
sudo kubeadm join 192.168.56.100:6443 \
  --token <TOKEN> \
  --discovery-token-ca-cert-hash sha256:<HASH>

20.5 HA-Validierung

bash
kubectl get nodes -o wide
kubectl -n kube-system get pods -l component=etcd -o wide
kubectl -n kube-system get pods -l component=kube-apiserver -o wide
kubectl get --raw=/readyz?verbose

Teste kontrolliert:

bash
sudo systemctl stop kubelet
# auf einem Control-Plane-Node, nicht auf allen

Danach prüfst du, ob der API-Endpunkt weiter antwortet.

20 / 40

21. Kubernetes mit kind

kind steht für Kubernetes in Docker. Es ist kein VM-Cluster, gehört aber in diese Datei, weil es für Workshops oft die pragmatischste Umgebung ist.

21.1 Wann du kind nutzt

Nutze kind, wenn du:

  • schnell starten willst,
  • Multi-Node-Verhalten ohne echte VMs brauchst,
  • Manifest-Tests lokal oder in CI ausführen willst,
  • Kubernetes-Versionen schnell wechseln willst,
  • keine Hypervisor-Abhängigkeit willst.

Nutze kind nicht als Ersatz für:

  • kubeadm-Bootstrap,
  • VM-Netzwerk-Fehlerbilder,
  • echte Node-Wartung,
  • Produktivbetrieb.

21.2 Installation

Linux AMD64:

bash
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
kind version

Mit Homebrew:

bash
brew install kind
kind version

21.3 Single-Node-kind

bash
kind create cluster --name workshop
kubectl cluster-info --context kind-workshop
kubectl get nodes -o wide

Löschen:

bash
kind delete cluster --name workshop

21.4 Multi-Node-kind mit Port-Mapping

Datei kind-workshop.yaml:

yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: workshop
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 80
        hostPort: 8080
        protocol: TCP
      - containerPort: 443
        hostPort: 8443
        protocol: TCP
  - role: worker
  - role: worker

Cluster erstellen:

bash
kind create cluster --config kind-workshop.yaml
kubectl get nodes -o wide

21.5 Image in kind laden

bash
docker build -t workshop/app:dev .
kind load docker-image workshop/app:dev --name workshop
kubectl set image deployment/app app=workshop/app:dev -n demo

21.6 Ingress mit kind

Bei einfachem Port-Mapping kannst du Ingress auf Host-Ports abbilden. Beispiel:

bash
kubectl apply -f addons/ingress-nginx/kind.yaml
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=180s

Danach testest du über localhost:8080 oder localhost:8443, abhängig von deinem Ingress-Setup.

21.7 Lokale Registry für kind

Für Workshops mit eigenen Images ist eine lokale Registry sinnvoll.

bash
docker run -d --restart=always -p 127.0.0.1:5001:5000 --name kind-registry registry:2

Dann konfigurierst du kind so, dass die Nodes die Registry erreichen. Nutze dafür ein Skript oder eine dokumentierte Workshop-Vorlage, damit alle Teilnehmenden dasselbe Verhalten haben.

21.8 kind-Troubleshooting

Symptom Ursache Lösung
kind create cluster schlägt fehl Docker läuft nicht Docker starten, Rechte prüfen
ImagePullBackOff bei lokalem Image Image nicht in kind geladen kind load docker-image ausführen
Ingress nicht erreichbar Port-Mapping fehlt extraPortMappings prüfen
Kein LoadBalancer kind hat keinen Cloud-LB Port-Mapping, Cloud Provider KIND oder MetalLB-Übung nutzen
Cluster alt/kaputt Wiederverwendung eines Profils kind delete cluster und neu erstellen
21 / 40

22. Kubernetes mit minikube

minikube ist eine lokale Kubernetes-Umgebung mit vielen Treibern und Add-ons. Es ist besonders gut für Einsteiger, weil viele Aufgaben durch minikube-Kommandos vereinfacht werden.

22.1 Wann du minikube nutzt

Nutze minikube, wenn du:

  • eine einzelne, komfortable Lernumgebung willst,
  • Ingress, Dashboard, Metrics Server oder Registry per Add-on aktivieren willst,
  • verschiedene Treiber vergleichen willst,
  • eine Remote-VM per SSH als Ziel verwenden willst.

22.2 Installation

Linux AMD64:

bash
curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube version

Homebrew:

bash
brew install minikube
minikube version

22.3 Treiberwahl

Treiber Einsatz
Docker Sehr praktisch auf Linux oder Docker Desktop
VirtualBox Klassische VM-Variante
VMware VMware Workstation/Fusion
KVM2/QEMU Gute Linux-VM-Option
SSH minikube auf einer vorhandenen Remote-VM
none Fortgeschritten, nutzt den vorhandenen Host direkt

22.4 minikube mit Docker-Treiber

bash
minikube start \
  --driver=docker \
  --kubernetes-version=v1.36.1 \
  --cpus=4 \
  --memory=8192 \
  --disk-size=40g

kubectl get nodes -o wide
minikube status

22.5 minikube mit VirtualBox

bash
minikube start \
  --driver=virtualbox \
  --kubernetes-version=v1.36.1 \
  --cpus=4 \
  --memory=8192 \
  --disk-size=40g

Als Default setzen:

bash
minikube config set driver virtualbox

22.6 minikube mit VMware

bash
minikube start \
  --driver=vmware \
  --kubernetes-version=v1.36.1 \
  --cpus=4 \
  --memory=8192 \
  --disk-size=40g

Als Default setzen:

bash
minikube config set driver vmware

22.7 minikube auf Remote-VM per SSH

Diese Variante ist nützlich, wenn du lokal keine Virtualisierung nutzen kannst.

Auf deinem Rechner:

bash
minikube start \
  --driver=ssh \
  --ssh-ip-address=<REMOTE_VM_IP> \
  --kubernetes-version=v1.36.1

Vorbedingungen auf der Remote-VM:

  • Linux mit SSH-Zugang,
  • ausreichend CPU, RAM und Disk,
  • Paketinstallation möglich,
  • keine kollidierenden Kubernetes- oder Docker-Altinstallationen,
  • Firewall erlaubt SSH von deinem Rechner.

22.8 Multi-Node-minikube

bash
minikube start \
  --driver=docker \
  --nodes=3 \
  --cpus=4 \
  --memory=8192 \
  --kubernetes-version=v1.36.1

kubectl get nodes -o wide

22.9 Add-ons

bash
minikube addons list
minikube addons enable metrics-server
minikube addons enable ingress
minikube addons enable dashboard

Dashboard:

bash
minikube dashboard

Ingress testen:

bash
kubectl get pods -n ingress-nginx
kubectl get ingress -A

22.10 LoadBalancer mit minikube tunnel

bash
minikube tunnel

Dieses Kommando braucht je nach System erhöhte Rechte und bleibt im Vordergrund. Für Workshops solltest du erklären, dass der Tunnel Teil der Umgebung ist und beendet werden muss.

22.11 Profile

bash
minikube profile list
minikube start -p workshop-a --driver=docker
minikube start -p workshop-b --driver=docker
minikube profile workshop-a
kubectl config current-context

Profile verhindern, dass Teilnehmende alte Cluster unbeabsichtigt wiederverwenden.

22.12 minikube-Rückbau

bash
minikube stop
minikube delete
minikube delete --all --purge
22 / 40

23. Vagrant für lokale VM-Provisionierung

Vagrant ist eine Automatisierungsschicht für reproduzierbare VMs. Für Workshops ist das nützlich, weil du mit einem Vagrantfile mehrere VMs definiert erzeugen kannst.

23.1 Wann Vagrant passt

Vagrant passt, wenn du:

  • lokale Multi-Node-VMs reproduzierbar erzeugen willst,
  • VirtualBox oder VMware als Provider verwenden willst,
  • ein einheitliches Trainer-Setup brauchst,
  • Provisionierungsskripte versionieren willst,
  • VMs nach dem Lab schnell zerstören willst.

Vagrant ist weniger geeignet für beliebige gemietete Remote-VMs. Dafür sind Cloud-init, Terraform, Ansible oder Provider-spezifische APIs meist passender.

23.2 Installation

bash
vagrant version
vagrant plugin list

VirtualBox wird von Vagrant direkt unterstützt. Für VMware brauchst du den VMware-Provider und die passende VMware-Utility.

VMware-Provider installieren:

bash
vagrant plugin install vagrant-vmware-desktop
vagrant plugin list

23.3 Projektstruktur

text
kubernetes-vagrant-lab/
├── Vagrantfile
├── scripts/
│   └── vagrant/
│       ├── bootstrap-common.sh
│       ├── init-control-plane.sh
│       └── join-worker.sh
└── manifests/
    └── cni/
        └── flannel.yaml

23.4 Vagrantfile für 1 Control Plane und 2 Worker

ruby
# Kubernetes VM lab: one control plane and two workers.
# Provider: VirtualBox by default, VMware via --provider=vmware_desktop.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/noble64"
  config.vm.synced_folder ".", "/vagrant", type: "virtualbox"

  nodes = [
    { name: "cp1",     ip: "192.168.56.10", role: "control-plane", memory: 4096, cpus: 2 },
    { name: "worker1", ip: "192.168.56.21", role: "worker",        memory: 4096, cpus: 2 },
    { name: "worker2", ip: "192.168.56.22", role: "worker",        memory: 4096, cpus: 2 }
  ]

  nodes.each do |node|
    config.vm.define node[:name] do |machine|
      machine.vm.hostname = node[:name]
      machine.vm.network "private_network", ip: node[:ip]

      machine.vm.provider "virtualbox" do |vb|
        vb.name = "k8s-#{node[:name]}"
        vb.memory = node[:memory]
        vb.cpus = node[:cpus]
      end

      machine.vm.provider "vmware_desktop" do |vmw|
        vmw.vmx["displayName"] = "k8s-#{node[:name]}"
        vmw.vmx["memsize"] = node[:memory].to_s
        vmw.vmx["numvcpus"] = node[:cpus].to_s
      end

      machine.vm.provision "shell", path: "scripts/vagrant/bootstrap-common.sh", args: [node[:ip]]

      if node[:role] == "control-plane"
        machine.vm.provision "shell", path: "scripts/vagrant/init-control-plane.sh", args: [node[:ip]]
      else
        machine.vm.provision "shell", path: "scripts/vagrant/join-worker.sh", args: [node[:ip]]
      end
    end
  end
end

23.5 Common-Provisioning-Skript

Datei scripts/vagrant/bootstrap-common.sh:

bash
#!/usr/bin/env bash
set -euo pipefail

NODE_IP="${1:?node ip missing}"
K8S_MINOR="v1.36"

export DEBIAN_FRONTEND=noninteractive

sudo hostnamectl set-hostname "$(hostname -s)"

cat <<EOF | sudo tee -a /etc/hosts
192.168.56.10 cp1
192.168.56.21 worker1
192.168.56.22 worker2
EOF

sudo swapoff -a
sudo sed -i.bak '/\sswap\s/s/^/#/' /etc/fstab

cat <<'EOF' | sudo tee /etc/modules-load.d/kubernetes.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter

cat <<'EOF' | sudo tee /etc/sysctl.d/99-kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg jq containerd

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl enable --now containerd
sudo systemctl restart containerd

sudo mkdir -p /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/ /
EOF

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

echo "KUBELET_EXTRA_ARGS=--node-ip=${NODE_IP}" | sudo tee /etc/default/kubelet
sudo systemctl enable kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet || true

23.6 Control-Plane-Skript

Datei scripts/vagrant/init-control-plane.sh:

bash
#!/usr/bin/env bash
set -euo pipefail

NODE_IP="${1:?node ip missing}"
K8S_VERSION="v1.36.1"

if [ -f /etc/kubernetes/admin.conf ]; then
  echo "Control plane already initialized."
  exit 0
fi

cat <<EOF >/tmp/kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: ${NODE_IP}
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/containerd/containerd.sock
  kubeletExtraArgs:
    - name: node-ip
      value: ${NODE_IP}
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: ${K8S_VERSION}
controlPlaneEndpoint: ${NODE_IP}:6443
networking:
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/12
apiServer:
  certSANs:
    - ${NODE_IP}
    - cp1
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF

sudo kubeadm init --config /tmp/kubeadm-config.yaml

mkdir -p /home/vagrant/.kube
sudo cp /etc/kubernetes/admin.conf /home/vagrant/.kube/config
sudo chown -R vagrant:vagrant /home/vagrant/.kube

# Installiere im Workshop hier dein standardisiertes CNI-Manifest.
# Beispiel:
# sudo -u vagrant kubectl apply -f /vagrant/manifests/cni/flannel.yaml

sudo kubeadm token create --print-join-command | sudo tee /vagrant/.join-command >/dev/null
sudo chmod 0644 /vagrant/.join-command

23.7 Worker-Join-Skript

Datei scripts/vagrant/join-worker.sh:

bash
#!/usr/bin/env bash
set -euo pipefail

if [ -f /etc/kubernetes/kubelet.conf ]; then
  echo "Worker already joined."
  exit 0
fi

for i in $(seq 1 60); do
  if [ -f /vagrant/.join-command ]; then
    break
  fi
  echo "Waiting for join command..."
  sleep 5
done

if [ ! -f /vagrant/.join-command ]; then
  echo "Join command missing."
  exit 1
fi

JOIN_COMMAND="$(cat /vagrant/.join-command)"
sudo ${JOIN_COMMAND}

23.8 Vagrant-Kommandos

VirtualBox:

bash
vagrant up --provider=virtualbox
vagrant status
vagrant ssh cp1

VMware:

bash
vagrant up --provider=vmware_desktop
vagrant status
vagrant ssh cp1

Provisionierung erneut ausführen:

bash
vagrant provision cp1
vagrant provision worker1
vagrant provision worker2

Snapshot:

bash
vagrant snapshot save cp1 before-cni
vagrant snapshot list cp1
vagrant snapshot restore cp1 before-cni

Rückbau:

bash
vagrant destroy -f
rm -f .join-command

23.9 Vagrant-Fallen

Symptom Ursache Lösung
Worker findet Join-Kommando nicht Control Plane noch nicht fertig Provisioning-Reihenfolge prüfen
Alle Nodes haben dieselbe IP Provider-Netz nicht korrekt Host-only-Netz und Adapter prüfen
CNI fehlt Skript enthält nur kubeadm CNI-Manifest ergänzen
VMware-Provider startet nicht Plugin oder Utility fehlt Provider und Utility installieren
Synced Folder fehlt Provider-spezifischer Folder-Typ Vagrantfile anpassen
Re-Provisioning bricht bei kubeadm ab Nicht idempotent Prüfen, ob /etc/kubernetes/admin.conf existiert
23 / 40

24. k3s in VMs

k3s ist eine leichte Kubernetes-Distribution. Sie eignet sich für Edge, Homelab, schnelle Remote-VMs und Workshops, wenn kubeadm nicht das Lernziel ist.

24.1 Single-Node-k3s

bash
curl -sfL https://get.k3s.io | sh -
sudo kubectl get nodes -o wide
sudo kubectl get pods -A

Kubeconfig für deinen Benutzer:

bash
mkdir -p "$HOME/.kube"
sudo cp /etc/rancher/k3s/k3s.yaml "$HOME/.kube/config"
sudo chown "$(id -u):$(id -g)" "$HOME/.kube/config"
sed -i "s/127.0.0.1/<NODE_IP_OR_DNS>/" "$HOME/.kube/config"

24.2 k3s ohne mitgelieferte Komponenten

Wenn du eigene Add-ons demonstrieren willst:

bash
curl -sfL https://get.k3s.io | sh -s - \
  --disable traefik \
  --disable servicelb \
  --disable local-storage

Danach installierst du Ingress, LoadBalancer und Storage nach Workshop-Standard.

24.3 k3s Multi-Node

Server:

bash
curl -sfL https://get.k3s.io | sh -s - server \
  --node-ip 10.10.0.10 \
  --tls-san k8s.example.net

sudo cat /var/lib/rancher/k3s/server/node-token

Agent:

bash
curl -sfL https://get.k3s.io | K3S_URL=https://10.10.0.10:6443 K3S_TOKEN=<TOKEN> sh -s - agent \
  --node-ip 10.10.0.21
24 / 40

25. RKE2 in VMs

RKE2 ist eine Kubernetes-Distribution mit stärkerem Fokus auf sichere Defaults und Enterprise-Betrieb. Sie eignet sich gut, wenn Rancher/SUSE-Kontexte oder härtungsnahe Plattformen Thema sind.

25.1 Single-Server-RKE2

bash
curl -sfL https://get.rke2.io | sudo sh -
sudo systemctl enable rke2-server.service
sudo systemctl start rke2-server.service
sudo journalctl -u rke2-server -f

Kubeconfig:

bash
mkdir -p "$HOME/.kube"
sudo cp /etc/rancher/rke2/rke2.yaml "$HOME/.kube/config"
sudo chown "$(id -u):$(id -g)" "$HOME/.kube/config"
export PATH="$PATH:/var/lib/rancher/rke2/bin"
kubectl get nodes -o wide

25.2 RKE2-HA-Grundmodell

RKE2-HA braucht eine feste Registrierungsadresse vor den Server-Nodes und eine ungerade Anzahl Server-Nodes.

flowchart TD
    Reg[Registrierungsadresse oder Load Balancer] --> S1[server1]
    Reg --> S2[server2]
    Reg --> S3[server3]
    S1 <-->|etcd| S2
    S2 <-->|etcd| S3
    S3 <-->|etcd| S1
    Reg --> A1[agent1]
    Reg --> A2[agent2]
25 / 40

26. k0s in VMs

k0s ist eine schlanke Kubernetes-Distribution mit Controller- und Worker-Rollen. Sie eignet sich als alternative Workshop-Variante, wenn du verschiedene Distributionen vergleichen willst.

26.1 Single-Node-k0s

bash
curl --proto '=https' --tlsv1.2 -sSf https://get.k0s.sh | sudo sh
sudo k0s install controller --single
sudo k0s start
sudo k0s status
sudo k0s kubectl get nodes -o wide

Kubeconfig:

bash
mkdir -p "$HOME/.kube"
sudo k0s kubeconfig admin > "$HOME/.kube/config"
chmod 600 "$HOME/.kube/config"
kubectl get nodes -o wide

26.2 Worker-Join

Auf dem Controller:

bash
sudo k0s token create --role=worker > worker-token

Auf dem Worker:

bash
sudo k0s install worker --token-file worker-token
sudo k0s start
26 / 40

27. Talos Linux in VMs

Talos Linux ist ein spezielles, unveränderliches Betriebssystem für Kubernetes. Du verwaltest Nodes nicht klassisch per SSH, sondern über talosctl und deklarative Maschinenkonfiguration.

27.1 Wann Talos passt

Talos passt, wenn du zeigen willst:

  • Kubernetes-Hosts ohne SSH-Betriebsmodell,
  • API-gesteuerte Node-Konfiguration,
  • immutable Infrastruktur,
  • klare Trennung zwischen OS-Konfiguration und Kubernetes-Workloads.

Für Einsteiger in Kubernetes ist Talos nicht der erste Weg, weil du gleichzeitig Kubernetes und ein neues OS-Betriebsmodell lernst.

27.2 Talos in VirtualBox oder VMware

Ablauf:

  1. Talos-Image oder ISO passend zur Plattform laden.
  2. VM mit ausreichend CPU, RAM und Disk erstellen.
  3. Netz stabil konfigurieren.
  4. Maschinenkonfiguration mit talosctl gen config erzeugen.
  5. Konfiguration auf Control Plane und Worker anwenden.
  6. Cluster bootstrappen.
  7. kubeconfig abrufen.

Schematisch:

bash
talosctl gen config workshop https://192.168.56.10:6443

talosctl apply-config --insecure --nodes 192.168.56.10 --file controlplane.yaml
talosctl apply-config --insecure --nodes 192.168.56.21 --file worker.yaml

talosctl bootstrap --nodes 192.168.56.10 --endpoints 192.168.56.10
talosctl kubeconfig --nodes 192.168.56.10 --endpoints 192.168.56.10
kubectl get nodes -o wide

Prüfe die offizielle Talos-Plattformdokumentation für VirtualBox, VMware und vSphere, weil Image-Typen, Guestinfo, Disk-Optionen und Plattformdetails unterschiedlich sind.

27 / 40

28. Add-ons nach der Cluster-Erstellung

Ein Kubernetes-Cluster ist nach dem Bootstrap noch keine vollständige Plattform. Für Workshops installierst du Add-ons bewusst und in fester Reihenfolge.

flowchart TD
    Bootstrap[kubeadm, kind, minikube oder Distribution] --> CNI[CNI]
    CNI --> DNS[CoreDNS validieren]
    DNS --> Metrics[Metrics Server]
    Metrics --> Ingress[Ingress oder Gateway]
    Ingress --> Certs[cert-manager optional]
    Certs --> Storage[StorageClass]
    Storage --> Observability[Monitoring, Logging]
    Observability --> GitOps[Argo CD oder Flux optional]

28.1 Empfohlene Reihenfolge

  1. CNI installieren.
  2. Nodes und CoreDNS validieren.
  3. Metrics Server installieren.
  4. StorageClass bereitstellen.
  5. Ingress Controller oder Gateway Controller installieren.
  6. Zertifikatsmanagement installieren, falls TLS geübt wird.
  7. Observability installieren.
  8. GitOps optional ergänzen.
  9. Security-Policies aktivieren.

28.2 Add-on-Matrix

Add-on Zweck Für welche Umgebung
Cilium CNI, NetworkPolicy, eBPF, Observability kubeadm, kind, k3s, RKE2, k0s
Calico CNI und NetworkPolicy kubeadm, VM-Cluster
Flannel Einfaches Pod-Netz Grundlagen und einfache Labs
Metrics Server kubectl top, HPA-Metriken fast alle Labs
MetalLB LoadBalancer in VM-/Bare-Metal-Netzen lokale und Remote-VM-Cluster
Ingress NGINX HTTP/HTTPS-Routing Labs mit Webzugriff
Gateway API Controller Moderne Routing-API Advanced-Labs
cert-manager Zertifikate TLS-Labs
Local Path Provisioner einfache lokale StorageClass Single-Node und Labs
Longhorn replizierter Storage Multi-Node-VM-Labs
28 / 40

29. Storage in VM-Umgebungen

Storage in VMs ist oft trügerisch: Eine virtuelle Disk ist nicht automatisch hochverfügbarer Storage. Unterscheide zwischen Lab-Persistenz und produktionsnahem Storage.

29.1 Optionen

Option Einsatz Bemerkung
emptyDir temporäre Daten Wird mit Pod gelöscht
hostPath lokale Lab-Übungen Nicht portabel, nicht für Produktion
Local PV lokale persistente Disk Node-gebunden
NFS CSI einfache Shared-Storage-Übung NFS-Server nötig
Longhorn replizierter VM-Storage Gut für Homelab/Workshop
Rook/Ceph komplexer verteilter Storage Für Advanced-Labs, ressourcenintensiv
Provider-Block-Storage Remote-VMs beim Provider CSI/Cloud-Integration abhängig

29.2 Local Path Provisioner für Labs

Wenn dein Cluster keine StorageClass hat, installiere eine einfache lokale StorageClass nur für Labs.

Validierung:

bash
kubectl get storageclass
kubectl create namespace storage-test
cat <<'EOF' | kubectl apply -n storage-test -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
EOF
kubectl get pvc -n storage-test
kubectl describe pvc data -n storage-test

29.3 Snapshot-Warnung

VM-Snapshots ersetzen kein Kubernetes-Backup. Ein Snapshot von laufenden Control-Plane-VMs kann etcd inkonsistent machen, wenn er unkoordiniert erstellt wird. Für etcd nutzt du etcdctl snapshot save oder distributionsspezifische Backup-Mechanismen.

29 / 40

30. Ingress, Gateway und Load Balancer

30.1 NodePort

NodePort ist einfach, aber für Workshop-Zugriff oft unbequem.

bash
kubectl expose deployment web -n demo --type=NodePort --port=80
kubectl get svc -n demo web -o wide

30.2 Ingress

Ingress braucht immer einen Ingress Controller.

bash
kubectl get ingressclass
kubectl get pods -A | grep -i ingress || true

Beispielressource:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: demo
spec:
  ingressClassName: nginx
  rules:
    - host: web.localtest.me
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

30.3 Gateway API

Gateway API eignet sich für Advanced-Labs, weil sie Rollen und Verantwortlichkeiten sauberer trennt.

flowchart TD
    Platform[Plattformteam] --> GatewayClass[GatewayClass]
    Platform --> Gateway[Gateway]
    AppTeam[App-Team] --> HTTPRoute[HTTPRoute]
    HTTPRoute --> Service[Service]
    Service --> Pods[Pods]
    Gateway --> HTTPRoute

30.4 MetalLB in VM-Netzen

MetalLB gibt Services vom Typ LoadBalancer eine IP aus einem von dir definierten Pool. In lokalen Host-only-Netzen wählst du einen freien Bereich, zum Beispiel 192.168.56.200-192.168.56.220.

Beispielkonzept:

yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: lab-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.56.200-192.168.56.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: lab-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - lab-pool

Prüfe vorher, ob diese Adressen nicht durch DHCP vergeben werden.

30 / 40

31. Security-Baseline

31.1 VM-Security

  • Nutze eigene Lab-SSH-Keys.
  • Deaktiviere Passwort-Login, wenn möglich.
  • Öffne Remote-Firewalls minimal.
  • Trenne Lab-Netze von produktiven Netzen.
  • Speichere keine produktiven Secrets in VMs.
  • Lösche kubeadm-Tokens nach Gebrauch oder lasse sie kurzlebig.
  • Entferne kubeconfigs nach Rückbau.

31.2 Kubernetes-Security

Nach Cluster-Aufbau:

bash
kubectl get clusterrolebinding
kubectl get serviceaccount -A
kubectl auth can-i '*' '*' --all-namespaces
kubectl get ns --show-labels

Namespace-Baseline:

bash
kubectl create namespace demo
kubectl label namespace demo pod-security.kubernetes.io/enforce=baseline
kubectl label namespace demo pod-security.kubernetes.io/audit=restricted
kubectl label namespace demo pod-security.kubernetes.io/warn=restricted

31.3 Secret-Regeln

  • Keine echten Passwörter in Git.
  • Keine produktiven Registry-Tokens.
  • Keine Cloud-Credentials in Workshop-Namespaces.
  • Secrets nach Übungen löschen.
  • Base64 nicht als Verschlüsselung behandeln.
bash
kubectl get secrets -A
kubectl delete namespace demo
31 / 40

32. Observability und Validierung

32.1 Basisvalidierung

bash
kubectl version
kubectl cluster-info
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl get events -A --sort-by=.lastTimestamp | tail -100
kubectl get componentstatuses 2>/dev/null || true
kubectl get --raw='/readyz?verbose'

32.2 Node-Sicht

bash
systemctl status kubelet --no-pager
systemctl status containerd --no-pager
journalctl -u kubelet -n 200 --no-pager
journalctl -u containerd -n 100 --no-pager
sudo crictl ps -a
sudo crictl images

32.3 Netzwerk-Sicht

bash
ip addr
ip route
ss -tulpen
sudo iptables-save | head -80
kubectl get svc,endpoints,endpointslices -A
kubectl run net-debug --rm -it --image=busybox:1.36 --restart=Never -- sh

Im Debug-Pod:

sh
nslookup kubernetes.default.svc.cluster.local
wget -qO- http://kubernetes.default.svc.cluster.local

32.4 Metrics Server

bash
kubectl top nodes
kubectl top pods -A

Wenn kubectl top nicht funktioniert, prüfst du zuerst Metrics Server, kubelet-Zertifikate und APIService-Status:

bash
kubectl get apiservice | grep metrics
kubectl -n kube-system logs deployment/metrics-server
32 / 40

33. Upgrade, Wartung und Snapshots

33.1 Grundsätze

  • Upgrade nicht während eines Workshops ohne Zeitfenster.
  • Minor-Versionen nicht wild mischen.
  • Erst Control Plane, dann Worker.
  • Vorher etcd-Backup bei kubeadm-Control-Plane.
  • Nachher Nodes, Pods, CoreDNS, CNI und Add-ons prüfen.

33.2 kubeadm-Upgrade grob

Auf Control Plane:

bash
sudo apt-mark unhold kubeadm
sudo apt-get update
sudo apt-get install -y kubeadm=<ZIELVERSION>
sudo apt-mark hold kubeadm
kubeadm version
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply <ZIELVERSION>

Danach kubelet/kubectl:

bash
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=<ZIELVERSION> kubectl=<ZIELVERSION>
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet

33.3 VM-Snapshots

Snapshots sind nützlich für Labs:

  • Snapshot vor kubeadm init,
  • Snapshot nach OS-Basis,
  • Snapshot nach erfolgreichem Cluster-Bootstrap,
  • Snapshot vor riskanten Übungen.

Aber:

  • Keine unkoordinierten Snapshots aller etcd-Member als Backup verkaufen.
  • Nach Snapshot-Restore Zeit und Zertifikate prüfen.
  • Bei Multi-Node-Clustern konsistente Snapshot-Zeitpunkte beachten.
33 / 40

34. Rückbau

34.1 kubeadm-Cluster zurückbauen

Auf Workern:

bash
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d /var/lib/cni /var/lib/kubelet /etc/kubernetes
sudo systemctl restart containerd

Auf Control Plane:

bash
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d /var/lib/cni /var/lib/kubelet /etc/kubernetes
rm -rf "$HOME/.kube"
sudo systemctl restart containerd

iptables nur kontrolliert bereinigen, wenn die VM vollständig für das Lab bestimmt ist:

bash
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -X

34.2 kind

bash
kind get clusters
kind delete cluster --name workshop
docker ps -a | grep kind || true

34.3 minikube

bash
minikube profile list
minikube stop
minikube delete --all --purge

34.4 Vagrant

bash
vagrant status
vagrant destroy -f
rm -f .join-command

34.5 Remote-VMs

  • kubeconfig lokal löschen.
  • SSH-Key aus authorized_keys entfernen, wenn VM bleibt.
  • Provider-Firewall zurücksetzen.
  • VM löschen, wenn sie nur für den Workshop existierte.
  • DNS-Einträge entfernen.
  • Volumes, Snapshots und Floating IPs löschen, damit keine Kosten weiterlaufen.
34 / 40

35. Troubleshooting-Katalog

35.1 kubeadm preflight schlägt fehl

Diagnose:

bash
sudo kubeadm init --config kubeadm-config.yaml --v=5
sudo journalctl -u kubelet -n 200 --no-pager
sudo crictl info

Häufige Ursachen:

  • Swap aktiv,
  • containerd läuft nicht,
  • cgroup-Treiber falsch,
  • Ports bereits belegt,
  • alte Kubernetes-Dateien vorhanden,
  • Hostname oder IP nicht eindeutig,
  • Firewall blockiert API oder kubelet.

35.2 Node bleibt NotReady

bash
kubectl describe node <node>
kubectl get pods -n kube-system -o wide
journalctl -u kubelet -n 200 --no-pager

Typische Ursachen:

  • CNI fehlt,
  • CNI-Pods crashen,
  • Node-IP falsch,
  • Pod-CIDR kollidiert,
  • kubelet kann Runtime nicht erreichen.

35.3 CoreDNS bleibt Pending oder CrashLoopBackOff

bash
kubectl -n kube-system describe deploy coredns
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl describe node

Ursachen:

  • Node nicht Ready,
  • CNI fehlt,
  • Ressourcen zu knapp,
  • Control-Plane-Taint in Single-Node nicht entfernt.

35.4 Worker kann nicht joinen

bash
sudo kubeadm join ... --v=5
nc -vz 192.168.56.10 6443
curl -k https://192.168.56.10:6443/readyz

Ursachen:

  • Token abgelaufen,
  • CA-Hash falsch,
  • API-Endpunkt nicht erreichbar,
  • Firewall blockiert,
  • falsches Interface,
  • Zertifikat-SAN passt nicht.

35.5 kubectl meldet Zertifikatsfehler

bash
kubectl config view --minify
openssl s_client -connect <api-endpoint>:6443 -showcerts </dev/null

Ursachen:

  • API wird über DNS/IP angesprochen, die nicht im Zertifikat steht,
  • kubeconfig zeigt auf alte IP,
  • Cluster wurde aus Snapshot mit anderer Netzkonfiguration wiederhergestellt.

35.6 Pods hängen in ImagePullBackOff

bash
kubectl describe pod <pod> -n <namespace>
kubectl get events -n <namespace> --sort-by=.lastTimestamp
sudo crictl pull <image>

Ursachen:

  • Image-Name falsch,
  • Registry nicht erreichbar,
  • Authentifizierung fehlt,
  • lokale Images nicht in kind/minikube geladen,
  • Remote-VM hat keinen Egress.

35.7 Service hat keine Endpoints

bash
kubectl get svc,endpoints,endpointslices -n demo
kubectl get pods -n demo --show-labels
kubectl describe svc <service> -n demo

Ursachen:

  • Selector passt nicht zu Pod-Labels,
  • Pods nicht Ready,
  • falscher Namespace,
  • Deployment nicht ausgerollt.

35.8 Ingress ist nicht erreichbar

bash
kubectl get ingressclass
kubectl get ingress -A
kubectl get pods -A | grep -i ingress
kubectl describe ingress <name> -n <namespace>

Ursachen:

  • Ingress Controller fehlt,
  • falsche ingressClassName,
  • DNS/Host-Header fehlt,
  • Port-Mapping bei kind fehlt,
  • minikube tunnel läuft nicht,
  • Firewall blockiert 80/443.

35.9 Vagrant-Provisionierung bricht ab

bash
vagrant status
vagrant ssh cp1
vagrant provision cp1
vagrant provision worker1

Ursachen:

  • Synced Folder nicht gemountet,
  • Skripte nicht ausführbar,
  • Provider-Plugin fehlt,
  • VM hat alte kubeadm-Zustände,
  • Join-Datei wurde nicht erzeugt,
  • VM-Host-only-Netz fehlt.

35.10 Remote-VM ist nach Firewall-Regel nicht mehr erreichbar

Prävention:

  • Öffne neue SSH-Session vor Firewall-Änderung.
  • Nutze Provider-Konsole als Notfallzugang.
  • Erlaube deine Admin-IP explizit.
  • Aktiviere Firewall erst nach Prüfung.

Diagnose über Provider-Konsole:

bash
sudo ufw status verbose
sudo iptables -S
sudo ss -tulpen | grep ssh
35 / 40

36. Workshop-Labs

Lab 1: kind in 10 Minuten

Ziel: Du erzeugst einen lokalen Multi-Node-Cluster ohne VM.

Aufgaben:

  1. kind installieren.
  2. Multi-Node-Konfiguration schreiben.
  3. Cluster erstellen.
  4. Deployment ausrollen.
  5. Service testen.
  6. Cluster löschen.

Abnahme:

bash
kind get clusters
kubectl get nodes -o wide
kubectl get pods -A

Lab 2: minikube mit Ingress

Ziel: Du startest minikube, aktivierst Ingress und erreichst eine App über Hostnamen.

Aufgaben:

  1. minikube starten.
  2. Ingress-Add-on aktivieren.
  3. Demo-App deployen.
  4. Ingress erstellen.
  5. Hostauflösung testen.

Abnahme:

bash
kubectl get ingress -A
curl -H 'Host: web.localtest.me' http://$(minikube ip)

Lab 3: kubeadm Single-Node in VirtualBox

Ziel: Du installierst Kubernetes manuell in einer VM.

Aufgaben:

  1. Ubuntu-VM erstellen.
  2. containerd installieren.
  3. kubeadm/kubelet/kubectl installieren.
  4. kubeadm init ausführen.
  5. CNI installieren.
  6. Control-Plane-Taint entfernen.
  7. Demo-App testen.

Abnahme:

bash
kubectl get nodes -o wide
kubectl get pods -A
kubectl run test --rm -it --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default

Lab 4: kubeadm Multi-Node mit Vagrant

Ziel: Du erzeugst drei VMs reproduzierbar und baust einen Cluster.

Aufgaben:

  1. Vagrantfile erstellen.
  2. Common-Provisioning ausführen.
  3. Control Plane initialisieren.
  4. Worker joinen.
  5. CNI installieren.
  6. Scheduling testen.

Abnahme:

bash
vagrant status
kubectl get nodes -o wide
kubectl create deployment web -n demo --image=nginx:stable --replicas=6
kubectl get pods -n demo -o wide

Lab 5: Remote-VM absichern

Ziel: Du betreibst einen Remote-Single-Node-Cluster mit minimaler Firewall.

Aufgaben:

  1. Remote-VM erzeugen.
  2. SSH-Key-Zugang einrichten.
  3. Firewall nur für Admin-IP öffnen.
  4. Kubernetes installieren.
  5. API-Zugriff validieren.
  6. Ingress optional öffnen.
  7. Rückbau durchführen.

Abnahme:

bash
ssh workshop@<remote-ip> hostname
kubectl get nodes -o wide
sudo ufw status verbose

Lab 6: Cluster-Fehlerbilder

Ziel: Du erkennst typische Fehler und behebst sie methodisch.

Fehlerbilder:

  • CNI fehlt,
  • falscher Service-Selector,
  • ImagePullBackOff,
  • Node NotReady,
  • Ingress ohne Controller,
  • kubeconfig zeigt auf falsche IP.

Abnahme:

Du dokumentierst pro Fehler:

  • Symptom,
  • Diagnosekommando,
  • Ursache,
  • Korrektur,
  • Validierung.
36 / 40

37. Review-Checkliste

37.1 Vor Provisionierung

  • Zielvariante ist ausgewählt.
  • Host-Ressourcen reichen aus.
  • CIDRs kollidieren nicht.
  • IP-Plan ist dokumentiert.
  • SSH-Zugang ist definiert.
  • Kubernetes-Version ist gepinnt.
  • CNI ist ausgewählt.
  • Rückbau ist bekannt.
  • Keine produktiven Secrets werden verwendet.

37.2 Nach OS-Basis

  • Hostname eindeutig.
  • IP-Adresse korrekt.
  • DNS oder /etc/hosts korrekt.
  • Zeit synchronisiert.
  • Swap deaktiviert.
  • Kernel-Module geladen.
  • sysctl gesetzt.
  • containerd läuft.
  • kubelet/kubeadm/kubectl installiert.

37.3 Nach Cluster-Bootstrap

  • kubectl get nodes zeigt erwartete Nodes.
  • Nodes sind Ready.
  • CNI-Pods laufen.
  • CoreDNS läuft.
  • Demo-Pod startet.
  • Service Discovery funktioniert.
  • Events zeigen keine kritischen Dauerfehler.
  • kubeconfig zeigt auf den richtigen API-Endpunkt.

37.4 Nach Add-ons

  • Metrics Server funktioniert.
  • StorageClass existiert, falls benötigt.
  • Ingress Controller oder Gateway Controller läuft.
  • LoadBalancer-Konzept ist getestet.
  • NetworkPolicy-Verhalten ist dokumentiert.
  • Security-Baseline ist angewendet.

37.5 Vor Übergabe an Teilnehmende

  • Reset-Anleitung getestet.
  • Snapshots sauber benannt.
  • Download-Links und Images erreichbar.
  • Provider-Kosten beachtet.
  • Troubleshooting-Kommandos bereit.
  • Lösungsschritte dokumentiert.
37 / 40

38. Anti-Patterns

Anti-Pattern Warum schlecht Besser
kubectl direkt gegen eine öffentliche API ohne IP-Restriktion API-Server unnötig exponiert VPN, Bastion oder Admin-IP-Allowlist
Pod-CIDR im selben Bereich wie VPN Routing-Konflikte CIDRs vorab planen
VMs kopieren ohne MAC/Machine-ID zu ändern Identitätskonflikte Sauber klonen oder IDs erneuern
CNI vergessen Nodes bleiben NotReady CNI als Pflichtschritt nach Bootstrap
Remote-Firewall komplett öffnen Angriffsfläche zu groß Minimalports öffnen
hostPath als produktives Storage-Modell Node-gebunden und brüchig CSI oder klar deklarierter Lab-Storage
minikube/kind als Produktionsmodell verkaufen Abstrahiert wichtige Betriebsaspekte Als Lern- und Testwerkzeug einordnen
Vagrant-Skripte nicht idempotent schreiben Re-Provisioning bricht Vorhandene Zustände prüfen
VM-Snapshot als etcd-Backup behandeln Inkonsistenz möglich etcd-Snapshot nutzen
Produktive Secrets in Labs verwenden Datenabflussrisiko Dummy-Secrets und getrennte Accounts
38 / 40

39. Kommandosammlung

39.1 Cluster-Kontext

bash
kubectl config get-contexts
kubectl config current-context
kubectl cluster-info
kubectl version

39.2 Nodes

bash
kubectl get nodes -o wide
kubectl describe node <node>
kubectl cordon <node>
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
kubectl uncordon <node>

39.3 kubeadm

bash
kubeadm version
sudo kubeadm init --config kubeadm-config.yaml
kubeadm token create --print-join-command
sudo kubeadm reset -f
sudo kubeadm upgrade plan

39.4 containerd und CRI

bash
sudo systemctl status containerd --no-pager
sudo journalctl -u containerd -n 100 --no-pager
sudo crictl info
sudo crictl ps -a
sudo crictl images
sudo crictl logs <container-id>

39.5 kubelet

bash
sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -n 200 --no-pager
sudo journalctl -u kubelet -f

39.6 kind

bash
kind version
kind create cluster --name workshop
kind create cluster --config kind-workshop.yaml
kind get clusters
kind get nodes --name workshop
kind load docker-image workshop/app:dev --name workshop
kind delete cluster --name workshop

39.7 minikube

bash
minikube version
minikube start --driver=docker
minikube start --driver=virtualbox
minikube start --driver=vmware
minikube start --driver=ssh --ssh-ip-address=<REMOTE_VM_IP>
minikube status
minikube addons list
minikube addons enable ingress
minikube tunnel
minikube stop
minikube delete --all --purge

39.8 Vagrant

bash
vagrant version
vagrant plugin list
vagrant up --provider=virtualbox
vagrant up --provider=vmware_desktop
vagrant status
vagrant ssh cp1
vagrant provision cp1
vagrant snapshot save cp1 before-change
vagrant destroy -f

39.9 Remote-Diagnose

bash
ssh workshop@<ip> hostnamectl
ssh workshop@<ip> ip addr
ssh workshop@<ip> sudo ufw status verbose
nc -vz <api-endpoint> 6443
curl -k https://<api-endpoint>:6443/readyz

39.10 Netzwerk

bash
ip addr
ip route
ss -tulpen
sudo tcpdump -ni any port 6443
kubectl get svc,endpoints,endpointslices -A
kubectl get ingress -A
kubectl get networkpolicy -A
39 / 40

40. Quellen und weiterführende Dokumentation

Primärquellen, die du für Versionen, Flags und Providerdetails regelmäßig prüfen solltest:

  • Kubernetes Setup: https://kubernetes.io/docs/setup/
  • kubeadm: https://kubernetes.io/docs/reference/setup-tools/kubeadm/
  • kubeadm installieren: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
  • kubeadm Cluster erstellen: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
  • kubeadm HA: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/
  • Kubernetes Ports und Protokolle: https://kubernetes.io/docs/reference/networking/ports-and-protocols/
  • Container Runtimes: https://kubernetes.io/docs/setup/production-environment/container-runtimes/
  • kind Quick Start: https://kind.sigs.k8s.io/docs/user/quick-start/
  • kind Konfiguration: https://kind.sigs.k8s.io/docs/user/configuration/
  • kind Ingress: https://kind.sigs.k8s.io/docs/user/ingress/
  • kind LoadBalancer: https://kind.sigs.k8s.io/docs/user/loadbalancer/
  • kind lokale Registry: https://kind.sigs.k8s.io/docs/user/local-registry/
  • minikube Start: https://minikube.sigs.k8s.io/docs/start/
  • minikube Treiber: https://minikube.sigs.k8s.io/docs/drivers/
  • minikube VirtualBox: https://minikube.sigs.k8s.io/docs/drivers/virtualbox/
  • minikube VMware: https://minikube.sigs.k8s.io/docs/drivers/vmware/
  • minikube SSH: https://minikube.sigs.k8s.io/docs/drivers/ssh/
  • Vagrant Dokumentation: https://developer.hashicorp.com/vagrant/docs
  • Vagrant VirtualBox Provider: https://developer.hashicorp.com/vagrant/docs/providers/virtualbox
  • Vagrant VMware Provider: https://developer.hashicorp.com/vagrant/docs/providers/vmware
  • k3s Installation: https://docs.k3s.io/installation
  • RKE2 Installation: https://docs.rke2.io/install/quickstart
  • RKE2 HA: https://docs.rke2.io/install/ha
  • k0s Installation: https://docs.k0sproject.io/
  • Talos Linux Dokumentation: https://docs.siderolabs.com/talos/
  • MetalLB: https://metallb.io/
40 / 40

K8S · Orthevs Academy

31 modules ως ενιαία διαδρομή μάθησης.

Η σειρά παραμένει αμετάβλητη: από τη βασική δομή του lab μέσω τεχνικών θεμελίων και λειτουργικών μοντέλων έως το τελικό troubleshooting. Τα τεχνικά ονόματα καταλόγων διατηρούνται σκόπιμα.

Orthevs – Η μηχανική ως βάση για όσα ακολουθούν.

Το Orthevs ως εφαρμογή

Εγκατάσταση του Orthevs

Η web app του Orthevs μπορεί να εγκατασταθεί σε υποστηριζόμενες κινητές συσκευές και browsers υπολογιστή.

iPhone και iPad

Ανοίξτε το Orthevs στο Safari. Πατήστε Κοινή χρήση και μετά Προσθήκη στην οθόνη αφετηρίας. Ενεργοποιήστε το Άνοιγμα ως web app, αν εμφανίζεται, και πατήστε Προσθήκη.

Android

Για τη web app, ανοίξτε το μενού του browser και επιλέξτε Εγκατάσταση εφαρμογής ή Προσθήκη στην αρχική οθόνη. Επιβεβαιώστε την εγκατάσταση.

Browser υπολογιστή

Ανοίξτε το μενού του browser ή το εικονίδιο εγκατάστασης στη γραμμή διευθύνσεων και επιλέξτε Εγκατάσταση εφαρμογής ή Προσθήκη στο Dock.

Η εγκατάσταση της web app εξαρτάται από τον browser. Αν δεν εμφανιστεί διάλογος εγκατάστασης, ακολουθήστε τα βήματα του browser που αναφέρονται παραπάνω.