Fullmoon System

Kubernetes mit Kubespray: Rocky Linux 9 und Cilium im Betrieb

EdwardMoon

Dieser Leitfaden baut mit Kubespray und Ansible einen reproduzierbaren Kubernetes-Cluster auf Rocky-Linux-9-Knoten auf. Nach der Bereitstellung werden drei Control-Plane-Knoten, etcd-Quorum, API-VIP und Cilium-Netzwerk geprüft. Im Mittelpunkt steht, ob die Plattform auch Ausfälle und Upgrades bewältigen kann.

Kubernetes 1.31.6 aus dem früheren Artikel hat im November 2025 sein Supportende erreicht. Diese überarbeitete Fassung verwendet den Stand Juli 2026: Kubespray 2.31.0 mit dem standardmäßigen Kubernetes 1.35.4 und gebündeltem Cilium 1.19.3. Vor der Einführung Prüfsummen des gewählten Kubespray-Tags und Ciliums Kubernetes-Kompatibilitätsmatrix gemeinsam kontrollieren.

Kubespray-Kubernetes-Aufbau: Ansible, API-VIP, drei Control-Plane- und drei Worker-Knoten, Cilium und etcd-Sicherung
Vom Ansible-Steuerknoten drei Control-Plane-/etcd- und drei Worker-Knoten über den API-VIP bereitstellen und Cilium-Pfade sowie externe Sicherungen prüfen

Versionen und Supportumfang

Komponente Stand dieses Leitfadens Auswahlgrund und Einschränkungen
Rocky Linux Aktueller Patchstand von 9.x Nebenversion, Kernel und Zeitsynchronisierung auf allen Knoten abstimmen
Kubespray Tag v2.31.0 Release-Tag und requirements für Reproduzierbarkeit festlegen
Kubernetes v1.35.4 Standard von Kubespray 2.31.0; unterstützte Nebenversion
Cilium v1.19.3 Mit Kubespray 2.31.0 gebündelte Version
containerd Kubespray-Standardwert Geprüfte Kombination des Releases 2.31.0 verwenden
etcd Drei Mitglieder Mehrheit auch bei Ausfall eines Knotens erhalten
Die neueste Kubernetes-Version ist nicht automatisch gemeinsam mit Kubespray und Cilium geprüft. Im Juli 2026 ist Kubernetes 1.36.2 der neueste stabile Stand, Kubespray 2.31.0 verwendet standardmäßig jedoch 1.35.4. Zusätzlich die garantierten Ende-zu-Ende-Testkombinationen der stabilen Cilium-Dokumentation prüfen. Nicht aufgeführte Kombinationen vor einem Produktionseinsatz gesondert testen.

Topologie

Host Beispiel-IP Rolle
ansible01 10.20.0.5 Kubespray ausführen sowie Inventory und Artefakte aufbewahren
api.k8s.example.com 10.20.0.10 Externer HAProxy-/Loadbalancer-VIP auf Port 6443
cp01~cp03 10.20.0.11~13 kube_control_plane + etcd
wk01~wk03 10.20.0.21~23 kube_node
backup01 10.20.0.30 Verschlüsselte etcd-Snapshots und Inventory-Sicherung

Den API-VIP mit mindestens zwei Proxys und VRRP oder einem vorhandenen Loadbalancer bereitstellen. Ein einzelner HAProxy reicht dafür nicht aus. Healthchecks auf Port 6443 müssen die kube-apiserver aller Control-Plane-Knoten berücksichtigen. Vorab einen Adressplan ohne Überschneidungen zwischen Knoten-, Pod- und Service-CIDRs sowie Unternehmens-, VPN- und Speichernetzen festlegen.

Namensauflösung und Ports prüfen

getent hosts api.k8s.example.com cp01 cp02 cp03 wk01 wk02 wk03

for host in cp01 cp02 cp03 wk01 wk02 wk03; do
  printf '%-6s ' "$host"
  timeout 3 bash -c "</dev/tcp/${host}/22" && echo SSH_OK || echo SSH_FAIL
done

timeout 3 bash -c '</dev/tcp/api.k8s.example.com/6443'   && echo API_VIP_OK || echo API_VIP_FAIL

