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.

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 |
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
- Kubespray-Tag sowie Python- und Ansible-requirements sind festgelegt.
- Supportdokumentation für die Kombination aus Kubernetes, Cilium und containerd geprüft.
- Drei Control-Plane-/etcd-Knoten und mehrere API-Loadbalancer vorbereitet.
- Pod-, Service-, Knoten-, VPN- und Speicher-CIDRs überschneiden sich nicht.
- SSH-Hostschlüssel, sudo-Umfang sowie Aufbewahrung von Vault-Geheimnissen und privaten Schlüsseln geprüft.
- Knoten, API, CoreDNS, Cilium-Konnektivität und NetworkPolicy getestet.
- etcd-Snapshots extern aufbewahrt und isolierte Wiederherstellung geübt.
- Zertifikatsablauf, Audit, Ressourcenengpässe und Cilium-Drops werden überwacht.
- Vor Upgrades dringende Hinweise, Version Skew, PDBs und freie Kapazität prüfen.
Weiterführende Dokumentation
- Offizielles Kubespray-Release v2.31.0
- Offizielle Kubernetes-Releases und Supportzeiträume
- Offizielle Kubernetes-Version-Skew-Richtlinie
- Offizielle Kubernetes-Kompatibilitätsanforderungen von Cilium
- Ansible-Playbooks, Vault und rollende Bereitstellung
- Linux: Speicher- und Inode-Mangel diagnostizieren
- Linux-Container und cgroups v2
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.