Zum Inhalt

Edge-Containerisierung & Leichtgewichtiges K3s-Clustering

Moderne Softwarearchitekturen im industriellen Edge-Bereich betreiben Anwendungen längst nicht mehr monolithisch direkt auf dem Host-Betriebssystem. Stattdessen nutzen professionelle Edge-Umgebungen OCI-konforme Container (Docker und Podman) sowie leichtgewichtige Orchestratoren wie K3s (Lightweight Kubernetes), um Microservices sauber zu kapseln, Abhängigkeiten zu isolieren und unterbrechungsfreie Rolling Updates durchzuführen.

Der Betrieb von Kubernetes auf ARM-Einplatinencomputern verlangt jedoch ein striktes Ressourcenmanagement: Unbegrenzte Speichernutzung führt unweigerlich zu Kernel-OOM-Panics, und die Schreiblatenzen gewöhnlicher MicroSD-Karten zerstören das Quorum der internen Datenbank. Diese Anleitung liefert den praxiserprobten Leitfaden für performante Edge-Cluster auf Raspberry Pi 4 und 5.


1. Edge-Container-Engines: Docker vs. Podman auf ARM64

Bei der Wahl der Container-Laufzeitumgebung für Edge-Appliances stehen Sicherheitsarchitektur und systemd-Integration an erster Stelle:

%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
    subgraph DockerArch["Docker (Daemon-Basiert)"]
        direction TB
        D_CLI["docker CLI"] -->|REST API| D_SOCK["/var/run/docker.sock"]
        D_SOCK --> D_DAEMON["dockerd (Root-Daemon)"]
        D_DAEMON --> D_SHIM["containerd-shim"]
        D_SHIM --> D_CONT["Container (Root)"]
    end

    subgraph PodmanArch["Podman (Daemonless & Rootless)"]
        direction TB
        P_CLI["podman CLI"] -->|Fork / Exec| P_FORK["conmon (Prozess-Monitor)"]
        P_SYS["systemd Unit"] -.->|Ueberwacht| P_FORK
        P_FORK --> P_CONT["Container (User-UID)"]
    end

    DockerArch -->|"Edge-Alternative"| PodmanArch

Gegenüberstellung der Architekturen

Kriterium Docker CE Podman
Hintergrunddienst Zentraler Daemon (dockerd) Daemonless (Direkter conmon Prozess)
Rechte-Modell Läuft mit Root-Rechten Natives Rootless (Unprivilegierter Benutzer)
systemd-Integration Eigene Restart-Policies Direkte Einbindung über systemd Units (Quadlets)
RAM-Overhead im Leerlauf ca. 90 MB RAM für den Daemon 0 MB RAM (Kein dauerhafter Hintergrunddienst)
Einsatzbereich Entwickler-Systeme & K3s-Backend Abgesicherte Edge-Appliances & Gateways

Zwingende Voraussetzung: 64-Bit ARM (aarch64)

Verwende für Container und Kubernetes ausnahmslos 64-Bit-Versionen von Raspberry Pi OS. Praktisch alle relevanten Infrastruktur-Container (PostgreSQL, Redis, Envoy, K3s, InfluxDB) haben die Unterstützung für 32-Bit ARM (armhf) auf Docker Hub und GitHub Container Registry eingestellt.


2. Kernel-Voraussetzungen: cgroups v2 & Speicher-Accounting

Bevor K3s oder Docker installiert werden können, muss der Linux-Kernel angewiesen werden, cgroups v2 mit Speicher-Accounting zu aktivieren. Ohne diese Kernel-Parameter können Container-Laufzeitumgebungen keine RAM-Grenzen durchsetzen, was zum sofortigen Absturz des Kubernetes-Agenten führt.

Kernel-Bootzeile anpassen

Datei /boot/firmware/cmdline.txt bearbeiten:

sudo nano /boot/firmware/cmdline.txt

Folgende Parameter am Ende der bestehenden Zeile anhängen (keine neue Zeile beginnen!):

cgroup_memory=1 cgroup_enable=memory