Rocky-Linux-9-Knoten vorab prüfen

Kubespray verwaltet containerd, kubelet, Kernelmodule und sysctl. Deshalb nicht zuvor unterschiedliche Installationsskripte ausführen. Stattdessen Betriebssystem, CPU, Arbeitsspeicher, Datenträger, cgroups, Zeitsynchronisierung und vorhandene Container-Laufzeiten aller Knoten erfassen.

cat /etc/rocky-release
uname -r
timedatectl status
free -h
lsblk -f
findmnt -no FSTYPE,OPTIONS / /var
stat -fc %T /sys/fs/cgroup
swapon --show
rpm -qa | grep -E 'kube(let|adm|ctl)|containerd|docker|cri-o' || true
Kubernetes 1.35 setzt standardmäßig cgroup v2 voraus. Die systemd-cgroup-v2-Konfiguration von Rocky Linux 9 beibehalten, statt von bisherigen cgroup-v1-Ausnahmen abhängig zu bleiben. SELinux und firewalld nicht pauschal abschalten; den unterstützten Ansatz der Kubespray-Versionsdokumentation und die Netzwerk-ACLs der Organisation prüfen.

Eigenes Verwaltungskonto und SSH-Hostschlüssel

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/kubespray_ed25519

for host in cp01 cp02 cp03 wk01 wk02 wk03; do
  ssh-copy-id -i ~/.ssh/kubespray_ed25519.pub "ansible@${host}"
  ssh-keyscan -H "$host" >> ~/.ssh/known_hosts.new
done

sort -u ~/.ssh/known_hosts.new >> ~/.ssh/known_hosts
rm -f ~/.ssh/known_hosts.new
chmod 0600 ~/.ssh/known_hosts

Ergebnisse von ssh-keyscan erst nach Abgleich mit Fingerabdrücken einer vertrauenswürdigen Konsole oder Anlagenverwaltung registrieren. Ohne StrictHostKeyChecking lassen sich Man-in-the-Middle-Angriffe nicht erkennen. Dem ansible-Konto nur erforderliche sudo-Rechte geben und direkte SSH-Anmeldung als root nicht erlauben.

Ausführungsumgebung auf Kubespray 2.31.0 festlegen

Kubespray-Dateien über einen Release-Tag statt den main-Zweig festlegen. Mit Python-venv und requirements.txt lässt sich dieselbe Steuerumgebung reproduzieren, ohne Ansible-Abhängigkeiten mit dem System-Python zu vermischen.

sudo dnf install -y git python3.12 python3.12-pip

git clone --branch v2.31.0 --depth 1   https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
git verify-tag v2.31.0 || git show --show-signature --no-patch v2.31.0

python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --requirement requirements.txt

python --version
ansible --version
git describe --tags --always
Eine Git-Signaturprüfung ist nur aussagekräftig, wenn der vertrauenswürdige Maintainer-Schlüssel unabhängig geprüft wurde. Für abgeschottete Netze Prüfsummen und Herkunft von Release-Archiv, Python-Wheels und Container-Images im verbundenen Netz prüfen und anschließend in ein freigegebenes Repository übernehmen.

Inventory erstellen

inventory/prod/inventory.yml

cp -a inventory/sample inventory/prod
cat > inventory/prod/inventory.yml <<'YAML'
all:
  hosts:
    cp01: {ansible_host: 10.20.0.11, ip: 10.20.0.11, access_ip: 10.20.0.11}
    cp02: {ansible_host: 10.20.0.12, ip: 10.20.0.12, access_ip: 10.20.0.12}
    cp03: {ansible_host: 10.20.0.13, ip: 10.20.0.13, access_ip: 10.20.0.13}
    wk01: {ansible_host: 10.20.0.21, ip: 10.20.0.21, access_ip: 10.20.0.21}
    wk02: {ansible_host: 10.20.0.22, ip: 10.20.0.22, access_ip: 10.20.0.22}
    wk03: {ansible_host: 10.20.0.23, ip: 10.20.0.23, access_ip: 10.20.0.23}
  children:
    kube_control_plane:
      hosts: {cp01: {}, cp02: {}, cp03: {}}
    kube_node:
      hosts: {wk01: {}, wk02: {}, wk03: {}}
    etcd:
      hosts: {cp01: {}, cp02: {}, cp03: {}}
    k8s_cluster:
      children:
        kube_control_plane: {}
        kube_node: {}
    calico_rr:
      hosts: {}
