Internet der Dinge: Hochverfügbare Anwendungen mit MQTT

Anwendungen im IoT lassen sich unterschiedlich skalierbar und hochverfügbar gestalten. Der richtige Weg hängt vom Einzelfall ab.​

In Pocket speichern vorlesen Druckansicht 4 Kommentare lesen

(Bild: metamorworks / shutterstock.com)

Lesezeit: 15 Min.
Von
  • Andreas Schiffler
Inhaltsverzeichnis

Wie schnell IoT im Alltag angekommen ist, zeigen die Mobilitätsangebote von Tier, Bolt und anderen Anbietern. Etwa 500.000 E-Scooter und Fahrräder sollen in europäischen Städten auf den Straßen verfügbar sein, die Interessierte per App finden und ausleihen können.

Der Anbieter Bolt konnte nach eigenen Angaben die Anzahl der Fahrten im Jahr 2021 gegenüber dem Vorjahr um 400 Prozent steigern (Bolt Safety Report 2022). Solche Roller oder Fahrräder sind informationstechnisch vernetzt und melden unter anderem ihren Batterieladezustand und Standort an zentrale Stellen. Diese Funktionen nutzen das IoT-Standardprotokoll MQTT.

Weitere Einsatzbereiche für MQTT sind Smart Buildings, intelligente Straßenbeleuchtung oder Parkplatz-Statusinformationen. Die Zahl der IoT-Anwendungen für die Grundversorgung wächst stetig. Das erfordert eine nahezu hundertprozentige Verfügbarkeit, analog zu digitalen Diensten wie Online-Banking, Messenger-Anwendungen oder kontaktlosem Zahlungsverkehr.

Der Ausdruck Hochverfügbarkeit (High Availability, HA) hat sich zur Standardanforderung für den Betrieb kritischer Systeme entwickelt. Die oft verwendete Bezeichnung Four-Nines-Verfügbarkeit steht für eine Verfügbarkeit von 99,99 Prozent. Für Cloud-Infrastruktur-Anbieter wie Amazon Web Services oder Microsoft Azure sind derart spezifizierte Verfügbarkeiten Teil der Vertragsunterlagen.

Die Heise-Konferenz zum IoT

Am 26. und 27. April findet die achte Auflage der building IoT 2023 in München statt. Die Fachkonferenz richtet sich an diejenigen, die Anwendungen und Produkte für das Internet der Dinge erstellen.

In diesem Jahr steht das Thema IIoT noch stärker im Fokus als in den vergangenen Jahren. Das Programm bietet unter anderem Vorträge zur Datenanalyse für das IIoT, Zeitreihendatenbanken, sicheres Edge-Computing mit Kubernetes und EU-Normen für Cybersecurity.

Um eine Four-Nines-Verfügbarkeit zu verdeutlichen, kann man grob eine zu akzeptierende Unterbrechung von etwa fünfzig Minuten pro Betriebsjahr annehmen. Der HA-Ansatz ermöglicht somit aus Nutzersicht einen unterbrechungsfreien und konsistenten Betrieb von Diensten. Das bedeutet im IoT-Fall vor allem den Informationsaustausch über das MQTT-Protokoll.

Das Bereitstellen von Daten oder Webseiten ist im Sinne der Verfügbarkeit durch Zonenreplikation, Lastausgleich und automatische Skalierung Stand der Technik. Will man MQTT-Brokern jedoch hochverfügbar machen, sind protokollspezifische Eigenschaften zu berücksichtigen. Es ist eine wesentliche Grundlage des Protokolls, dass die Verbindung zwischen Broker und Client anders als bei Webseitenanfragen dauerhaft aufrechterhalten bleibt. Weiterhin speichern die Broker die Informationen, die beim Verbindungsaufbau und während der Verbindung entstehen, als Session-Informationen oder kurz Sessions. Diese enthalten unter anderem die Informationen zu den Topics, die Clients abonniert haben (Subscriptions), Nachrichten, die sich in der Warteschlange befinden (Queued Messages) und die Information zum letzten Willen (Last Will) für den Fall, dass ein System nicht mehr erreichbar ist.

