Back openDesk Edu for a sovereign, open-source education — every vote counts.
Vote nowSave products you love by clicking the heart icon.
Comprehensive DR testing and edge-specific patterns for k3s — failover validation, scheduled snapshots, multi-cluster recovery, and RPO/RTO planning.
k3s ist leichtgewichtig und schnell – aber der Betrieb in einer Produktionsumgebung erfordert die gleiche operationale Disziplin wie bei einem vollständigen Kubernetes. Upgrades, Backups, Disaster Recovery und Kapazitätsplanung werden kritisch, wenn Edge-Standorte zu Single Points of Failure für die Geschäftsprozesse werden.
Dieser Artikel behandelt den produktionsreifen Betrieb von k3s: Upgrade-Strategien, Backup/Restore sowohl für SQLite- als auch für etcd-Backends, Disaster-Recovery-Muster, High-Availability-Architekturen und bekannte Probleme bei einer Skalierung.
k3s bietet mehrere Upgrade-Pfade an, abhängig von Ihrem Deployment-Modell und Ihrer Downtime-Toleranz.
Bestens geeignet für: Stabile Standorte mit Internetverbindung und externen etcd/PostgreSQL-Datenspeichern.
apt-cache madison k3s
# Pin to specific minor version for controlled rollouts
sudo apt install -y k3s=1.30.2+k3s1
# Or upgrade to latest patch in current minor
sudo apt install -y --only-upgrade k3s
Vorteile:
apt downgrade k3s (behält die vorherige Binary)Nachteile:
Bestens geeignet für: Airgap-Umgebungen, Version Pinning oder wenn eine präzise Kontrolle über den Upgrade-Prozess erforderlich ist.
# Download new version
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.30.2+k3s1 sh -
# For airgap, download and install manually
wget https://github.com/k3s-io/k3s/releases/download/v1.30.2+k3s1/k3s
wget https://github.com/k3s-io/k3s/releases/download/v1.30.2+k3s1/k3s-airgap-images-amd64.tar.gz
sudo install k3s /usr/local/bin/
sudo systemctl restart k3s
Vorteile:
Nachteile:
Bestens geeignet für: Zero-Downtime-Upgrades mit vollständiger Rollback-Fähigkeit.
Konzept: Ein zweiter k3s-Cluster wird auf derselben Hardware bereitgestellt, der Traffic wird umgeschaltet, verifiziert und anschließend wird der alte Cluster außer Betrieb genommen.
Implementierung:
# On each node, install k3s-green alongside k3s-blue
sudo mkdir -p /etc/rancher/k3s-green
sudo cat > /etc/rancher/k3s-green/config.yaml <<'EOF'
token: green-cluster-token
datastore-endpoint: mysql://user:pass@mysql-green.example.com:3306/k3s
node-name: ${HOSTNAME}-green
server: https://10.0.1.10:6443
tls-san:
- k3s-green.example.com
EOF
sudo curl -sfL https://get.k3s.io | INSTALL_K3S_NAME=k3s-green INSTALL_K3S_EXEC="server --config /etc/rancher/k3s-green/config.yaml" sh -
# Cutover traffic (example: update Traefik ingress routing)
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
EOF
# Verify green cluster is healthy
kubectl --kubeconfig /etc/rancher/k3s-green/k3s.yaml get nodes
# Decommission blue cluster after validation period
sudo systemctl stop k3s
Vorteile:
Nachteile:
Bestens geeignet für: Automatisierte Upgrades über viele Edge-Nodes hinweg mit minimalen Unterbrechungen.
Der system-upgrade-controller verwaltet k3s-Upgrades über Plan- und Upgrade-Ressourcen:
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: k3s-upgrade
namespace: system-upgrade
spec:
version: v1.30.2+k3s1
concurrency: 1 # Upgrade one node at a time
serviceAccountName: system-upgrade-controller
# Drain pods before upgrade
drain:
enable: true
force: true
ignoreDaemonSets: true
deleteLocalData: true
# Upgrade strategy
upgrade:
image: rancher/k3s-upgrade:v1.30.2+k3s1
# Target nodes
nodeSelector:
matchLabels:
k3s.io/hostname: ${HOSTNAME}
Workflow:
Vorteile:
Nachteile:
k3s unterstützt zwei Datastores mit sehr unterschiedlichen Backup-Semantiken: SQLite (Standard, HA via embedded etcd) und externe Datenbanken (PostgreSQL/MySQL/etcd).
k3s verwendet SQLite für den Control-Plane-Status und embedded etcd für den HA-Cluster-Status.
Backup via k3s etcd-snapshot CLI (empfohlen für embedded etcd):
# Manual snapshot
sudo k3s etcd-snapshot save --name backup-$(date +%Y%m%d-%H%M%S)
# Scheduled snapshots (crontab)
0 2 * * * sudo k3s etcd-snapshot save --name daily-backup-\$(date +\%Y\%m\%d-\%H\%M\%S)
# S3 offload (k3s v1.30+)
sudo k3s etcd-snapshot save --s3 \
--s3-endpoint s3.us-west-2.amazonaws.com \
--s3-bucket my-k3s-backups \
--s3-access-key AKIA... \
--s3-secret-key secret... \
--s3-region us-west-2
Restore:
# Stop k3s
sudo systemctl stop k3s
# Restore from snapshot
sudo k3s etcd-snapshot restore --name backup-20260711-020000
# Start k3s
sudo systemctl start k3s
# Verify
sudo k3s kubectl get nodes
Backup auf Dateiebene (nur im Notfall):
# Stop k3s first (required for consistent copy)
sudo systemctl stop k3s
# Copy data directory
sudo tar czf k3s-backup-$(date +%Y%m%d).tar.gz /var/lib/rancher/k3s/
# Start k3s
sudo systemctl start k3s
Warnung: Backups auf Dateiebene sind fehleranfällig. Sie erfassen etcd-Datenverzeichnisse in ihrem laufenden Zustand, was möglicherweise nicht über k3s-Versionen hinweg portabel ist. Verwenden Sie wann immer möglich k3s etcd-snapshot.
Wenn k3s einen externen Datastore verwendet, sichern Sie diesen Datastore direkt und nicht k3s.
PostgreSQL:
pg_dump -U k3s_user -h db.example.com k3s_db | gzip > k3s-pg-backup.sql.gz
# Point-in-time recovery (PITR)
pg_dump -U k3s_user -h db.example.com -F c -b k3s_db > k3s-pg-backup.dump
MySQL:
mysqldump -u k3s_user -p k3s_db | gzip > k3s-mysql-backup.sql.gz
External etcd:
# Snapshot (ETCDCTL_API=3 für etcd v3)
ETCDCTL_API=3 etcdctl \
--endpoints https://etcd1.example.com:2379 \
--cacert /etc/kubernetes/pki/etcd/ca.crt \
--cert /etc/kubernetes/pki/etcd/server.crt \
--key /etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-snapshot.db
# Restore
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
--data-dir /var/lib/etcd-restore
For disaster recovery, you typically restore to new hardware or cloud VMs rather than the original failed node.
Embedded etcd to new cluster:
# Auf einem neuen Node k3s mit einem Token installieren, der auf die Restore-Daten verweist
sudo curl -sfL https://get.k3s.io | \
INSTALL_K3S_EXEC="server --token my-cluster-token \
--server https://10.0.1.10:6443 \
--cluster-init" \
sh -
# Anschließend Snapshot wiederherstellen
sudo k3s etcd-snapshot restore --name backup-20260711-020000
External PostgreSQL to new cluster:
# Auf einem neuen k3s-Cluster die Konfiguration so anpassen, dass der bestehende Datastore verwendet wird
sudo cat > /etc/rancher/k3s/config.yaml <<'EOF'
token: restored-cluster-token
datastore-endpoint: postgres://k3s_user:password@db.example.com:5432/k3s_db?sslmode=require
server: https://10.0.2.10:6443
EOF
# k3s starten (es wird eine Verbindung zum bestehenden Datastore hergestellt)
sudo systemctl start k3s
Caution: Restoring to a new cluster with the same token and server IP will cause the old cluster (if it comes back online) to fight for cluster membership. Ensure failed nodes are fully powered off or have their server IPs changed before bringing up restored nodes.
| Backend | RPO | RTO | Split-Brain Risk | Data Loss on Split | Storage Overhead |
|---|---|---|---|---|---|
| SQLite + embedded etcd | 5-10 min (snapshot interval) | 10-30 min (restore) | Low (Raft quorum) | None (writes block) | ~2x (Raft replication) |
| External PostgreSQL | seconds (WAL shipping) | minutes (failover) | Medium (need Patroni/HAProxy) | Possible (async replicas lag) | ~2-3x (replicas) |
| External MySQL | seconds (binlog) | minutes (failover) | Medium (need Orchestrator/HAProxy) | Possible (async replicas lag) | ~2-3x (replicas) |
| External etcd | seconds (snapshot interval) | minutes (restore) | Low (Raft quorum) | None (writes block) | ~2x (Raft replication) |
Recommendation:
| Business Criticality | RPO Target | RPO Target | Recommended Approach |
|---|---|---|---|
| Edge compute (offline) | 1 day | 4 hours | SQLite backups + snapshots, restore from last good backup |
| Retail/warehouse operations | 1 hour | 15 minutes | External PostgreSQL with streaming replication + Patroni failover |
| Industrial control systems | 5 minutes | 5 minutes | External etcd with quorum + automated failover |
| Critical infrastructure | seconds | seconds | Multi-region PostgreSQL with synchronous replication |
Split-brain occurs when two subsets of nodes believe they are the authoritative cluster, causing divergence of state (different pod IPs, different configmaps).
k3s built-in prevention (embedded etcd):
# etcd-Mitgliedschaft prüfen
sudo k3s kubectl get nodes -o wide
sudo k3s etcd-snapshot ls
# Falls ein Split-Brain vermutet wird, blockiert das Quorum die Schreibvorgänge
# Last-nodes-standing setzt sich fort (eine Seite gewinnt, die andere stoppt)
External PostgreSQL with Patroni:
Patroni manages leader election and failover for PostgreSQL:
# /etc/patroni/patronictl.yaml
scope: k3s-cluster
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: node1.example.com:8008
postgresql:
listen: 0.0.0.0:5432
connect_address: node1.example.com:5432
data_dir: /var/lib/postgresql/14/main
bin_dir: /usr/lib/postgresql/14/bin
config:
max_connections: 200
hot_standby: on
max_wal_senders: 5
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
initdb:
- encoding: UTF8
- data-checksums
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false
Etcd quorum checks:
# Überprüfen des etcd-Cluster-Health-Status
ETCDCTL_API=3 etcdctl --endpoints https://etcd1.example.com:2379 \
--cacert /etc/etcd/ca.crt --cert /etc/etcd/server.crt \
--key /etc/etcd/server.key endpoint health
# Überprüfen der Mitgliederliste und des Leaders
ETCDCTL_API=3 etcdctl --endpoints https://etcd1.example.com:2379 \
--cacert /etc/etcd/ca.crt --cert /etc/etcd/server.crt \
--key /etc/etcd/server.key member list
# Wenn das Quorum verloren geht (kein Leader), greift last-nodes-standing automatisch
# Bei eingebettetem etcd ist kein manuelles Eingreifen erforderlich
Don't wait for a real disaster — test failover regularly.
Test embedded etcd failover:
# In einem 3-Knoten-Cluster den Leader-Knoten beenden
sudo k3s kubectl get nodes -o wide
sudo k3s kubectl get cs # Überprüfen, welcher Knoten der etcd-Leader ist
sudo systemctl stop k3s # Stoppen auf dem Leader-Knoten
# Überprüfen, ob die verbleibenden Knoten einen neuen Leader wählen (sollte 5-10 Sekunden dauern)
sudo k3s kubectl get nodes # Auf einem der verbleibenden Knoten
# Gestoppten Knoten neu starten
sudo systemctl start k3s
sudo k3s kubectl get nodes # Sollte automatisch wieder beitreten
Test PostgreSQL failover (Patroni):
# Aktuellen PostgreSQL-Leader prüfen
sudo patronictl -c /etc/patroni/patronictl.yaml list
# Switchover (manueller Failover)
sudo patronictl -c /etc/patroni/patronictl.yaml switchover \
--master node1 --candidate node2
# Überprüfen, ob k3s mit dem neuen Leader verbindet
sudo k3s kubectl get nodes
Test disaster recovery from backup:
# Cluster stoppen
sudo systemctl stop k3s # Alle Knoten
# Auf einem Knoten aus einem aktuellen Snapshot wiederherstellen
sudo k3s etcd-snapshot restore --name backup-20260711-020000
# Wiederhergestellten Knoten starten
sudo systemctl start k3s
# Andere Knoten mit demselben Token hinzufügen (sie treten dem wiederhergestellten Cluster bei)
# Auf neuen Knoten:
sudo curl -sfL https://get.k3s.io | \
INSTALL_K3S_EXEC="server --token my-cluster-token \
--server https://10.0.1.10:6443" \
sh -
Symptom: k3s nodes (running embedded etcd) get stuck in startup loop, logs show request header size too large or snap: failed to recover database.
Root Cause: etcd data directory size grows large (1-2GB) with thousands of pod events. When restoring snapshots or restarting, etcd must load entire data directory into memory before accepting connections. At ~2500 nodes, this exceeds available RAM on small VMs.
Workaround:
# etcd-Historie komprimieren (behält nur den aktuellen Zustand)
ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2379 \
compact $(ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2379 endpoint status --write-out="json" | jq -r '.[0].Status.header.revision')
# Defragmentieren (Speicherplatz freigeben)
ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2379 defrag
# Snapshot (erstellt eine saubere Kopie)
sudo k3s etcd-snapshot save --name clean-snapshot
Langfristige Lösung: Externes etcd mit dediziertem Speicher, oder die Pod-Churn-Rate begrenzen und Event-Throttling aktivieren.
Symptom: Pods starten nicht mit dem Fehler FailedCreatePodSandBox, die Logs zeigen runtime network not ready.
Ursache: k3s v1.36.0 enthielt eine Kubernetes-Regression, bei der die containerd CRI die Pod-Netzwerk-Sandbox auf bestimmten Kernel-Versionen (5.15+ mit cgroup v2) nicht initialisieren kann.
Workaround: Upgrade auf k3s v1.36.1+, in dem dies behoben wurde:
sudo apt install -y k3s=1.36.1+k3s1
# or
sudo curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.36.1+k3s1 sh -
Alternative: Downgrade auf v1.35.x, falls ein sofortiges Upgrade nicht möglich ist.
Symptom: k3s stürzt auf Nodes ohne Default Route wiederholt ab (z. B. Multi-Homed Edge-Devices mit spezifischem Routing).
Ursache: k3s geht davon aus, dass eine 0.0.0.0/0 Default Route für das Cluster-Networking existiert. Wenn diese fehlt, schlagen API-Server-Bindings oder die Pod-CIDR-Zuweisung fehl.
Workaround:
# Add explicit CIDR routes (even if no default route)
ip route add 10.42.0.0/16 via 192.168.1.1 dev eth0
ip route add 10.43.0.0/16 via 192.168.1.1 dev eth0
# Or add dummy default route
ip route add default via 192.168.1.1 dev eth0 metric 999
Langfristige Lösung: Konfigurieren Sie k3s mit expliziten cluster-cidr und service-cidr, die mit Ihrem Routing übereinstimmen:
# /etc/rancher/k3s/config.yaml
cluster-cidr: 10.42.0.0/16
service-cidr: 10.43.0.0/16
node-ip: 10.0.1.10
Symptom: Kubernetes-Controller und Operator zeigen connection timeout zum API-Server, insbesondere in Clustern mit hunderten von Nodes.
Ursache: Die Standard-Verbindungslimits des k3s API-Servers (maximale gleichzeitige Verbindungen) sind für Single-Node- oder kleine HA-Cluster optimiert. Ab 500+ Nodes überschreiten die Controller-Watch-Verbindungen diese Limits.
Workaround:
# /etc/rancher/k3s/config.yaml
kube-apiserver-arg:
- max-mutating-requests-inflight=3000
- max-requests-inflight=3000
- event-ttl=72h
kube-scheduler-arg:
- kube-api-qps=100
- kube-api-burst=100
kube-controller-manager-arg:
- kube-api-qps=100
- kube-api-burst=100
Starten Sie k3s nach der Konfigurationsänderung neu.
Symptom: k3s verbraucht 100 % CPU oder verursacht OOM-Kills auf einem Raspberry Pi 4 mit 4 GB RAM bei Betrieb von 20+ Pods.
Ursache: Die Standard-Resource-Requests von k3s sind für Server-Hardware optimiert. Edge-Devices mit begrenzten Ressourcen haben Schwierigkeiten mit kubelet containerd-Metriken, etcd-Compaction und kube-proxy iptables-Operationen.
Workaround:
# /etc/rancher/k3s/config.yaml
kube-apiserver-arg:
- default-request-timeout=5m
- max-request-bytes=3145728
kubelet-arg:
- container-log-max-files=3
- container-log-max-size=10M
- eviction-hard=memory.available_lessthan_200Mi,nodefs.available_lessthan_10%
- eviction-soft=memory.available_lessthan_500Mi
- eviction-soft-grace-period=memory.available=1m
kube-scheduler-arg:
- kube-api-qps=50
- kube-api-burst=100
Zusätzliches Tuning:
# Disable k3s features you don't need
sudo cat > /etc/rancher/k3s/config.yaml <<'EOF'
disable:
- traefik # If using custom ingress
- servicelb # If using MetalLB
- local-storage # If using Longhorn or Rook-Ceph
- metrics-server # If using external Prometheus
EOF
Nutzen Sie diese Tabelle, um die richtige Upgrade-, Backup- und HA-Strategie für Ihr produktives k3s-Deployment auszuwählen.
| Anforderung | Upgrade-Strategie | Backup-Methode | HA-Backend | RPO | RTO |
|---|---|---|---|---|---|
| Single Node, unkritisch | Manuelles Skript | Tägliche Snapshots | N/A | 1 Tag | 4 Std. |
| 3 Nodes, Edge Retail | system-upgrade-controller | Stündliche Snapshots + S3 | embedded etcd | 1 Std. | 15 Min. |
| 10 Nodes, industrielle Steuerung | Blue-green | S3 Offload bei Änderung | external etcd | 5 Min. | 5 Min. |
| 50 Nodes, Multi-Site | system-upgrade-controller | Streaming Replica | PostgreSQL + Patroni | 1 Min. | 1 Min. |
| 100+ Nodes, Cloud | Cluster API provider for k3s | PITR-Backups | PostgreSQL synchron | Sekunden | Sekunden |
k3s etcd-snapshot save --s3 einrichten und Restore verifizierenk3s ist produktionsreif, wenn Sie es mit der gleichen operationalen Disziplin behandeln wie ein vollständiges Kubernetes – Upgrades, Backups und Disaster Recovery sind nicht optional.