YAML

Die etcd-Mitgliederzahl ungerade halten. Control Plane und Worker getrennt definieren: ansible_host ist die SSH-Adresse, ip und access_ip sind Adressen für die Kommunikation zwischen Knoten. Bei NAT oder mehreren Netzwerkkarten beworbene Adressen und Routing gesondert prüfen.

Die folgenden drei Dateien im Editor öffnen und die jeweiligen Schlüssel ergänzen oder vorhandene Werte ändern. Die Beispiele sind YAML-Dateiinhalte, keine Shell-Befehle.

Wichtige Werte in inventory/prod/group_vars/all/all.yml

ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/kubespray_ed25519

loadbalancer_apiserver:
  address: 10.20.0.10
  port: 6443

kubeconfig_localhost: true
kubectl_localhost: true

Kubernetes und Sicherheitsstandards

In Kubespray v2.31.0 verwenden kube_version und cilium_version numerische Versionen ohne vorangestelltes v. Kubespray ergänzt das Präfix selbst in Download-URLs und Container-Tags. Werte daher nicht als v1.35.4 angeben. Vorhandene Schlüssel im Beispiel-YAML ändern, statt sie am Ende doppelt hinzuzufügen.

kube_version: 1.35.4
container_manager: containerd
kube_network_plugin: cilium

kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
cluster_name: cluster.local

kube_api_anonymous_auth: false
remove_anonymous_access: true
kubernetes_audit: true
supplementary_addresses_in_ssl_keys:
  - 10.20.0.10
  - api.k8s.example.com

Pod- und Service-CIDRs dürfen Router-, VPN- oder Rechenzentrumsnetze nicht überlappen. Ein späterer Wechsel bereits verwendeter CIDRs kann einer Migration auf einen neuen Cluster gleichkommen. API-VIP und DNS-Name müssen im SAN des kube-apiserver-Zertifikats enthalten sein.

Cilium-Overlay und Hubble konfigurieren

cilium_version: 1.19.3
cilium_tunnel_mode: vxlan
cilium_identity_allocation_mode: crd
cilium_ipam_mode: kubernetes
cilium_cni_exclusive: true

cilium_enable_hubble: true
cilium_hubble_install: true
cilium_hubble_tls_generate: true
cilium_enable_hubble_ui: false
Der Ersatz von kube-proxy verändert den Service-Datenpfad und ist keine bloße Leistungsoption. Dieser Basisleitfaden behält kube-proxy bei. Für einen Ersatz cilium_kube_proxy_replacement, globalen API-Endpunkt, DSR, SNAT und Host-Firewall gesondert planen und Last- sowie Ausfalltests durchführen.

Prüfungen vor der Bereitstellung

CIDR-Überschneidungen und Inventory-Struktur

ip route
ip -4 address show

grep -R --line-number -E   'kube_(service_addresses|pods_subnet)|loadbalancer_apiserver|kube_version|cilium_version'   inventory/prod/group_vars

ansible-inventory -i inventory/prod/inventory.yml --graph
ansible-inventory -i inventory/prod/inventory.yml --list   > inventory/prod/inventory-expanded.json

SSH, sudo und Python prüfen

ansible -i inventory/prod/inventory.yml all -m ping
ansible -i inventory/prod/inventory.yml all --become   -m command -a 'id'
ansible -i inventory/prod/inventory.yml all --become   -m shell -a 'python3 --version; stat -fc %T /sys/fs/cgroup; swapon --show'

Ein erfolgreiches ping-Modul allein genügt nicht. Prüfen, ob become ohne Interaktion funktioniert und Python-Interpreter sowie cgroup v2 auf allen Knoten zusammenpassen. Ansible-Vault-Passwortdateien und private SSH-Schlüssel nicht ins Repository einchecken.