Beispiel:

console=serial0,115200 console=tty1 root=PARTUUID=7c8d9e0f-02 rootfstype=ext4 fsck.repair=yes rootwait cgroup_memory=1 cgroup_enable=memory

Speichern und das System neu starten:

sudo reboot

Speicher-cgroups überprüfen

Prüfen, ob die Parameter vom Kernel geladen wurden:

cat /proc/cgroups | grep memory

Erwartete Ausgabe (dritte Spalte muss eine 1 sein):

memory  2   245 1


3. Leichtgewichtiges Kubernetes: K3s-Architektur

Ein Standard-Kubernetes (K8s) verbraucht allein für seine Control-Plane (etcd, kube-apiserver, kube-scheduler, kube-controller-manager) mehrere Gigabyte RAM. K3s, entwickelt von Rancher Labs (SUSE) und zertifiziert von der CNCF, bündelt alle Komponenten in einer einzigen Binärdatei unter 60 MB.

%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
    subgraph ControlPlane["K3s Server-Node (rpi-node01)"]
        direction TB
        API["k3s API Server / SQLite"]
        KUBE_S["Kubelet + Containerd"]
        API --> KUBE_S
    end

    subgraph Worker1["K3s Agent 1 (rpi-node02)"]
        direction TB
        KUBE_A1["Kubelet + Containerd"]
    end

    subgraph Worker2["K3s Agent 2 (rpi-node03)"]
        direction TB
        KUBE_A2["Kubelet + Containerd"]
    end

    ControlPlane ==>|TLS Port 6443| Worker1
    Worker1 --> Worker2

Warum K3s ideal für den Raspberry Pi ist

  1. Leichtgewichtige Datenbank: Ersetzt das ressourcenintensive etcd auf Einzelknoten durch eine integrierte SQLite-Engine (ermöglicht dennoch eingebettetes etcd für 3-Knoten-HA-Cluster).
  2. Entfernte Altlasten: Entfernt veraltete Cloud-Provider-Treiber zugunsten moderner, schlanker CSI-Schnittstellen.
  3. Geringer Speicherverbrauch: Die Master-Control-Plane belegt weniger als 512 MB RAM, Worker-Knoten begnügen sich mit ca. 150 MB RAM.

4. Single-Node K3s-Installation & Produktions-Tuning

Um den verfügbaren Arbeitsspeicher auf dem Raspberry Pi optimal für eigene Anwendungen zu nutzen, wird K3s unter Deaktivierung ressourcenintensiver Standardkomponenten (wie dem integrierten Alt-Traefik) installiert.

Installationsbefehl auf dem Master-Node (rpi-node01) ausführen:

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik --disable servicelb --write-kubeconfig-mode 644" sh -

Erklärung der Parameter

  • --disable traefik: Deaktiviert den integrierten Traefik-Ingress. (In der Produktion empfiehlt sich die gezielte Installation eines modernen Ingress per Helm).
  • --disable servicelb: Deaktiviert den Klipper-Load-Balancer, um Ports für professionelle Lösungen wie MetalLB freizuhalten.
  • --write-kubeconfig-mode 644: Erlaubt normalen Benutzern die Ausführung von kubectl-Befehlen ohne sudo.

Cluster-Status überprüfen

Prüfen, ob der Knoten betriebsbereit ist:

kubectl get nodes -o wide

Ausgabe:

NAME         STATUS   ROLES                  AGE   VERSION        INTERNAL-IP     OS-IMAGE                         KERNEL-VERSION
rpi-node01   Ready    control-plane,master   45s   v1.30.2+k3s1   192.168.10.51   Debian GNU/Linux 12 (bookworm)   6.6.31+rpt-rpi-2712

Laufende System-Pods kontrollieren:

kubectl get pods -A

5. Multi-Node-Cluster: Worker-Knoten hinzufügen

Um die Ausfallsicherheit und Rechenkapazität zu erhöhen, können weitere Raspberry Pis (rpi-node02, rpi-node03) als Worker-Knoten hinzugefügt werden.