Ferner ermöglicht das Protokoll, zurückgehaltene Nachrichten (Retained Messages) im Broker zu hinterlegen. Solche Nachrichten müssen zusammen mit den client-spezifischen Sessions im Fall eines Verbindungsabbruchs, Broker-Fehlers oder Netzwerk-Routenwechsels persistent zur Verfügung stehen.

Folglich muss ein HA-MQTT-Dienst im Falle einer Störung die folgenden Funktionen durchgängig und mindestens mit einer Verfügbarkeit von beispielsweise 99,99 Prozent, sicherstellen:

  • Wiederaufnehmen der unterbrochenen Client-Broker-Verbindung: Der MQTT-Broker kennt die Autorisierung (Access Control Lists) und Authentifizierung (Credentials und gegebenenfalls TLS Session) der Clients.
  • Der Broker muss Themenabonnements (Subscriptions) und noch nicht zugestellte Nachrichten in der Warteschlange (Queued Messages mit QoS > 0) des Clients bereitstellen beziehungsweise senden.
  • Er muss aufbewahrte Nachrichten (Retained Messages inklusive Last Will) bereithalten beziehungsweise senden.

Zusammengefasst handelt es sich um client- und verbindungsspezifische persistente Daten.

Um HA-Dienste umzusetzen, replizieren Unternehmen typischerweise virtuelle Maschinen oder Container (Nodes) mit identischen Applikationen in Clustern, die sie in unterschiedlichen Verfügbarkeitszonen betreiben. Infrastrukturanbieter bieten in der Region Central Europe in der Regel drei Verfügbarkeitszonen an. Eine Zone ist ein physisch separiertes Rechenzentrum innerhalb einer Region. Ein zonenredundanter Load Balancer sorgt für die Erreichbarkeit und die gleichmäßige Auslastung der einzelnen Nodes über Zonengrenzen hinweg. Aus Sicht des Clients ist stets ein identischer öffentlicher Endpunkt erreichbar. Replikationsregeln und eine Skalierungsregel (Auto Scaling) passen die Anzahl der Nodes an. Meist legen die Betreiber eine Mindest- und eine Maximalanzahl fest.

Ein Hochverfügbarkeits-Cluster bietet einen Lastenausgleich und Replikationsregeln, um mehrere Knoten redundant zu betreiben (Abb. 1).

Regelgrößen für die Skalierung sind im Wesentlichen die Zahl der Verbindungen oder die Last an einzelnen Nodes. Statusinformationen (Healthiness Probes) und zyklisch gesendete Lebenszeichen (Heartbeats) sorgen dafür, dass man die einzelnen Nodes überwachen und bei einem Ausfall oder Überlastung durch die Replikationen ersetzen kann. Alle Nodes sind zonenunabhängig Teil eines gemeinsamen virtuellen Netzwerks.

Diese Aufstellung kann eine gleichbleibende hohe Performance garantieren, die weitgehend unabhängig davon ist, ob es zehn, tausend oder eine Million Clientanfragen gibt. Solche Architekturen sorgen theoretisch für eine Verfügbarkeit von Five Nines. Allerdings gilt dabei die Annahme, dass die einzelnen Nodes als Einzelgänger agieren und sich im Fehlerfall durch neu gestartete Replikate im Cluster ersetzen lassen. Es müssen keine Daten zwischen Nodes ausgetauscht werden.

Im Gegensatz dazu genügt es für einen hochverfügbaren MQTT-Broker jedoch nicht, ein oder mehrere Replikate des Brokers bereitzustellen, da die protokollspezifischen Funktionen stets erfüllt sein müssen. Das bedeutet insbesondere, dass beim Ersetzen eines ausgefallenen Knotens die client- und verbindungsspezifischen persistenten Daten verfügbar sein müssen, um die Verbindung wiederaufzunehmen. Um das sicherzustellen, kann man erstens eine feste Zahl von Replikaten statt Auto Scaling verwenden. Zweitens lässt sich eine zusätzliche Kommunikation zwischen den Nodes einrichten, die stets die persistenten Daten synchronisiert, um alle Broker mit dem MQTT-Active-Active-Cluster-Konzept auf demselben Stand zu halten.