Playbook-Syntax und Änderungsliste prüfen

ansible-playbook -i inventory/prod/inventory.yml   cluster.yml --syntax-check

git status --short
git diff -- inventory/prod/group_vars inventory/prod/inventory.yml

tar --exclude='credentials' --exclude='artifacts'   -czf "inventory-prod-$(date +%F).tgz" inventory/prod

Kubespray ausführen

Nicht sofort beliebige Tags auf sämtliche Knoten anwenden. Das offizielle cluster.yml mit geprüftem Inventory ausführen. Bei Fehlern vor einem erneuten Lauf zuerst den ursprünglich fehlgeschlagenen Task und den Knotenzustand prüfen. Kubespray strebt Idempotenz an; externe Loadbalancer und Netzwerke haben jedoch einen eigenen Zustand.

mkdir -p logs

ansible-playbook -i inventory/prod/inventory.yml   cluster.yml --become   2>&1 | tee "logs/cluster-$(date +%F-%H%M%S).log"

test ${PIPESTATUS[0]} -eq 0
Bei Verwendung von tee kann der abschließende Shell-Exitcode zu tee gehören. Das Ergebnis von ansible-playbook deshalb über PIPESTATUS[0] prüfen. Logs können Hostnamen, IP-Adressen und Task-Ergebnisse enthalten. Zugriffsrechte und Aufbewahrungsdauer festlegen und sensible Informationen vor externer Weitergabe entfernen.

Bereitstellungsergebnisse prüfen

kubeconfig und Control-Plane-Endpunkt

find inventory/prod/artifacts -maxdepth 2 -type f -ls

install -d -m 0700 "$HOME/.kube"
install -m 0600 inventory/prod/artifacts/admin.conf "$HOME/.kube/config"

kubectl config view --minify
kubectl cluster-info
kubectl get --raw='/readyz?verbose'

Artefaktnamen hängen von Kubespray-Tag und Konfiguration ab; zuerst das find-Ergebnis prüfen. kubeconfig enthält cluster-admin-Zugangsdaten und darf nicht unverändert in gemeinsam genutzten Home-Verzeichnissen oder CI-Artefakten landen. Für den Betrieb OIDC und RBAC mit minimalen Rechten verwenden.

Knoten, etcd und System-Pods prüfen

kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl get --raw='/livez?verbose'

ansible -i inventory/prod/inventory.yml etcd --become   -m command -a 'systemctl status etcd --no-pager'

kubectl -n kube-system get endpoints kube-dns
kubectl get events -A --sort-by='.lastTimestamp' | tail -n 50

Cilium und Hubble prüfen

kubectl -n kube-system rollout status daemonset/cilium --timeout=10m
kubectl -n kube-system get pods -l k8s-app=cilium -o wide

cilium status --wait --wait-duration 10m
cilium connectivity test

kubectl -n kube-system get secret | grep -i hubble
kubectl -n kube-system logs deployment/hubble-relay --tail=100

cilium connectivity test erzeugt mehrere Namespaces und Pods. Gemäß den Betriebsvorgaben in einer geeigneten Prüfumgebung ausführen und danach bereinigen. Erst Workloads bereitstellen, wenn Cilium-DaemonSet auf allen Knoten, Operator, CoreDNS und Service-Routing funktionieren.

Workloads und NetworkPolicy prüfen

Kurztest für Deployment, Service und DNS

kubectl create namespace smoke-test
kubectl -n smoke-test create deployment web   --image=registry.k8s.io/e2e-test-images/agnhost:2.53   -- /agnhost netexec --http-port=8080
kubectl -n smoke-test expose deployment web --port=80 --target-port=8080
kubectl -n smoke-test rollout status deployment/web --timeout=5m

kubectl -n smoke-test run client --rm -it --restart=Never   --image=docker.io/library/busybox:1.36.1 --command -- /bin/sh -c 'nslookup web; wget -qO- http://web/'

Standardmäßig verweigern, dann ausdrücklich erlauben

cat <<'YAML' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-default-deny
  namespace: smoke-test
spec:
  podSelector: {matchLabels: {app: web}}
  policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-allow-client
  namespace: smoke-test