Schritt 1: Cluster-Token abrufen

Auf dem Master-Knoten (rpi-node01) den Sicherheitstoken auslesen:

sudo cat /var/lib/rancher/k3s/server/node-token

Beispiel-Token:

K10398f7e2d9a8c...::server:7b2a9e4d018fca65b21

Schritt 2: Worker-Node beitreten lassen

Per SSH auf dem Worker-Knoten (rpi-node02) einloggen (sicherstellen, dass cgroup_memory=1 aktiv ist) und den K3s-Agent-Installationsbefehl ausführen:

curl -sfL https://get.k3s.io | K3S_URL=https://192.168.10.51:6443 \
  K3S_TOKEN="K10398f7e2d9a8c...::server:7b2a9e4d018fca65b21" \
  sh -

Schritt 3: Cluster-Status kontrollieren

Zurück auf dem Master-Knoten prüfen, ob alle Knoten im Cluster registriert sind:

kubectl get nodes

Erwartete Ausgabe:

NAME         STATUS   ROLES                  AGE     VERSION
rpi-node01   Ready    control-plane,master   10m     v1.30.2+k3s1
rpi-node02   Ready    <none>                 2m15s   v1.30.2+k3s1
rpi-node03   Ready    <none>                 45s     v1.30.2+k3s1


6. Produktions-Workload mit strikten Ressourcen-Limits

Auf Embedded-Systemen kann ein einziger fehlerhafter Container mit Speicherleck das gesamte Betriebssystem lahmlegen. Jedes Deployment auf einem Raspberry Pi Edge-Cluster muss zwingend CPU- und Memory-Limits definieren.

Manifest edge-sensor-collector.yaml erstellen:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: edge-sensor-collector
  namespace: default
  labels:
    app: sensor-telemetrie
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sensor-telemetrie
  template:
    metadata:
      labels:
        app: sensor-telemetrie
    spec:
      containers:
      - name: collector
        image: alpine:latest
        command: ["/bin/sh", "-c"]
        args:
          - while true; do
              echo "[$(date -u +%FT%TZ)] Telemetrie-Heartbeat von Pod auf $(hostname)" >> /tmp/telemetrie.log;
              sleep 5;
            done
        resources:
          requests:
            cpu: "50m"       # 5% eines CPU-Kerns garantiert reserviert
            memory: "32Mi"   # 32 MB Arbeitsspeicher garantiert reserviert
          limits:
            cpu: "250m"      # Maximal 25% eines Kerns zulaessig
            memory: "64Mi"   # Maximal 64 MB RAM (bei Ueberschreitung OOMKilled)
        livenessProbe:
          exec:
            command:
            - test
            - -f
            - /tmp/telemetrie.log
          initialDelaySeconds: 10
          periodSeconds: 15

Workload im Cluster bereitstellen:

kubectl apply -f edge-sensor-collector.yaml

Pod-Verteilung auf die Nodes überprüfen:

kubectl get pods -o wide

7. Speicher-Flaschenhals: NVMe vs. MicroSD bei K3s-Clustern

Wird ein Hochverfügbarkeits-Cluster mit 3 Master-Knoten und eingebettetem etcd betrieben, dürfen keinesfalls gewöhnliche MicroSD-Karten als Datenträger verwendet werden.

Das etcd-Latenzproblem

Das etcd-Konsensprotokoll setzt voraus, dass synchrone Schreibvorgänge (fsync) in unter 10 Millisekunden abgeschlossen werden. - MicroSD-Karten erreichen bei paralleler Last oft nur Latenzen zwischen 50 ms und 250 ms. - Überschreitet etcd das Timeout, verliert der Cluster seine Leader-Wahl, Knoten fallen auf NotReady und der Cluster friert ein. - Lösung: Betreibe K3s-Master immer auf M.2 NVMe SSDs über PCIe Gen 2/3 (Latenzen typischerweise < 0,8 ms).


Nächste Schritte

Bringe deine Edge-Infrastruktur zur Vollendung, indem du physische lokale Festplatten komplett überflüssig machst: