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.
2. Zielbild
Eine VM-basierte Kubernetes-Umgebung soll drei Ziele erfüllen:
- Reproduzierbarkeit: Du kannst die Umgebung löschen und mit denselben Parametern wieder aufbauen.
- Realitätsnähe: Du arbeitest mit Linux, systemd, Container Runtime, kubelet, Netzwerkadaptern, Firewalls, Zertifikaten und echten Cluster-Komponenten.
- 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.
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.
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]
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]
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 |
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.
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 |
9. Netzdesign für VM-Cluster
VM-Netzwerk ist der häufigste Grund für fehlerhafte Labs. Trenne bewusst:
- Management-Zugriff: SSH,
kubectl, Host-to-VM. - Node-to-Node: kubelet, CNI, Pod-Verkehr, Control-Plane-Kommunikation.
- Internet-Egress: Paketinstallation und Image-Pulls.
- 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.
10. Host-Vorbereitung
10.1 Gemeinsame Host-Prüfung
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
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:
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:
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.
11. VM-Basisstandard
Jede VM im Workshop sollte reproduzierbar die gleichen Basiseigenschaften haben.
11.1 Hostname und Namensauflösung
sudo hostnamectl set-hostname cp1
hostnamectl
Beispiel für /etc/hosts in lokalen Labs:
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
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
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
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:
sudo swapoff -a
sudo sed -i.bak '/\sswap\s/s/^/#/' /etc/fstab
free -h
11.6 Zeitsynchronisation
timedatectl
sudo timedatectl set-ntp true
Zeitdrift verursacht Zertifikats-, Token- und etcd-Probleme. Prüfe die Uhr besonders bei suspendierten Laptop-VMs.
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 |
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:
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
- Erstelle jede VM mit Ubuntu Server LTS.
- Aktiviere zwei Netzwerkadapter: NAT und Host-only.
- Installiere OpenSSH Server.
- Setze Hostname und statische IP oder DHCP-Reservation.
- Prüfe Internetzugriff und Host-to-VM-SSH.
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 |
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
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 |
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:
hostnamectl
cat /etc/machine-id
ip addr
ip route
systemctl status ssh
Wenn geklonte VMs dieselbe Machine-ID haben, erzeugst du sie neu:
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.
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/TCPnur von deiner IP, VPN oder internem Netz. - etcd niemals öffentlich öffnen.
- kubelet
10250/TCPnicht ö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.
# 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:
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:
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.
#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
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
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:
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:
sudo crictl info || true
sudo ctr version
Wenn crictl noch keine Runtime findet, legst du eine Konfiguration an:
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
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:
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
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:
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.
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:
sudo kubeadm init --config kubeadm-config.yaml
Kubeconfig für den Lab-Benutzer:
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:
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:
kubectl apply -f addons/cni/flannel.yaml
# oder
kubectl apply -f addons/cni/calico.yaml
# oder Cilium über Helm/CLI nach Workshop-Standard
Validierung:
kubectl get nodes -o wide
kubectl get pods -A
kubectl get events -A --sort-by=.lastTimestamp | tail -50
18.5 Test-Deployment
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
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:
hostname -f || hostname
ip addr
ip route
sudo systemctl is-active containerd
kubeadm version
19.3 Control Plane initialisieren
Auf cp1:
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:
kubeadm token create --print-join-command
19.4 Worker beitreten lassen
Auf worker1 und worker2:
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:
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
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.
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
controlPlaneEndpointvorkubeadm 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:
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:
sudo kubeadm init --config kubeadm-ha.yaml --upload-certs
Weitere Control-Plane-Nodes beitreten lassen:
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:
sudo kubeadm join 192.168.56.100:6443 \
--token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<HASH>
20.5 HA-Validierung
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:
sudo systemctl stop kubelet
# auf einem Control-Plane-Node, nicht auf allen
Danach prüfst du, ob der API-Endpunkt weiter antwortet.
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:
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:
brew install kind
kind version
21.3 Single-Node-kind
kind create cluster --name workshop
kubectl cluster-info --context kind-workshop
kubectl get nodes -o wide
Löschen:
kind delete cluster --name workshop
21.4 Multi-Node-kind mit Port-Mapping
Datei kind-workshop.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:
kind create cluster --config kind-workshop.yaml
kubectl get nodes -o wide
21.5 Image in kind laden
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:
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.
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 |
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:
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:
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
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
minikube start \
--driver=virtualbox \
--kubernetes-version=v1.36.1 \
--cpus=4 \
--memory=8192 \
--disk-size=40g
Als Default setzen:
minikube config set driver virtualbox
22.6 minikube mit VMware
minikube start \
--driver=vmware \
--kubernetes-version=v1.36.1 \
--cpus=4 \
--memory=8192 \
--disk-size=40g
Als Default setzen:
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:
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
minikube start \
--driver=docker \
--nodes=3 \
--cpus=4 \
--memory=8192 \
--kubernetes-version=v1.36.1
kubectl get nodes -o wide
22.9 Add-ons
minikube addons list
minikube addons enable metrics-server
minikube addons enable ingress
minikube addons enable dashboard
Dashboard:
minikube dashboard
Ingress testen:
kubectl get pods -n ingress-nginx
kubectl get ingress -A
22.10 LoadBalancer mit minikube tunnel
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
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
minikube stop
minikube delete
minikube delete --all --purge
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
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:
vagrant plugin install vagrant-vmware-desktop
vagrant plugin list
23.3 Projektstruktur
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
# 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:
#!/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:
#!/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:
#!/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:
vagrant up --provider=virtualbox
vagrant status
vagrant ssh cp1
VMware:
vagrant up --provider=vmware_desktop
vagrant status
vagrant ssh cp1
Provisionierung erneut ausführen:
vagrant provision cp1
vagrant provision worker1
vagrant provision worker2
Snapshot:
vagrant snapshot save cp1 before-cni
vagrant snapshot list cp1
vagrant snapshot restore cp1 before-cni
Rückbau:
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 |
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
curl -sfL https://get.k3s.io | sh -
sudo kubectl get nodes -o wide
sudo kubectl get pods -A
Kubeconfig für deinen Benutzer:
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:
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:
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:
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
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
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:
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]
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
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:
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:
sudo k0s token create --role=worker > worker-token
Auf dem Worker:
sudo k0s install worker --token-file worker-token
sudo k0s start
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:
- Talos-Image oder ISO passend zur Plattform laden.
- VM mit ausreichend CPU, RAM und Disk erstellen.
- Netz stabil konfigurieren.
- Maschinenkonfiguration mit
talosctl gen configerzeugen. - Konfiguration auf Control Plane und Worker anwenden.
- Cluster bootstrappen.
- kubeconfig abrufen.
Schematisch:
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.
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
- CNI installieren.
- Nodes und CoreDNS validieren.
- Metrics Server installieren.
- StorageClass bereitstellen.
- Ingress Controller oder Gateway Controller installieren.
- Zertifikatsmanagement installieren, falls TLS geübt wird.
- Observability installieren.
- GitOps optional ergänzen.
- 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 |
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:
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.
30. Ingress, Gateway und Load Balancer
30.1 NodePort
NodePort ist einfach, aber für Workshop-Zugriff oft unbequem.
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.
kubectl get ingressclass
kubectl get pods -A | grep -i ingress || true
Beispielressource:
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:
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.
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:
kubectl get clusterrolebinding
kubectl get serviceaccount -A
kubectl auth can-i '*' '*' --all-namespaces
kubectl get ns --show-labels
Namespace-Baseline:
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.
kubectl get secrets -A
kubectl delete namespace demo
32. Observability und Validierung
32.1 Basisvalidierung
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
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
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:
nslookup kubernetes.default.svc.cluster.local
wget -qO- http://kubernetes.default.svc.cluster.local
32.4 Metrics Server
kubectl top nodes
kubectl top pods -A
Wenn kubectl top nicht funktioniert, prüfst du zuerst Metrics Server, kubelet-Zertifikate und APIService-Status:
kubectl get apiservice | grep metrics
kubectl -n kube-system logs deployment/metrics-server
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:
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:
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.
34. Rückbau
34.1 kubeadm-Cluster zurückbauen
Auf Workern:
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:
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:
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -X
34.2 kind
kind get clusters
kind delete cluster --name workshop
docker ps -a | grep kind || true
34.3 minikube
minikube profile list
minikube stop
minikube delete --all --purge
34.4 Vagrant
vagrant status
vagrant destroy -f
rm -f .join-command
34.5 Remote-VMs
- kubeconfig lokal löschen.
- SSH-Key aus
authorized_keysentfernen, 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.
35. Troubleshooting-Katalog
35.1 kubeadm preflight schlägt fehl
Diagnose:
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
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
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
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
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
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
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
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 tunnelläuft nicht,- Firewall blockiert 80/443.
35.9 Vagrant-Provisionierung bricht ab
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:
sudo ufw status verbose
sudo iptables -S
sudo ss -tulpen | grep ssh
36. Workshop-Labs
Lab 1: kind in 10 Minuten
Ziel: Du erzeugst einen lokalen Multi-Node-Cluster ohne VM.
Aufgaben:
kindinstallieren.- Multi-Node-Konfiguration schreiben.
- Cluster erstellen.
- Deployment ausrollen.
- Service testen.
- Cluster löschen.
Abnahme:
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:
- minikube starten.
- Ingress-Add-on aktivieren.
- Demo-App deployen.
- Ingress erstellen.
- Hostauflösung testen.
Abnahme:
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:
- Ubuntu-VM erstellen.
- containerd installieren.
- kubeadm/kubelet/kubectl installieren.
kubeadm initausführen.- CNI installieren.
- Control-Plane-Taint entfernen.
- Demo-App testen.
Abnahme:
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:
- Vagrantfile erstellen.
- Common-Provisioning ausführen.
- Control Plane initialisieren.
- Worker joinen.
- CNI installieren.
- Scheduling testen.
Abnahme:
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:
- Remote-VM erzeugen.
- SSH-Key-Zugang einrichten.
- Firewall nur für Admin-IP öffnen.
- Kubernetes installieren.
- API-Zugriff validieren.
- Ingress optional öffnen.
- Rückbau durchführen.
Abnahme:
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.
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/hostskorrekt. - Zeit synchronisiert.
- Swap deaktiviert.
- Kernel-Module geladen.
- sysctl gesetzt.
- containerd läuft.
- kubelet/kubeadm/kubectl installiert.
37.3 Nach Cluster-Bootstrap
kubectl get nodeszeigt 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.
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 |
39. Kommandosammlung
39.1 Cluster-Kontext
kubectl config get-contexts
kubectl config current-context
kubectl cluster-info
kubectl version
39.2 Nodes
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
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
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
sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -n 200 --no-pager
sudo journalctl -u kubelet -f
39.6 kind
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
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
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
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
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
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/