spec:
  podSelector: {matchLabels: {app: web}}
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector: {matchLabels: {run: client}}
      ports:
        - {protocol: TCP, port: 8080}
YAML

kubectl -n smoke-test get networkpolicy

Erfolg vor Anwendung der Richtlinie, Fehlschlag nach Standardverweigerung und Erfolg nach ausdrücklicher Freigabe getrennt prüfen. Wird auch ausgehendes DNS begrenzt, tatsächlichen kube-dns-Selektor und UDP sowie TCP 53 berücksichtigen. Nach dem Test Ressourcen mit kubectl delete namespace smoke-test entfernen.

etcd-Snapshots und Wiederherstellung vorbereiten

Unmittelbar nach dem Aufbau etcd-Snapshots automatisieren. Einen Snapshot auf einem Mitglied erzeugen und in einen verschlüsselten Speicher außerhalb des Clusters kopieren. Wiederherstellungsablauf zusammen mit Inventory, Zertifikaten und Kubespray-Tag dokumentieren.

# Auf einem etcd-Knoten der standardmäßigen Host-Bereitstellung ausführen.
sudo systemctl cat etcd
sudo grep -E '^ETCD_(LISTEN_CLIENT_URLS|TRUSTED_CA_FILE|CERT_FILE|KEY_FILE)=' /etc/etcd.env

sudo bash -euo pipefail <<'BASH'
backup_dir=/var/backups/etcd
install -d -m 0700 "$backup_dir"
snapshot="$backup_dir/snapshot-$(date +%F-%H%M%S).db"
node_name=$(hostname -s)
ca=/etc/ssl/etcd/ssl/ca.pem
cert="/etc/ssl/etcd/ssl/admin-${node_name}.pem"
key="/etc/ssl/etcd/ssl/admin-${node_name}-key.pem"
for file in "$ca" "$cert" "$key"; do test -r "$file"; done
etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert="$ca" --cert="$cert" --key="$key" snapshot save "$snapshot"
etcdutl snapshot status "$snapshot" --write-out=table
BASH
Bei der Standardeinstellung etcd_deployment_type: host läuft etcd als systemd-Dienst. In /etc/etcd.env und systemctl cat etcd tatsächliche Endpunkte und Zertifikatspfade prüfen und die obigen Werte anpassen. Weichen Inventory- und Betriebssystemhostnamen voneinander ab, auch den Admin-Zertifikatsnamen anhand der vorhandenen Datei korrigieren. Wiederherstellung nicht im selben Cluster erproben: Snapshot-Restore, API-Start und Objektkonsistenz regelmäßig isoliert testen.

Zertifikate, Audit und Betriebskontrollen

sudo kubeadm certs check-expiration
kubectl auth can-i --list
kubectl auth can-i create clusterrolebindings --as=system:anonymous

kubectl get --raw='/metrics' | grep -E   'apiserver_request_total|apiserver_request_duration_seconds' | head

kubectl get nodes -o json | jq -r   '.items[] | [.metadata.name,.status.nodeInfo.kubeletVersion,.status.nodeInfo.containerRuntimeVersion] | @tsv'
  • API-VIP und DNS prüfen und testen, dass kubectl-Anfragen nach dem Stoppen eines Control-Plane-Knotens weiter erfolgreich sind.
  • Alarme für etcd-Mitglieder, Leader, Datenbankgröße und Snapshot-Erfolg einrichten.
  • Knoten-Dateisysteme, Inodes, Speicherdruck, PID-Druck und Image-Dateisystemkapazität überwachen.
  • kube-apiserver-Audit-Logs zentral übertragen und Manipulationsschutz sowie Aufbewahrung einrichten.
  • cluster-admin-kubeconfig aus alltäglichen Konten entfernen und OIDC, RBAC sowie kurze Sitzungen verwenden.
  • Cilium-Drops, Richtlinienentscheidungen, DNS-Fehler und Ablauf von Hubble-Zertifikaten beobachten.

Kubernetes mit Kubespray aktualisieren

