Veröffentlicht am

Production Cluster mit kubeadm aufsetzen: Schritt für Schritt

Teilen:
Authors

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)

RolleAnzahlCPURAMDiskOS
Control Plane3 (HA)4 vCPU8 GB50 GB SSDUbuntu 22.04 / RHEL 9
Worker Node3+4-8 vCPU16-32 GB100 GB SSDUbuntu 22.04 / RHEL 9
Load Balancer12 vCPU4 GB20 GBHAProxy / 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

FeatureCiliumCalicoFlannel
NetworkPolicyJa (L3/L4 + L7)Ja (L3/L4)Nein
eBPFJa (Kern-Architektur)OptionalNein
Service MeshIntegriert (optional)NeinNein
EncryptionWireGuard / IPsecWireGuard / IPsecNein
PerformanceSehr hochHochMittel
ObservabilityHubble (integriert)Prometheus-basiertMinimal
KomplexitätMittel-HochMittelNiedrig
EmpfehlungProduction (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 CaseStorageClassProvisioner
Datenbanken (PostgreSQL, MySQL)Repliziert, SSDLonghorn, Rook-Ceph
Elasticsearch / LoggingLokal, schnelllocal-path, OpenEBS
Shared Storage (ReadWriteMany)NFS-basiertNFS-Subdir, Longhorn (RWX)
Temporäre DatenEmptyDirKubernetes-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-admin für Devs)
  • Pod Security Standards auf restricted in 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:


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