Für die Umsetzung haben sich zwei Konzepte etabliert. Das erste ist dadurch gekennzeichnet, dass alle Nodes im Cluster aktiv sind und Verbindungen aufbauen, halten und beenden können. Für dieses Active-Active-Konzept existieren mindestens zwei identische Nodes, die untereinander persistente Daten synchronisieren. Um den Betrieb im Cluster zu überwachen, gilt es zudem, Statusinformationen auszutauschen und pro Knoten einen Diagnoseendpunkt bereitzustellen. Letztere sorgen dafür, dass stets die eingestellte Anzahl Replikationen aktiv ist. Als Diagnoseendpunkt dient häufig ein Webservice in Form einer REST API, der im simpelsten Fall mit einem HTTP-Statuscode wie 200 - OK antwortet.

Im Active-Active-Cluster-Konzept sind alle beteiligten Knoten gleich gestellt aktiv und verfügen über synchronisierte Informationen (Abb. 2).

Ein Cluster-Management-Dienst wie Kubernetes oder Docker Swarm kann sich darum kümmern, die Replikation regelmäßig abzurufen beziehungsweise zu erstellen und zu starten. Alternativ können die einzelnen Knoten mit implementierter Cluster-Management-Funktion die Aufgabe übernehmen, beispielsweise bei Docker-Deployments oder Bare-Metal-Setups. Dabei überprüft jeder Knoten alle anderen.

Ein weiterer wesentlicher Bestandteil ist der Lastenausgleich (Load Balancing) zwischen den einzelnen Knoten. Als Lastkriterium gilt die Zahl der Verbindungen, die ein Knoten mit Clients aufbaut, oder die CPU-Nutzung des Knotens. Die übliche Konfiguration versucht daher, die Zahl der Verbindungen für alle Knoten gleich zu verteilen. Der Load Balancer bildet für jedes Paket in einer Verbindung einen Hash-Wert basierend mindestens auf Protokolltyp, Zieladresse, Ziel-Port, Quelladresse und Quell-Port.

Das stellt sicher, dass der Load Balancer Pakete von einem bestimmten Client an exakt einen Knoten sendet. Die Verteilung des Datenverkehrs erfolgt somit nur zwischen verschiedenen Verbindungen und nicht für Pakete innerhalb derselben. Bricht eine Verbindung ab oder erreicht eine inaktive Verbindung ihre Ablaufzeit, setzt das System sie zurück (Connection Reset by Server/Client). Eine frische Verbindung kann eine neue Zuordnung zwischen Client und Knoten bedeuten.

Das MQTT-Protokoll nutzt die Möglichkeit, die Verbindung zwischen Broker und Client aufrechtzuerhalten, als eine Basisfunktion. Der Parameter Keep Alive definiert, wie lange der Broker die Verbindung ohne Datenverkehr aufrechterhalten soll, damit Clients beispielsweise abonnierte Nachrichten empfangen.

Daher sollte der Zeitablauf für inaktive Verbindungen im Load Balancer mindestens dem Wert entsprechen, der als Keep Alive definiert ist. Andernfalls verliert der Client seine Verbindung. Das ist zwar kein Fehler, aber es kommt zu einer neuen Zuordnung zwischen Client und Knoten. Das bedingt eine Aktualisierung der persistenten Daten, die zwischen den Knoten synchronisiert werden müssen.

Im Active-Active-Szenario wächst die Zahl der Verbindungen zwischen den Knoten quadratisch im Verhältnis zu der Zahl der Replikationen. Da der Datenverkehr und somit die Kosten zwischen den Zonen gleichermaßen steigen, ist eine detaillierte Abstimmung notwendig, um eine sinnvolle Balance zwischen Kosten und Leistung zu erreichen. Ein Knoten muss beispielsweise bei drei Replikationen drei Verbindungen halten. Bei vier Replikationen wären es sechs und bei fünf Replikationen schon zehn Verbindungen.

Eingangsgrößen zum Einrichten eines Active-Active-Clusters sind die Zahl der zu erwartenden Clientverbindungen, die maximale Anzahl Clients pro Knoten und die Einstellungen des Lastenausgleichs – insbesondere Keep Alive. Die Performance skaliert durch eine höhere Zahl von Knoten im Cluster (Horizontal Scaling). Weiterhin gilt es zu prüfen, ob eine Zonenredundanz mit ihren zusätzlichen Kosten notwendig ist. Sie entscheidet über die theoretische Höhe der Verfügbarkeit. Active-Active bedeutet, dass alle Knoten aktiv sind, Rechenleistung konsumieren und Datenverkehr erzeugen und damit Kosten verursachen.

