- Authors

- Name
- Phillip Pham
- @ddppham
Kubernetes Production Cluster aufsetzen: Von kubeadm bis zum fertigen Setup
TL;DR: Dieser Guide führt durch das Aufsetzen eines produktionsreifen Kubernetes-Clusters mit kubeadm: HA Control Plane mit 3 Nodes, CNI-Auswahl (Cilium empfohlen), StorageClass-Konfiguration, Ingress mit NGINX, und Security Hardening. Am Ende steht eine Checkliste für Production Readiness.
Warum kubeadm?
Managed Kubernetes (EKS, AKS, GKE) nimmt einem viel Arbeit ab -- aber nicht jeder kann oder will in die Public Cloud. Gründe für Self-Managed Kubernetes mit kubeadm:
- Datenhoheit: Cluster läuft auf eigener Infrastruktur oder bei einem deutschen Hoster
- Kosten: Kein Management-Fee pro Cluster (EKS: ~73 EUR/Monat nur für die Control Plane)
- Lerneffekt: Wer kubeadm versteht, versteht Kubernetes
- Flexibilität: Volle Kontrolle über Versionen, CNI, Runtime und Addons
Voraussetzungen
Hardware-Empfehlung (Production)
| Rolle | Anzahl | CPU | RAM | Disk | OS |
|---|---|---|---|---|---|
| Control Plane | 3 (HA) | 4 vCPU | 8 GB | 50 GB SSD | Ubuntu 22.04 / RHEL 9 |
| Worker Node | 3+ | 4-8 vCPU | 16-32 GB | 100 GB SSD | Ubuntu 22.04 / RHEL 9 |
| Load Balancer | 1 | 2 vCPU | 4 GB | 20 GB | HAProxy / NGINX |
Netzwerk
- Alle Nodes im gleichen Netzwerk (oder mit Routing)
- Ports offen: 6443 (API), 2379-2380 (etcd), 10250 (kubelet), 10257/10259 (Controller/Scheduler)
- Pod-CIDR:
10.244.0.0/16(anpassen je nach CNI) - Service-CIDR:
10.96.0.0/12(default)
Alle Nodes vorbereiten
# Auf JEDEM Node ausführen:
# Swap deaktivieren (Kubernetes-Pflicht)
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# Kernel-Module laden
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# Sysctl-Parameter setzen
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# containerd installieren
sudo apt-get update
sudo apt-get install -y containerd
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# SystemdCgroup aktivieren (wichtig!)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
# kubeadm, kubelet, kubectl installieren (v1.31)
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | \
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | \
sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
HA Control Plane aufsetzen
Load Balancer einrichten
Für HA brauchen die 3 Control-Plane Nodes einen Load Balancer davor. Einfachste Variante mit HAProxy:
# /etc/haproxy/haproxy.cfg (auf dem LB-Node)
frontend k8s-api
bind *:6443
mode tcp
default_backend k8s-control-plane
backend k8s-control-plane
mode tcp
balance roundrobin
option tcp-check
server cp1 192.168.1.10:6443 check fall 3 rise 2
server cp2 192.168.1.11:6443 check fall 3 rise 2
server cp3 192.168.1.12:6443 check fall 3 rise 2
kubeadm Config erstellen
# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.31.4
controlPlaneEndpoint: "lb.k8s.internal:6443" # LB-Adresse
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/12"
etcd:
local:
dataDir: /var/lib/etcd
apiServer:
certSANs:
- "lb.k8s.internal"
- "192.168.1.9" # LB-IP
- "192.168.1.10" # CP1
- "192.168.1.11" # CP2
- "192.168.1.12" # CP3
extraArgs:
- name: audit-log-path
value: /var/log/kubernetes/audit.log
- name: audit-log-maxage
value: "30"
- name: audit-log-maxbackup
value: "10"
- name: audit-log-maxsize
value: "100"
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: "192.168.1.10"
bindPort: 6443
Ersten Control-Plane Node initialisieren
# Auf CP1:
sudo kubeadm init --config=kubeadm-config.yaml --upload-certs
# kubeconfig kopieren
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Der Output enthält zwei Join-Kommandos -- eins für weitere Control-Plane Nodes und eins für Worker Nodes. Sofort notieren.
Weitere Control-Plane Nodes joinen
# Auf CP2 und CP3 (Befehl aus dem kubeadm init Output):
sudo kubeadm join lb.k8s.internal:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane \
--certificate-key <cert-key>
Worker Nodes joinen
# Auf jedem Worker Node:
sudo kubeadm join lb.k8s.internal:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
# Status prüfen (auf CP1)
kubectl get nodes
# Alle Nodes sollten "NotReady" sein -- das CNI fehlt noch
CNI-Plugin installieren
Ohne CNI gibt es kein Pod-Networking. Die Wahl des CNI hat langfristige Auswirkungen.
CNI-Vergleich
| Feature | Cilium | Calico | Flannel |
|---|---|---|---|
| NetworkPolicy | Ja (L3/L4 + L7) | Ja (L3/L4) | Nein |
| eBPF | Ja (Kern-Architektur) | Optional | Nein |
| Service Mesh | Integriert (optional) | Nein | Nein |
| Encryption | WireGuard / IPsec | WireGuard / IPsec | Nein |
| Performance | Sehr hoch | Hoch | Mittel |
| Observability | Hubble (integriert) | Prometheus-basiert | Minimal |
| Komplexität | Mittel-Hoch | Mittel | Niedrig |
| Empfehlung | Production (Default) | Production (bewährt) | Dev/Test |
Cilium installieren (empfohlen)
# Cilium CLI installieren
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
curl -L --fail --remote-name-all \
https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-amd64.tar.gz
sudo tar xzvf cilium-linux-amd64.tar.gz -C /usr/local/bin
rm cilium-linux-amd64.tar.gz
# Cilium installieren
cilium install --version 1.16.5
# Status prüfen
cilium status --wait
kubectl get nodes # Jetzt sollten alle "Ready" sein
Alternative: Calico
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml
# Danach Installation CR mit podCIDR anpassen -- siehe Calico-Docs
Storage Classes einrichten
Production-Cluster brauchen mindestens eine StorageClass für PersistentVolumes.
Longhorn installieren (empfohlen für Self-Managed)
Longhorn bietet repliziertes Block-Storage direkt im Cluster:
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=2
Storage-Empfehlung nach Use Case
| Use Case | StorageClass | Provisioner |
|---|---|---|
| Datenbanken (PostgreSQL, MySQL) | Repliziert, SSD | Longhorn, Rook-Ceph |
| Elasticsearch / Logging | Lokal, schnell | local-path, OpenEBS |
| Shared Storage (ReadWriteMany) | NFS-basiert | NFS-Subdir, Longhorn (RWX) |
| Temporäre Daten | EmptyDir | Kubernetes-nativ |
Ingress Controller installieren
NGINX Ingress Controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2 \
--set controller.nodeSelector."node-role\.kubernetes\.io/worker"="" \
--set controller.service.type=LoadBalancer
cert-manager + Let's Encrypt
Automatische TLS-Zertifikate gehören zu jedem Production-Setup:
helm repo add jetstack https://charts.jetstack.io && helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace --set crds.enabled=true
Danach einen ClusterIssuer für Let's Encrypt erstellen und Ingress-Ressourcen mit der Annotation cert-manager.io/cluster-issuer: "letsencrypt-prod" versehen.
Security Hardening
RBAC: Keine Wildcard-ClusterRoles
# Sinnvolles RBAC für ein Dev-Team
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: developer
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
# Kein "delete" auf Pods -- wird über Deployments geregelt
# Kein Zugriff auf Secrets -- nur über ServiceAccounts
Pod Security Standards erzwingen
# Namespace mit Restricted PSS
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
NetworkPolicy: Default-Deny
In Production-Namespaces sollte standardmäßig kein Traffic erlaubt sein. Erstellt eine default-deny-all NetworkPolicy (Ingress + Egress) und gebt dann explizit nur DNS (Port 53 UDP/TCP) und die benötigten Service-Verbindungen frei. Ohne die DNS-Ausnahme funktioniert im Namespace gar nichts.
Production Readiness Checkliste
- HA Control Plane (3 Nodes) mit Load Balancer
- CNI-Plugin installiert und getestet (Cilium oder Calico)
- kubeadm und kubelet Version gepinnt (
apt-mark hold) - Ingress Controller mit TLS (cert-manager + Let's Encrypt)
- NetworkPolicies: Default-Deny in Production-Namespaces
- Mindestens eine StorageClass konfiguriert und als Default gesetzt
- RBAC konfiguriert (kein
cluster-adminfür Devs) - Pod Security Standards auf
restrictedin Prod-Namespaces - API-Server Audit Logging aktiviert
- Prometheus + Grafana mit Alerts eingerichtet
- etcd-Snapshot automatisiert + Velero installiert
- Restore getestet
- Node-Upgrade-Prozess dokumentiert
Fazit
Ein produktionsreifer Kubernetes-Cluster ist mehr als kubeadm init. Die eigentliche Arbeit steckt in CNI, Storage, Ingress, Security und Monitoring. Dieser Guide gibt euch das Fundament -- aber plant genug Zeit für Testing ein, bevor der erste Workload in Production geht.
Wer den Aufwand für Self-Managed scheut: Managed Kubernetes (EKS, AKS) ist eine valide Alternative. Die Konzepte aus diesem Guide (CNI, Storage, Ingress, Security) braucht man dort genauso.
Verwandte Artikel:
- Kubernetes Backup mit Velero: Praxisguide
- Kubernetes Disaster Recovery: RPO, RTO und Failover
- Kubernetes Security Hardening
- Kubernetes Monitoring und Observability
- Helm Package Management
Braucht ihr Hilfe beim Cluster-Aufbau? Wir unterstützen beim Design, Setup und Betrieb eurer Kubernetes-Plattform -- egal ob Self-Managed oder Cloud. Jetzt Kontakt aufnehmen oder schreibt an kontakt@pexon-consulting.de.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
High Availability: Kubernetes Control Plane absichern
Kubernetes Control Plane hochverfuegbar betreiben: Multi-Master-Setup, etcd-Quorum, API-Server-Loadbalancing und Failure-Szenarien.
Kubernetes-Upgrades ohne Downtime durchführen
Kubernetes-Cluster ohne Ausfallzeit upgraden: Schritt-für-Schritt-Anleitung mit PodDisruptionBudgets, Rolling Node Upgrades und Pre-Upgrade-Checkliste.
Disaster Recovery für Kubernetes-Cluster planen
Kubernetes Disaster Recovery planen: Von etcd-Backups über Multi-Cluster-Failover bis zur DR-Teststrategie mit konkreten Befehlen.
Redis auf Kubernetes: Cluster und Sentinel
Redis auf Kubernetes mit Helm deployen. Vergleich von Standalone, Cluster und Sentinel inklusive Persistence, Passwörter und Client-Anbindung.
Kubernetes Change Management: Sichere Production-Änderungen
Change Management für Kubernetes-Cluster: Risikobewertung, GitOps-basierte Approval-Workflows, Rollback-Strategien und Emergency-Change-Prozesse.