Kubernetes-Nebenversionen schrittweise erhöhen und die Version-Skew-Richtlinie einhalten. Zuerst prüfen, ob Ziel-Patchversion und Cilium-Version in Prüfsummen- und Supportlisten des Ziel-Kubespray-Tags enthalten sind. Anschließend etcd-Snapshot, Workload-Sicherungen, PodDisruptionBudgets und freie Kapazität kontrollieren.

git fetch --tags --prune
git tag --sort=-version:refname | head

# Vorhandenes Inventory in neuen Clone mit neuer venv kopieren und Unterschiede prüfen.
git diff v2.31.0..'<TARGET_KUBESPRAY_TAG>' --   inventory/sample roles/kubespray_defaults docs

ansible-playbook -i inventory/prod/inventory.yml   upgrade-cluster.yml --become --syntax-check

ansible-playbook -i inventory/prod/inventory.yml   upgrade-cluster.yml --become   2>&1 | tee "logs/upgrade-$(date +%F-%H%M%S).log"

test ${PIPESTATUS[0]} -eq 0

Vor upgrade-cluster.yml die dringenden Upgrade-Hinweise des neuen Releases lesen. Bei Kubespray 2.31 insbesondere cgroup v1, das eingestellte ingress-nginx, das archivierte Kubernetes Dashboard, erforderliche etcd-Vorversionen und entfernte Variablen beachten. Control Plane und Worker nicht gleichzeitig neu starten.

Häufige Fehler und sichere Alternativen

Fehler Auswirkung Alternative
Direkt aus main bereitstellen Abhängigkeiten und Standardwerte ändern sich laufend Release-Tag, requirements und Prüfsummen festlegen
Zwei etcd-Mitglieder Verlust der Mehrheit bei einem Knotenausfall Ungerade Anzahl von drei oder fünf Mitgliedern
Einzelner API-Endpunkt control plane SPOF Mehrere Loadbalancer und VIP-Healthchecks
Überlappende CIDRs Routingkonflikte zwischen Pods, Services und Unternehmensnetz IPAM und Routen vor der Bereitstellung prüfen
SSH-Hostschlüsselprüfung deaktivieren Man-in-the-Middle-Angriffe bleiben unerkannt Fingerabdruck prüfen und danach in known_hosts aufnehmen
cluster-admin-kubeconfig gemeinsam nutzen Offenlegung clusterweiter Vollzugriffsrechte OIDC, RBAC und kurze Sitzungen
Lediglich eine Snapshot-Datei erzeugen Wiederherstellbarkeit bleibt ungeprüft Regelmäßige isolierte Wiederherstellungsübungen

Checkliste für den Aufbau

  1. Kubespray-Tag sowie Python- und Ansible-requirements sind festgelegt.
  2. Supportdokumentation für die Kombination aus Kubernetes, Cilium und containerd geprüft.
  3. Drei Control-Plane-/etcd-Knoten und mehrere API-Loadbalancer vorbereitet.
  4. Pod-, Service-, Knoten-, VPN- und Speicher-CIDRs überschneiden sich nicht.
  5. SSH-Hostschlüssel, sudo-Umfang sowie Aufbewahrung von Vault-Geheimnissen und privaten Schlüsseln geprüft.
  6. Knoten, API, CoreDNS, Cilium-Konnektivität und NetworkPolicy getestet.
  7. etcd-Snapshots extern aufbewahrt und isolierte Wiederherstellung geübt.
  8. Zertifikatsablauf, Audit, Ressourcenengpässe und Cilium-Drops werden überwacht.
  9. Vor Upgrades dringende Hinweise, Version Skew, PDBs und freie Kapazität prüfen.

Weiterführende Dokumentation

Zusammenfassung

Ein Kubernetes-Aufbau mit Kubespray prüft Versionskombination, Drei-Knoten-Mehrheit, API-Endpunkt, Netzwerk und Wiederherstellungsplan gemeinsam. Release-Tag und Inventory festlegen, anonyme Zugriffe begrenzen und Cilium-Konnektivität, NetworkPolicy sowie etcd-Restore tatsächlich testen. Erst nach diesen Prüfungen wird aus der Rocky-Linux-9-Übung eine betreibbare Kubernetes-Plattform.