Skalierbarkeit – was man als Start-up beachten muss
Skalierbarkeit ist für jedes Start-up ein wichtiges Schlagwort. Man hat seine Anwendung gebaut und erste Kunden benutzen sie. Dann kommt der Erfolg und man wird von seinen Kunden überrannt. Die Anwendung streikt, der Umsatz fällt aus. Um dies zu vermeiden ist es wichtig sich bereits früh Gedanken über die Skalierbarkeit seiner Anwendung zu machen. Wenn man die wichtigsten Punkte beachtet, wird das Wachstum eines Startups viel reibungsloser voranschreiten.
1. Messen, Messen, Messen
Das wichtigste für einen 24 x 7 x 365 Betrieb einer Anwendung ist die Maxime “Kenne deine Anwendung”. Wann fühlt sie sich wohl, wann ist sie holprig, wann steht sie kurz vor dem Kollaps. Nur wenn man viele Werte misst und seine Anwendung kennt, kann man den Zustand bestimmen und Probleme vorhersehen. Wie es bei einem Patienten üblich ist einen Herzmonitor anzulegen, so muss auch eine Anwendung permanent vermessen werden. Was vielen Startups dabei passiert: Nach einem GAU stellt man fest was man vorher alles hätte messen sollen, aber dann ist es zu spät.
Was soll man nun messen? Im Prinzip alle Punkte innerhalb der Anwendung. Wie lange dauert ein Aufruf, gibt es Ausreißer, wie sieht die durchschnittliche Dauer aus, was ist normal, wie verändert sich das Verhalten unter Last und wie waren die Werte der letzen Woche im Vergleich sind wichtige Fragen die man beantworten können sollte. Auch für einen VC ist es wichtig, dass das Startup diese Fragen beantworten kann, da das Wachstum der technischen Plattform unter Umständen stark gefährdet ist. Oft sind diese Fragen aber leider immer noch nicht Bestandteil einer Technical Due Diligence.
Manchmal sind die Zahl der Messpunkte und die Datenmenge zu groß für ein Startup. Dann sollten aber mindestens alle Dienste Aufrufe vermessen werden. Dies gilt besonders für die langsamen Dienste wie:
* ERP Anbindungen
* DB Zugriffe
* Dateisystem Zugriffe
* Externe Zugriffe wie zum Beispiel Payment Provider
Für jeden dieser langsamen Aufrufe muss ein maximaler Zeitraum (SLA, Service Level Agreement) angegeben werden, meist in Millisekunden. Unter Umständen kann auch ein Wert von mehreren Stunden sinnvoll sein. Wenn dieser maximale Zeitraum überschritten wird, muss die Anwendung eine Warnung verschicken (Mail, Twitter und SMS sind dafür üblich).
Durch Zusammenrechnen aller maximalen SLA Werte kann man jederzeit bestimmen, wie lange ein User maximal warten muss. Alle langsamen Aufrufe sollten mit einem Timeout belegt sein, dazu im nächsten Punkt mehr.
Ein weiteres weites Feld für das Vermessen der Anwendung sind geschäftsspezifische Sensor-Punkte. Je nach Anwendung unterscheiden sich diese, können aber korrekte Bezahlungen,fehlgeschlagene Bezahlungen, Logins pro Minute oder Checkouts pro Minute umfassen. Diese sollte man in Echtzeit messen und visualisieren.
2. Keine synchronen Aufrufe verwenden
Wie sich mittlerweile herumgesprochen hat, sind synchrone Aufrufe in der Anwendung keine gute Idee. Insbesondere wenn es Aufrufe von Systemen sind, deren Verläßlichkeit man selbst nicht kontrollieren kann, wie etwa externe Dienste. Synchrone Aufrufe blockieren mitunter und belegen so viele Resourcen in der Anwendung, dass diese stehen bleibt. Daher ist die erste Regel für synchrone, externe Aufrufe dass sie ein Timeout besitzen. So wird die Anwendung bei Ausfall von Diensten nicht zum Stillstand gebracht. Die Timeouts muss man mit anderen Services abstimmen, mit eventuellen Timeouts des Webservers oder des Applikationscontainers, mit den Timeouts von Proxies und Load-Balancern. Sonst bricht eventuell ein Glied in der Kette zu schnell ab und der Benutzer bekommt das Timeout zu sehen. Daher gilt es, die richtige maximale Dauer zu berechnen. Nicht zu kurz, sonst hat man zu viele Fehler, nicht zu lange sonst steht die Anwendung.
Alle Anbindungen an Services die keine 100% Zuverlässigkeit haben – und wer hat diese schon – brauchen eine Sicherung die herausspringt wenn es Probleme gibt. Diese Sicherung stellt dann bei anhaltenden Problemen das Feature gänzlich ab, z.B. die Bezahlmöglichkeit per Kreditkarte wenn der Kreditkartenprovider nicht verfügbar ist. Die Sicherung schickt automatisch auch eine Warnung. Besser kein Feature als eines das nicht funktioniert, die Kunden verärgert und die Stabilität der Anwendung gefährdet. Auch sollten (synchron) angebundene Dienste von Hand notfalls abschaltbar sein, wie auch alle anderen für die Stabilität gefährlichen Teile der Anwendung.
Aber selbst mit einem Timeout kann es passieren, dass die Anwendung blockiert wenn zu viele Benutzer feststecken. Man sollte daher auf synchrone Aufrufe soweit wie möglich verzichten, mindestens aber alle mit ihrem Verhalten, SLAs und Timeouts im Überblick behalten (Liste aller langsamen, synchronen Aufrufe mit ihren SLA und in welchem UseCase sie auftreten).
3. Alle langsamen Zugriffe auf später verschieben
Langsame Zugriffe kann man oft nicht vermeiden. Diese Umfassen meist schreibende Zugriffe auf ein ERP, die DB, berechnen Bilder, befüllen einen Cache mit neuen Werten oder berechnen Umsatzmetriken für Kunden nach einem Kauf. Diese langsamen Zugriffe sollten asynchron erfolgen in einem Write-Behind Ansatz. Das System kehrt sofort zum Benutzer zurück und führt erst danach die Anfrage aus. Dadurch merken die Benutzer nichts von einer hohen Last, da sie sofort eine Antwort bekommen. Das System verteilt dann in Ruhe die Anfrage und bearbeitet sie. Oft geschieht dies durch Kopplung der Komponenten mittels eines Messaging Systems. Dieses kann Anfragen auf mehrere Server verteilen. Klassische Fälle sind Login oder Checkout in einem eCommerce System.
4. CAP vs. ACID oder Transaktionen vermeiden
Transaktionen skalieren schlechtim Web-Umfeld – heutzutage eine Binsenweisheit. Transaktionen folgen dem ACID-Prinzip (Atomicity, Consistency, Isolation, Durability), skalierbare Web-Anwendungen dem CAP-Theorem (Consistency, Availability, Partition Tolerance). Manchmal sind Transaktionen aber notwendig. Man sollte sie gezielt benutzen, etwa bei Auftragseingängen, Bezahlvorgängen usw. Wichtig ist es, dass man die vier Isolations-Level von Transaktionen versteht. Oft reicht ein niedriger Level. Man sollte diese Entscheidung daher bewusst treffen und nicht der Datenbank oder der Standard-Einstellung der Middleware überlassen. Ein niedriger Isolations-Level bringt Performanz und eine bessere (vertikale) Skalierbarkeit. Der Verzicht auf Transaktionen bringt eine bessere horizontale Skalierbarkeit.
5. Caching, Caching, Caching
Es ist kein Geheimnis mehr, dass die Grundlage jeglicher Skalierbarkeit das Cachen von Inhalten ist. Cachen also ja, aber das Richtige. Wildes Cachen aller Daten führt nur zu schlechter Performance und hohen Kosten für Hardware.
Als Regel gilt: möglichst nahe am Benutzer cachen. Dies bedeutet soweit es geht im Browser. Daten die im Browser gecached werden – z.B. in Javascript – erzeugen keinen Request auf dem Server und damit auch keinerlei Last. Das zweitbeste Caching ist ein Expire Header in den Daten, ein festgesetztes Haltbarkeitsdatum. Nur nach Ablauf dieser Haltbarkeit wird der Browser wieder beim Server anfragen und Last erzeugen. Wenn möglich sollte man alle seine Daten versionieren und ein Haltbarkeitsdatum (Expire) weit in der Zukunft für jede Version festlegen. Wenn das nicht möglich ist, bleiben einem Etag und Last-Modified Header. Diese erzeugen einen Request auf dem Server um zu prüfen ob sich Daten geändert haben. Dabei ist der Request klein und erzeugt keinen Cache oder DB Zugriff wenn man es richtig macht. Zwischen Browser und Server kann man mittels eines Reverse-Caches Zugriffe auf den Server vermeiden. Man braucht dazu aber eine weitere Infrastruktur von Proxies. Das bestmögliche nächste Caching ist das Cachen auf dem Server mittels einer Cache Lösung, in der Regel bietet sich dazu ein Memcached-Server an. Der Benutzer erzeugt dadurch zwar Server-Last, aber keine langsamen Datenbank-Zugriffe.
Die Aufzählung zeigt: Je näher man an der Datenbank cached, desto höher ist die Last für das System, je näher am Benutzer desto niedriger ist die Last. Caching ist daher immer ein Schichtenmodell aus einzelnen Caching-Schichten die aufeinander aufbauen und sich gegenseitig ergänzen.
6. Log Dateien lesen
Jeder Server schreibt heutzutage Log Dateien. Aber wenn man kein dediziertes Operations-Team hat – und manchmal selbst dann – werden die Log Dateien oft nicht gelesen. Dies ist ein Fehler der einen bei der Skalierung der Anwendung treffen wird. Am besten ist es, alle Log Dateien automatisch täglich nach Fehlern auszuwerten. Dazu ist es hilfreich Fehler zu gruppieren, zusammenzufassen und eine Top 10 zu erstellen. Diesen Fehlern sollte man nachgehen. Jeder Fehler kann ein potentielles Problem nach sich ziehen und ein GAU auslösen. Logs sollten frei von Fehlern sein. Wenn es Fehler gibt ist das kein gutes Zeichen für die zukünftige Skalierung.
In seinem Webserver sollte man das Zugriffs-Log aktivieren. In diesem kann man täglich die langsamsten Seiten seiner Anwendung auswerten und diese optimieren (z.B. mit Caching, siehe oben). Wer MySQL benutzt sollte das Slow-Query-Log aktivieren. Dort werden alle langsamen DB-Anfragen protokolliert. Man kann diese täglich auswerten und den langsamen Anfragen auf den Grund gehen. Im Normal-Betrieb sollten keine langsamen Anfragen – Standardwert größer 2 Sekunden – auftreten.
Es gibt Vorwarnungen in den Log Dateien. Bevor eine Anwendung ausfällt, zeigt sie oft Symptome. Diese zu deuten ist nicht immer einfach, aber man muss sie unbedingt beachten und darf sie auf keinen Fall ignorieren!
7. Kapazitätsplanung
Skalierbarkeit bedeutet auch immer Kapazitätsplanung, oder für ein Startup die Frage, was brauche ich an Hardware im nächsten Quartal? Dazu ist es wichtig zu verstehen, wie die eigenen Geschäftsmetriken (Umsatz, Benutzer, Page Impressions, …) mit der Last der Server zusammenhängen. Was sind die Treiber für die Last? Kennt man den Zusammenhang, kann man aus dem Geschäftsmetriken-Forecast den Hardware-Forecast berechnen. Für linear skalierbare Systeme ist das trivial, für Systeme die an bestimmte Deckel stoßen wie zum Beispiel Datenbanken oder Load Balancer, muss man diese potentiellen Deckel voraussehen. Wo befindet sich der nächste Deckel in meiner Firma? Web-Server, DB-Server oder die Auslieferung von Bildern? Zwei Grundregeln:
* Systeme die man vertikal skaliert, haben mehr Deckel als horizontale Systeme
* Single Point of Failures (SPoF) haben Deckel, die schwer zu beseitigen sind
´Wer ausreichend früh Kapazitätsplanung betreibt, wird seltener nachts von einer SMS geweckt.
Fazit
Skalierbarkeit ist ein großes Feld. Wenn man aber die wichtigsten Punkte beachtet, wird das Wachstum eines Startups viel reibungsloser voranschreiten. Vermeiden sie den Fehler vieler Startups über Skalierbarkeit und Zuverlässigkeit ihrer Anwendung zu spät nachzudenken.
Zur Person
Stephan Schmidt ist Entwicklungsleiter bei brands4friends. Dort ist er im Rahmen seiner Tätigkeit für die Skalierbarkeit der Architektur verantwortlich. Davor war er in mehreren Firmen Teamleiter, Entwicklungsleiter und CTO. Der Schwerpunkt seines Interesses liegt in der Produktivität von Softwareentwicklung, agilen Methoden und Lean Development. Sein Blog heißt Code Monkeyism. Unter diesem Namen ist er auch bei Twitter zu finden.