Der Active-Passive-Cluster setzt auf ein anderes Konzept für hohe Verfügbarkeit. Dabei betreibt man immer eine ungerade Anzahl Knoten, die zwar alle Replikate sind, aber in unterschiedlichen Rollen agieren. Kerngedanke ist der Algorithmus für konsensfähige Resilienz gegen zukünftige Ausfälle (Resilience Against Future Threats, kurz Raft Consensus Algorithm). Angewandt auf Server-Cluster definiert er die Knoten in Rollen: Anführer (Leader), Kandidat (Candidate) und Mitläufer (Follower). Der Anführer ist aktiv und nimmt Verbindungen vom Lastenausgleich an, die Mitläufer sind passiv und nehmen keine Verbindungen von außen an.

Im Active-Passive-Cluster-Konzept wird nur ein Knoten aktiv betrieben und es erfolgt ein Informationsaustausch zu Lebenszeichen und Auswahl des aktiven Knotens (Abb. 3).

Auf jedem Knoten ist ein passendes Protokoll für den Raft-Algorithmus implementiert, um einen neuen Anführer zu definieren, wenn der aktive ausfällt. Jeder Mitläuferknoten startet eine Ablaufzeit, sobald er ein Lebenszeichen vom Anführer erhalten hat. Die Ablaufzeit wählt er zufällig in einem Bereich von beispielsweise 100 bis 200 Millisekunden. Erhält ein Mitläufer kein Lebenszeichen vom Anführer, sendet er ein Lebenszeichen an alle anderen Knoten und kündigt an, dass er die Rolle des Anführers übernehmen kann. Damit wird er zum Kandidaten.

Erreicht diese sogenannte Leader-Election-Nachricht andere Knoten, bevor deren individuelle Ablaufzeit verstrichen ist, werden sie eine passende Leader Election senden, um für den Kandidaten zu stimmen. Kommt eine Mehrheit zustande, was bei einer ungeraden Zahl von Knoten immer der Fall ist, ist der Kandidat in der Rolle des Anführers bestätigt und nimmt von nun an die Verbindungen vom Lastausgleich entgegen.

Den Raft-Algorithmus zu implementieren und die Knoten in drei unterschiedliche Verfügbarkeitszonen zu verteilen, gewährleistet eine hohe Verfügbarkeit. Ein ausführliche und anschauliche Beschreibung des Raft-Algorithmus findet man in einem c't-Artikel.

Anders als beim Active-Active-Konzept erfolgt die Verteilung der MQTT-spezifischen persistenten Daten nur unidirektional stets vom Anführer an die Mitläufer. Auch die Datenverbindungen für den Raft-Algorithmus sind im Regelbetrieb zwischen Anführer- und Mitläuferknoten aufgebaut, aber zwischen den Mitläuferknoten nicht erforderlich. Damit steigt beim Zufügen von Knoten die Zahl der Verbindungen linear.

Wenn ein Knoten ausgelastet beziehungsweise überlastet ist oder ausfällt, startet das Cluster-Management eine Replikation und sorgt dafür, dass stets die definierte Anzahl Knoten aktiv ist. Eine horizontale Skalierung erfolgt nicht. Das Cluster-Management und der Raft-Algorithmus laufen unabhängig voneinander. Der Lastenausgleich ist fest definiert und kennt alle Knoten, sodass im Falle eines Wechsels des Anführers der neue innerhalb kürzester Zeit die Verbindungen übernehmen kann. Die Mitläufer erhalten dafür stets aktuelle persistente Daten vom Anführerknoten, also dem aktiven MQTT-Broker.

Im Vergleich zum Active-Active-Konzept ist die maximale Leistung auf die Leistungsfähigkeit eines aktiven Knoten beschränkt. Steigt die Last durch eine wachsende Zahl von Verbindungen, lässt sich das System skalieren, indem man die Kapazität beispielsweise durch zusätzliche virtuelle CPU-Kerne und Netzwerkschnittstellen erhöht. Diese vertikale Skalierung lässt sich ebenso wie die horizontale über Autoskalierungsregeln feingranular definieren, um dynamisch und kosteneffizient auf wechselnde Lasten zu reagieren.

