Talk notes

This commit is contained in:
2024-01-08 18:17:09 +01:00
parent 3c384205c9
commit 6cdc6c1c2b
17 changed files with 157 additions and 88 deletions

124
notes/NOTES.md Normal file
View File

@@ -0,0 +1,124 @@
# Millionen langlebiger TCP-Verbindungen Herausforderungen und Lösungen bei Update-Prozessen
Ihr pünktlich am Bahnhof, um in euren wohlverdienten Urlaub zu starten. Während ihr auf den verspäteten Zug wartet, habt ihr
jede Menge Zeit zu überlegen, ob ihr daheim wirklich die Herdplatte ausgeschaltet habt. Praktisch, wenn der Herd mit dem
Internet verbunden ist und ihr einfach in einer App nachschauen könnt, ob die Herdplatten wirklich abgeschaltet sind.
IoT (also "Internet of Things") beschreibt genau diese Art von Geräten. Ich bin Softwareentwickler mit Fokus auf
Softwarearchitektur bei Scandio. Bei Scandio entwickeln wir für unsere Kunden maßgeschneiderte intelligente und kreative
Softwarelösungen. Dabei haben wir ausgeprägten Stärken unter anderem im IoT-Bereich.
Ein Projekt in diesem Bereich will ich heute vorstellen. Dabei geht es um ein Gateway zwischen den IoT-Geräten unseres Kunden
und verschiedenen Backends und die Herausforderungen, die auftreten, wenn so ein System aktualisiert werden muss.
## Inhalt
Ein kurzer Überblick: Nach dem Projektsetup (also Hintergrund, Motivation und Ziel des Projekts) beschreibe ich die
Problemstellung und dann natürlich auch die Lösungen: wie sieht die Architektur des Systems jetzt ab, was genau passiert, wenn
das System Updates bekommt, und was wären unsere Wunschträume für die Architektur, wenn Zeit und Geld keine Rolle spielen würde
oder wir von vorne anfangen könnten.
## Projektsetup
Das Projekt, von dem ich heute rede, hat den Namen "Heimdall". Heimdall ist in der nordischen Mythologie der Wächter der
Götterbrücke Bifröst (der Regenbogen im Bild) und hat außergewöhnliche Wahrnehmungsfähigkeiten. Genauso wie Heimdall die
Verbindung zwischen den Welten überwacht, fungiert unser Heimdall-System als ein Gateway, das die Brücke zwischen einer Vielzahl
von IoT-Geräten und verschiedenen Backend-Systemen bildet. Ähnlich der mythischen Figur hat unser Heimdall die Aufgabe, die
Sicherheit, Stabilität und Effizienz dieser Verbindungen zu gewährleisten.
Heimdall wird seit 2018 entwickelt und hat ein vollständig extern entwickeltes und verwaltetes System abgelöst, auf das unser
Kunde keinerlei Zugriff hatte (weder auf Entwicklungsdetails noch auf den Source Code).
Eure smarte Herdplatte muss regelmäßig an das Herdplattenstatusbackend die Information senden, welche Herdplatten auf welcher
Stufe angeschaltet sind. Gleichzeitig schickt der smarte Kühlschrank eures Nachbarn Bilder an ein Backend, das herausfinden
soll, wie viele Eier noch im Kühlschrank sind, und die smarte Dunstabzugshaube eurer Tante meldet an das entsprechende Backend,
dass die Filter eine Reinigung brauchen. Heimdall sitzt zwischen den Geräten und den Backends und sorgt dafür, dass die
richtigen Nachrichten das richtige Backend erreichen, und dass umgekehrt auch beispielsweise das Herdplattenbackend die
Herdplatte anweisen kann, sich abzuschalten.
## Problemstellung
Die Geräte halten eine Websocket-Verbindung zu Heimdall aufrecht. Über diese Verbindungen werden Nachrichten ausgetauscht. Das
wichtigste Qualitätsziel bei Heimdall ist, dass diese Verbindungen zwischen Gerät und Heimdall so selten wie möglich
unterbrochen werden. Das hat zwei Gründe: Zum einen kann ich als Nutzer nicht sehen, auf welcher Stufe mein Herd gerade kocht,
wenn der Herd nicht mit Heimdall verbunden ist. Zum anderen finden einige Prozesse nach jeder Neuverbindung statt, was
potenziell zu Überlastung von Heimdall oder den Backends führen könnte, wenn die Neuverbindungen zu häufig stattfinden.
Es gibt ein Szenario, in dem Verbindungen nicht gehalten werden können: nämlich wenn der Server, mit dem die Hausgeräte
verbunden sind, ein Update bekommt und daher ersetzt werden muss. Daher ist das Ziel, dass solche Neustarts bei den meisten
notwendigen Konfigurationsänderungen des Systems nicht notwendig sind.
## Lösungen
Wir schauen uns jetzt mal an, wie Heimdall aufgebaut ist.
Das Kernstück von Heimdall ist der "Web Socket Manager", kurz WSM. Im Wesentlichen öffnet ein Gerät eine Websocket-Verbindung
zum WSM. Der WSM leitet die Nachrichten des Geräts an das Backend weiter und umgekehrt werden Nachrichten vom Backend an das
Gerät zurückgeleitet.
Es gibt allerdings mehrere Instanzen des WSM, die von einem Load Balancer im Round-Robin-Algorithmus geroutet werden. Bei
synchronen Aufrufen an das Backend kommt die Antwort an die richtige WSM-Instanz zurück. Allerdings sind asynchrone Aufrufe den
synchronen vorzuziehen, und Backends könnten auch direkt Nachrichten an Geräte senden wollen.
Hier kommen der Forwarding-Service (FORS) und eine Adress-Datenbank ins Spiel. Jeder WSM-Pod schreibt nach einer geöffneten
Verbindung mit einem Gerät die Geräte-ID zusammen mit der eigenen Adresse in die Adress-Datenbank. Der FORS fragt nun für ein
Gerät die richtige Adresse ab und kann Nachrichten vom Backend an die richtige FORS-Instanz weiterleiten.
Dazu kommt, dass es in Wirklichkeit nicht nur ein Backend gibt. Ein Message Mapping (implementiert als Kubernetes Config Map)
kann vom WSM genutzt werden, um zu entscheiden, an welches Backend ein konkreter Nachrichtentyp mit einer konkreten Version
gesendet werden soll. Dieses Mapping ändert sich regelmäßig Hier ist es daher wichtig, dass eine Rekonfiguration ohne Neustart
der WSM-Pods möglich ist.
Der WSM hat noch diverse andere Aufgaben: Er führt den TLS-Handshake durch, wobei validiert wird, dass das Client-Zertifikats
von einem von ca. 10 Issuer-CAs signiert wurde. Bei manchen Issuern soll der Expiry-Timestamp ignoriert werden. Manche
Client-Zertifikate sollen sofort abgelehnt werden, da die zugehörigen Geräte defekt sind (das wird über eine Datenbank mit
blockierten Zertifikaten geregelt; für das Blockieren eines Zertifikats ist ebenfalls kein Neustart notwendig). Manche Geräte
sind in Quarantäne und dürfen nur bestimmte Nachrichten versenden (beispielsweise Nachrichten im Zusammenhang mit
Firmware-Updates; wie die blockierten Zertifikate wird das über eine eigene Datenbank konfiguriert). Nach einem erfolgreichen
TLS-Handshake muss der WSM das Websocket-Upgrade (also den Protokollwechsel von HTTP zu Websocket) durchführen. Schließlich gibt
es noch einen Corporate Handshake (das ist vom Protokoll des Kunden vorgesehen; vor dem Abschluss dieses Handshakes dürfen keine
anderen Nachrichten in beide Richtungen gesendet/weitergeleitet werden).
Hier ist noch einmal eine Zusammenfassung des gesamten für uns relevanten Teil des Heimdall-Systems. Die Grundidee ist also,
dass der WSM so selten wie möglich neugestartet werden muss. Das erreichen wir, indem Konfiguration, die sich regelmäßig ändern
könnte, externalisiert wird und ohne Neustarts geändert werden kann.
Was passiert, wenn der WSM doch einmal neugestartet werden muss? Wenn ein WSM-Pod kontrolliert beendet wird, werden die
Verbindungen zu den entsprechenden Geräten nacheinander kontrolliert gekappt, sodass sich die Geräte mit anderen Instanzen neu
verbinden. Dieser Prozess dauert aktuell etwa 3 Stunden.
Falls ein Pod (beispielsweise aufgrund eines Bugs) unkontrolliert beendet wird, versuchen sich alle Geräte auf einmal neu zu
verbinden. Das würde zur Überlastung des Systems führen, daher gibt es einen Load Balancer, der dafür sorgt, dass sich Geräte
nicht zu schnell wieder verbinden können. Natürlich ist das ein ungünstiger Zustand, da die Funktionalität der noch nicht wieder
verbundenen Geräte teilweise eine ganze Weile eingeschränkt sein kann.
Das Deployment einer neuen WSM-Version läuft so ab: Zunächst werden in einem Canary-Deployment 1/3 der Instanzen durch Instanzen
der neuen Version ersetzt. Diese neue Version läuft parallel zu der alten für eine Woche, bevor auch die übrigen Instanzen
ersetzt werden. So können Fehler frühzeitig erkannt werden.
Das bedeutet übrigens nicht, dass nach dem Canary-Deployment 1/3 der Verbindungen auf der neuen Version stattfinden. Die
Wahrheit ist etwas komplizierter, da die Geräte, die sich nach dem Abschalten mancher der alten Instanzen neu verbinden,
genauso häufig mit den Canary-Instanzen wie mit den anderen alten Instanzen verbinden. In dem 7-Tage-Zeitraum gleichen sich die
Anzahl der verbundenen Geräte dann etwas aus, weil Geräte regelmäßig auch aus anderen Gründen die Verbindung verlieren und sich
neu verbinden.
Jetzt habt ihr gesehen, wie das System tatsächlich aussieht. Natürlich haben wir in den gut 5 Jahren, in denen das Projekt
existiert, auch dazugelernt und würden einige Dinge anders gestalten. Zu Beginn habe ich ja bereits gesagt, dass das Ziel wäre,
den WSM so selten wie möglich updaten zu müssen. Dafür ist es hilfreich, wenn der WSM so wenige Aufgaben wie möglich bekommt. In
einer idealen Welt sollte er daher der kleinste Service im gesamten System sein. Die Realität ist: der WSM ist der mit Abstand
größte Service des Heimdall-Systems. Woran liegt das? Aktuell hat der WSM einige Aufgaben:
* TLS und alles, was dazugehört: die Terminierung der TLS-Verbindung, die Validierung der Clientzertifikate, die Ausnahmen zur
Validierung der Zertifikate und das Blocklisting von Zertifikaten.
* Der Websocket-Upgrade, also die Entgegennahme der initialen HTTP-Verbindung und der Wechsel zum Websocket-Protokoll
* Der Corporate Handshake, der zu Beginn jeder Verbindung stattfinden muss
* Das Message Routing an die Backends inklusive Offenhalten der Verbindungen und Warten auf Antworten
* Die Quarantäne-Logik
* Etwas Logik zur Formatierung für Nachrichten, da beispielsweise alte Geräte Nachrichten als sortiertes JSON erwarten
Hier wäre daher unser Vorschlag, um die Verbindungen noch länge halten zu können. Vor die übrigen Systeme wird ein Service
gesetzt, der nur dafür zuständig ist, die TCP-Verbindung zu den Geräten zu halten, die TLS-Verschlüsselung zu terminieren und
die Client-Zertifikate im Rahmen der allgemein gültigen Regeln zu validieren. Nach der TLS-Terminierung wird die TCP-Verbindung
einfach an das Äquivalent des jetzigen WSMs weitergetunnelt. Wenn die WSM-Instanz beendet wird, kann der Tunnel an einer andere
WSM-Instanz weitergeroutet werden, ohne die Verbindung zum Gerät abzubrechen.
Hier seht ihr noch einmal, wie ihr mich erreichen könnt. Danke fürs Zuhören!