Ein Modell zu trainieren ist nur die halbe Arbeit: Um Wert zu schaffen, müssen Sie ML-Modelle auf Edge-Geräte deployen, sie aktuell halten und jederzeit wissen, welche Version auf welchem Gerät läuft. Dieser Leitfaden zeigt ein bewährtes Muster für Paketierung, Versionierung und Rollout von Modellen über eine ganze Flotte, bei dem MQTT Updates signalisiert und Statusmeldungen einsammelt, während die Modelldateien selbst über HTTPS übertragen werden.
Warum das Deployment von Modellen an der Edge schwierig ist
Ein Modell auf einen Server zu bringen ist ein einzelnes Deployment. Es auf Hunderte Gateways, Kameras oder Mikrocontroller zu bringen ist ein Problem verteilter Systeme:
- Geräte sind zu zufälligen Zeiten offline und müssen beim erneuten Verbinden aufholen.
- Die Hardware unterscheidet sich, sodass ein einzelnes Modellartefakt womöglich nicht auf jedes Gerät passt.
- Ein schlechtes Modell kann Vorhersagen unbemerkt verschlechtern, statt abzustürzen. Sie brauchen also Gesundheitssignale, die über „der Prozess läuft“ hinausgehen.
- Bandbreite und Speicher sind begrenzt, und manche Standorte liegen hinter Firewalls, die nur ausgehende Verbindungen zulassen.
MQTT passt gut zu diesen Bedingungen. Geräte halten eine einzige ausgehende Verbindung zu einem Broker, Retained Messages geben Nachzüglern sofort die neueste Anweisung, und eine Last-Will-Nachricht teilt Ihnen mit, wenn ein Gerät wegfällt. Wenn Ihnen diese Aufgabenteilung neu ist, ist unsere Übersicht, was ein MQTT-Broker macht, eine gute Einführung.
Schritt 1: Das Modell als versioniertes Artefakt paketieren
Behandeln Sie jedes Modell als unveränderliches Release, niemals als Datei, die überschrieben wird. Ein Modellpaket sollte Folgendes enthalten:
| Datei | Zweck |
|---|---|
model.tflite (oder .onnx, _edgetpu.tflite, .engine) |
Das kompilierte Modell für eine bestimmte Runtime |
labels.txt oder classes.json |
Namen der Ausgabeklassen in der richtigen Reihenfolge |
manifest.json |
Version, Runtime, Eingabeform, Vorverarbeitung, Zielhardware, Prüfsumme |
preprocess.json (optional) |
Normalisierungskonstanten, Skalierungsmodus, Kanalreihenfolge |
Abweichungen bei der Vorverarbeitung sind die häufigste Ursache für „das Modell funktioniert auf meinem Laptop, aber nicht auf dem Gerät“. Wenn Sie die erwartete Eingabegröße, Farbreihenfolge und Normalisierung im Manifest festhalten und das Gerät dies beim Laden prüft, verhindern Sie eine ganze Klasse unbemerkter Fehler.
Wählen Sie das Format je nach Ziel:
| Zielhardware | Typisches Format | Hinweise |
|---|---|---|
| Mikrocontroller (ESP32-S3, Cortex-M) | TensorFlow Lite Micro, int8 | Meist in die Firmware kompiliert; Aktualisierung per Firmware-OTA |
| Raspberry Pi / ARM-Gateway | TFLite oder ONNX Runtime | Lässt sich zur Laufzeit als Datei austauschen |
| Coral Edge TPU | _edgetpu.tflite |
Muss mit dem Edge TPU Compiler kompiliert werden |
| NVIDIA Jetson | TensorRT-Engine oder ONNX | Engines sind an GPU und TensorRT-Version gebunden, daher pro Gerätetyp bauen |
Bei Mikrocontrollern steckt das Modell oft im Firmware-Image, sodass ein Modell-Update ein Firmware-Update ist. Unser Tutorial zu TensorFlow Lite Micro behandelt diesen Fall; der Rest dieses Leitfadens konzentriert sich auf Geräte der Linux-Klasse, die Modelle von der Festplatte laden.
Schritt 2: Semantisch versionieren und jedes Release signieren
Verwenden Sie eine Versionskennung, die dem Betriebspersonal etwas sagt, etwa defect-classifier 2.3.0, bei der sich die Hauptversionsnummer ändert, wenn sich Ein- oder Ausgaben inkompatibel ändern. Berechnen Sie zusätzlich zur Version eine SHA-256-Prüfsumme des Artefakts und veröffentlichen Sie sie im Manifest. Geräte müssen eine Datei ablehnen, deren Prüfsumme nicht übereinstimmt. Für höhere Sicherheit signieren Sie das Manifest mit einem privaten Schlüssel, den Ihr CI-System verwahrt, und liefern nur den öffentlichen Schlüssel an die Geräte aus.
Speichern Sie Artefakte in einem HTTPS-Objektspeicher oder auf einem internen Dateiserver. MQTT ist der falsche Kanal für Binärdateien mit mehreren Megabyte: Der Broker müsste große Payloads im Speicher halten, und viele Pläne begrenzen die Nachrichtengröße. Der kostenlose Serverless-Plan von ConnectMQ zum Beispiel begrenzt Nachrichten auf 256 KB, was für ein Manifest reichlich ist, aber nicht für ein Bildverarbeitungsmodell.
Schritt 3: Updates über MQTT signalisieren
Die Steuerungsebene verwendet drei Topics pro Gerät (oder pro Gerätegruppe):
fleet/<site>/<device>/model/desired retained, written by the deployment service
fleet/<site>/<device>/model/reported retained, written by the device
fleet/<site>/<device>/model/events not retained, progress and errors
Der Deployment-Dienst veröffentlicht eine Nachricht mit dem Sollzustand als Retained Message, sodass ein Gerät, das offline ist, sie erhält, sobald es sich verbindet:
{
"model": "defect-classifier",
"version": "2.3.0",
"url": "https://models.example.com/defect-classifier/2.3.0/model.tflite",
"sha256": "9f2c4c1d5e8b0a7f3d6e1b2c4a5f6e7d8c9b0a1f2e3d4c5b6a7f8e9d0c1b2a3f",
"runtime": "tflite",
"rollout": "canary",
"issuedAt": "2026-10-16T08:00:00Z"
}
Das Gerät vergleicht den Sollzustand mit dem, was es ausführt, lädt herunter, prüft, tauscht aus und meldet zurück:
{
"model": "defect-classifier",
"version": "2.3.0",
"state": "active",
"previous": "2.2.1",
"loadedAt": "2026-10-16T08:03:12Z",
"selfTest": "passed"
}
Dieses Soll/Ist-Muster folgt derselben Idee wie Device Shadows und funktioniert mit jedem standardkonformen Broker.
In Industrie-4.0-Umgebungen, etwa bei einer kamerabasierten Qualitätsprüfung an der Fertigungslinie, hängen Sie diese Steuer-Topics sinnvollerweise in den Unified Namespace (UNS) ein. Dessen Topic-Baum folgt der ISA-95-Hierarchie Enterprise → Site → Area → Line → Cell, sodass aus fleet/<site>/<device> beispielsweise acme/werk-stuttgart/pruefung/linie-2/zelle-1/model/desired wird. Modellstatus und Maschinendaten liegen dann in derselben Struktur, und Rollouts lassen sich gezielt pro Werk, Bereich oder Linie steuern. Hintergründe dazu bietet der Beitrag zu Sparkplug B und Unified Namespace.
Schritt 4: Ein minimaler Update-Agent für Geräte
Der folgende Agent läuft auf einem Linux-Edge-Gerät. Er abonniert sein Soll-Topic, lädt das Modell herunter, prüft die Prüfsumme, führt einen Smoke-Test aus, tauscht das aktive Modell atomar aus und behält die vorherige Version für ein Rollback.
import hashlib
import json
import os
import shutil
import time
import urllib.request
from pathlib import Path
import paho.mqtt.client as mqtt
DEVICE = "gw-07"
BASE = f"connectmq/demo/a91e4d/fleet/plant1/{DEVICE}/model"
MODELS = Path("/opt/models")
ACTIVE = MODELS / "active" # symlink to the active version directory
STATE_FILE = MODELS / "state.json"
def current_version():
try:
return json.loads(STATE_FILE.read_text())["version"]
except (FileNotFoundError, KeyError, json.JSONDecodeError):
return None
def report(client, state, **extra):
body = {"model": extra.pop("model", None), "state": state,
"version": extra.pop("version", current_version()),
"ts": int(time.time() * 1000), **extra}
client.publish(f"{BASE}/reported", json.dumps(body), qos=1, retain=True)
def event(client, msg):
client.publish(f"{BASE}/events", json.dumps({"msg": msg, "ts": int(time.time() * 1000)}), qos=0)
def sha256_of(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(1 << 20), b""):
h.update(chunk)
return h.hexdigest()
def smoke_test(model_path):
# Load the model and run one inference on a stored sample input.
# Replace with your runtime, e.g. tflite_runtime.interpreter.Interpreter.
return model_path.stat().st_size > 0
def apply_update(client, desired):
version = desired["version"]
if version == current_version():
report(client, "active", model=desired["model"], version=version)
return
target = MODELS / version
target.mkdir(parents=True, exist_ok=True)
tmp = target / "model.tmp"
event(client, f"downloading {version}")
urllib.request.urlretrieve(desired["url"], tmp)
if sha256_of(tmp) != desired["sha256"]:
tmp.unlink(missing_ok=True)
report(client, "failed", model=desired["model"], reason="checksum mismatch")
return
final = target / "model.tflite"
tmp.replace(final)
if not smoke_test(final):
report(client, "failed", model=desired["model"], reason="smoke test failed")
return
previous = current_version()
link_tmp = MODELS / "active.tmp"
if link_tmp.exists() or link_tmp.is_symlink():
link_tmp.unlink()
link_tmp.symlink_to(target)
os.replace(link_tmp, ACTIVE) # atomic swap of the symlink
STATE_FILE.write_text(json.dumps({"version": version, "previous": previous}))
report(client, "active", model=desired["model"], version=version, previous=previous)
# Keep only the current and previous versions on disk
for d in MODELS.iterdir():
if d.is_dir() and not d.is_symlink() and d.name not in (version, previous):
shutil.rmtree(d, ignore_errors=True)
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(f"{BASE}/desired", qos=1)
report(client, "online")
def on_message(client, userdata, msg):
try:
apply_update(client, json.loads(msg.payload))
except Exception as exc: # report every failure instead of dying silently
report(client, "failed", reason=str(exc))
MODELS.mkdir(parents=True, exist_ok=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{DEVICE}-a91e4d")
client.on_connect = on_connect
client.on_message = on_message
client.will_set(f"{BASE}/reported", json.dumps({"state": "offline"}), qos=1, retain=True)
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()
Beachten Sie, dass der Download innerhalb des Nachrichten-Callbacks läuft, was die Netzwerkschleife blockiert. Für kleine Modelle und einen Testaufbau ist das akzeptabel; in der Produktion übergeben Sie die Arbeit an einen Worker-Thread, damit Keepalives auch bei langen Downloads weiterlaufen. Ihr Inferenzprozess sollte das Modell über den Symlink active öffnen und neu laden, wenn er sich ändert, zum Beispiel indem er die Statusdatei überwacht.
Um während des Testens einen Rollout auszulösen, veröffentlichen Sie eine Soll-Nachricht als Retained Message:
mosquitto_pub -h broker.connectmq.com -p 1883 -i deployer-a91e4d -r -q 1 \
-t "connectmq/demo/a91e4d/fleet/plant1/gw-07/model/desired" \
-f desired.json
Der öffentliche MQTT-Broker eignet sich, um den Ablauf auszuprobieren, doch denken Sie daran, dass er für jeden lesbar ist und nur für Tests, nicht für Produktionsdaten gedacht ist. Hinterlegen Sie dort also niemals echte Modell-URLs oder signierte Tokens.
Schritt 5: In Stufen ausrollen
Schicken Sie ein neues Modell niemals auf einen Schlag an die gesamte Flotte. Ein gestaffelter Rollout sieht so aus:
- Schattenbetrieb. Das neue Modell läuft auf einigen Geräten neben dem alten, und beide Ausgaben werden veröffentlicht, aber nur das alte löst Aktionen aus. Vergleichen Sie beide offline.
- Canary. Veröffentlichen Sie den Sollzustand an eine kleine Gruppe von Geräten. Beobachten Sie deren gemeldeten Zustand, die Inferenzlatenz und die Verteilung der Vorhersagen.
- Schrittweise Wellen. Erweitern Sie auf größere Gruppen und pausieren Sie zwischen den Wellen. Ein Deployment-Skript schreibt einfach das Soll-Topic für die nächste Gerätecharge.
- Vollständiger Rollout. Sobald die letzte Welle gesund ist, machen Sie die neue Version zum Standard für neu provisionierte Geräte.
- Rollback. Verschlechtern sich die Metriken, veröffentlichen Sie die vorherige Version als Sollzustand. Da das Gerät die letzte Version auf der Festplatte behält, braucht ein Rollback nicht einmal einen Download.
Die Gruppenzugehörigkeit kann in Ihrem Deployment-Dienst liegen, oder Sie kodieren sie in Topics, zum Beispiel fleet/plant1/canary/model/desired, das ein Gerät der Canary-Gruppe zusätzlich zu seinem eigenen Topic abonniert.
Überwachen, was jedes Gerät ausführt
Da jedes Gerät eine Retained Message auf reported veröffentlicht, liefert ein einziges Wildcard-Abonnement auf fleet/+/+/model/reported im Moment des Verbindens ein vollständiges Inventar der Flotte. Kombinieren Sie es mit Vorhersage-Telemetrie, um Drift zu erkennen: Verschieben sich die Klassenverteilung oder die Konfidenzwerte nach einem Update stark, sieht das Modell vermutlich Eingaben, für die es nicht trainiert wurde. Unser Artikel zu IoT MLOps geht tiefer auf Monitoring und Retraining-Schleifen ein.
Checkliste für die Produktion
- Artefakte sind unveränderlich, versioniert und auf HTTPS gespeichert.
- Jedes Manifest enthält eine SHA-256-Prüfsumme, idealerweise eine Signatur.
- Geräte prüfen, führen einen Smoke-Test aus und tauschen atomar aus, wobei sie eine vorherige Version behalten.
- Soll- und Ist-Zustand liegen auf MQTT-Topics mit Retained Messages, mit Last Will zur Offline-Erkennung.
- Rollouts verlaufen über Schattenbetrieb, Canary, Wellen und vollständigen Rollout, mit einem Rollback per einzelner Nachricht.
- Broker verwenden TLS auf Port 8883 und Zugangsdaten pro Gerät.
Für produktive Flotten übernimmt ein verwalteter MQTT-Broker Zertifikate, Skalierung und Authentifizierung, damit sich Ihr Team auf die Modelle konzentrieren kann. Bei ConnectMQ erhält jedes Gerät Zugangsdaten, deren Client ID an es gebunden ist, und der Live-Topic-Explorer im Portal zeigt Soll- und Ist-Nachrichten, während ein Rollout voranschreitet. Für den größeren Zusammenhang, wie Modelle und vernetzte Geräte zusammenspielen, lesen Sie unseren umfassenden Leitfaden zu KI und IoT.
Starten Sie mit ConnectMQ
ConnectMQ ist ein verwalteter MQTT-Broker, der MQTT 3.1.1 und 5.0 über TCP, TLS und WebSockets unterstützt und bereit ist, Ihre Signale für Modell-Updates zu transportieren. Registrieren Sie sich für den kostenlosen Serverless-Plan, ohne Kreditkarte, und richten Sie noch heute Ihren ersten Rollout ein.
Häufig gestellte Fragen
Kann ich die Modelldatei selbst über MQTT senden?
Kleine Dateien können Sie in Stücke aufteilen, aber das lohnt sich selten. Nutzen Sie MQTT für das Update-Signal und den Status und laden Sie das Artefakt über HTTPS herunter, wo fortsetzbare Downloads und Caching Standard sind.
Wie erhält ein Gerät, das offline war, das Update?
Da die Soll-Nachricht eine Retained Message ist, stellt der Broker sie sofort zu, wenn sich das Gerät wieder verbindet und abonniert. Das Gerät vergleicht dann die Versionen und aktualisiert bei Bedarf.
Wie mache ich ein fehlerhaftes Modell rückgängig?
Veröffentlichen Sie die vorherige Version als Sollzustand. Ein Gerät, das seine letzte Version auf der Festplatte behält, kann sofort zurückwechseln, ohne etwas herunterzuladen.
Verwenden Mikrocontroller denselben Ansatz?
Die Signalisierung ist dieselbe, doch das Modell ist meist in die Firmware kompiliert. Das Gerät führt daher ein Firmware-OTA-Update durch, statt eine Modelldatei auszutauschen.