Diese Art von HA-MQTT-Cluster werden in der Pro Edition für den Mosquitto MQTT Broker von der Firma Cedalo angeboten. Darüber hinaus gibt es andere Unternehmen, die MQTT-Broker mit High Availability anbieten, wie VerneMQ. Jedes Produkt hat seine eigene Art der HA-Konfiguration, sodass die Konzepte von Pro Mosquitto nicht direkt auf VerneMQ und die anderen Anbieter übertragbar sind.

Am Ende bleibt die Frage, welches Cluster-Konzept zu einem spezifischen Anwendungsfall passt. Grundvoraussetzung ist, dass die Zahl der Zonen eine hohe Verfügbarkeit sicherstellt. Im Fall Active-Active sind es somit mindestens zwei Knoten, die jeweils in einer separaten Verfügbarkeitszone laufen, und für Active-Passive sind es mindestens drei Knoten, die auf drei Zonen verteilt sind. Bezüglich der Leistungsfähigkeit gilt zu berücksichtigen, dass im Fall Active-Active immer eine nicht zu unterschätzende Last durch die bidirektionale Synchronisierung der Daten im Cluster entsteht.

Die horizontale Skalierung durch mehr Replikationen ist ein häufiges Szenario, das sich einfach umsetzen lässt. Eine vertikale Skalierung des jeweils aktiven Knotens mit Active-Passive kann jedoch von Vorteil sein, wenn beispielsweise die Kosten zum Skalieren der Kapazität eines Knotens (vCPU, RAM) geringer sind als der Betrieb eines zusätzlichen Knotens.

Aus Sicht der Anwendung kann ein klares Entscheidungskriterium sein, wie die Kommunikation über MQTT genutzt wird. Erfolgt beispielsweise für den eingangs skizzierten Fall von E-Scootern kein Informationsaustausch zwischen den Clients, weil E-Scooter 1 nichts von E-Scooter 2 wissen muss, sind zwei unabhängige Active-Passive-HA-Cluster sinnvoll, da keine horizontale Skalierung notwendig ist. Es ist lediglich erforderlich, die Autorisierung und Authentifizierung zwischen den aktiven Anführerknoten, aber keine persistenten Daten zu synchronisieren. Durch den zonenredundanten Lastausgleich sind beispielsweise 500.000 Clients mit Cluster 1 und die restlichen 500.000 mit Cluster 2 verbunden. Auch wenn der Active-Active-Ansatz ebenso gut funktioniert, erzeugt die bidirektionale Synchronisierung zwischen den Knoten zusätzliche, überflüssige Last. Dieser Effekt wächst mit zusätzlichen Clientverbindungen überproportional.

Ein anderer Fall wäre ein performanter Informationsaustausch zwischen MQTT-Clients wie bei Messenger-Diensten (Peer to Peer). Hier erfolgt der Informationsaustausch direkt über den Broker ohne weitere Backend-Funktionalität. Steigt die Anforderung beziehungsweise Zahl der Verbindungen, sind bei extrem hohen Anforderungen (> 1.000.000 Verbindungen) die Grenzen für eine horizontale Skalierung weiter gesteckt als bei der vertikalen Skalierung im Active-Passive-Cluster.

Die Anwendung definiert die Anforderungen. Maßgeblich sind die Antworten auf die Fragen: Ist eine direkte Kommunikation zwischen Clients notwendig? Und wie viele Clientverbindungen werden benötigt? Die Antworten bestimmen, welcher Ansatz besser zur Anwendung passt.

Spielt die Skalierung beziehungsweise Performance gegenüber der Hochverfügbarkeit eine untergeordnete Rolle, entscheidet letztlich die Ausführung eines HA-MQTT-Angebots, bei der insbesondere auf die Verteilung in Verfügbarkeitszonen zu achten ist.

Dr. Andreas Schiffler
ist an der Technischen Hochschule Würzburg-Schweinfurt im Themengebiet Produktions- und Datentechnik im Maschinenbau als Forschungsprofessor berufen. Neben den Forschungsthemen rund um den 3D-Metalldruck, betreibt Dr. Schiffler im Rahmen der studentischen Ausbildung ein Kubernetes Cluster für die praxisorientierten Grundlagen zu IoT und Industrie 4.0.

(rme)