Dev Blog #2 - Metriken

  • Liebe Community,


    wir haben im vergangenen Monat nicht nur am Winter Update gearbeitet, sondern uns auch intensiv mit Metriken und der Implementierung dieser auf unserem Netzwerk auseinandergesetzt.


    Mit diesem zweiten Devblog wollen wir euch einen Einblick in diese Arbeiten geben! Wie auch beim Ersten gilt: Der Post kann sehr technisch werden und ist besonders für diejenigen gedacht, die sich schon ein wenig mit der Materie auskennen oder einfach nur technisches Interesse haben.


    Außerdem haben wir den Source Code wieder veröffentlicht, um auch der Open Source Community etwas zurückzugeben. Die Projekte findet ihr auf unserem Github Profil.


    Also dann, legen wir mal los, in dem wir den groben Rahmen stecken und unsere Motivation erläutern.


    Motivation


    Schon seit Ewigkeiten haben wir bei uns am Netzwerk das Problem, dass wir kein vernünftiges Monitoring haben. Das heißt, wir haben keine guten Möglichkeiten Dinge, die auf dem Netzwerk passieren (Spielerjoins, Gespielte Spiele, usw.) live zu tracken.


    Man kann sich jetzt fragen, wie wir bis jetzt diese Daten erfasst haben. Ganz einfach: Wir haben ausgenutzt, dass sich diese Metriken aus anderen Daten ableiten lassen. Beispiel: Wir speichern die monatlichen Statistiken von jedem von euch als Spieler und aus diesen können wir berechnen, wie viel ein Modus insgesamt gespielt wurde.


    Wo liegt jetzt das Problem?

    Diese Werte sind abhängig von anderen Daten. Ohne die MStats hätten wir diese Möglichkeit nicht mehr. Und wenn die MStats existieren, aber inkonsistent sind, dann bringt uns die Auswertung auch nur semi-viel. Daher muss ein System her, das unabhängig von diesen Daten läuft und einen konsistenten Datensatz an Metriken für das Netzwerk generiert. Und genau bei dieser Problemstellung setzen wir jetzt an.


    Metriken


    Zuerst haben wir uns Gedanken gemacht, welche Arten von Metriken es gibt und inwiefern wir sie einsetzen können. Dafür gibt es das sogenannte OpenMetrics Projekt, das einen Standard für Metriken definiert. Dieser Standard ist abgeleitet von Prometheus, die wohl bekannteste TSDB für Monitoring Systeme, die wir auch verwenden wollten. Dementsprechend haben für uns nur die Typen Counter, Gauge, Histogram und Summary Relevanz, weil nur diese in Prometheus implementiert sind.


    Davon sind für uns am meisten Counter (nur steigend) und Gauge (steigend und fallend) einsetzbar, weil wir solche Dinge wie beispielsweise "Spielerbeitritte", "gespielte Runden" und "gekaufte Cosmetics" nicht näher analysieren wollen und dafür keine Histogramme o.Ä. brauchen.


    Um einfach mal anschaulich zu sehen, wie zum Beispiel eine Gauge aussehen kann, hier ein Graph von gemessenen Temperaturen in Celsius pro Minute:


    Beispiel Metrik mit Temperaturen in Celsius


    Prometheus


    Nachdem wir geklärt haben, was für Metriken Arten wir brauchen, fehlt uns nur noch ein Weg, um diese auch persistent zu speichern. Entschieden haben wir uns dabei für Prometheus, aber es gibt auch Alternativen wie InfluxDB oder Graphite, die sehr gut sind aber teilweise grundlegend anders funktionieren.

    Prometheus haben wir dabei in unserem eigenen Monitoring Kubernetes Cluster deployed, wo gebündelt alle zugehörigen Applikationen laufen (z.B. auch Grafana).


    Jetzt stellt sich aber die Frage: Wie kriege ich Metriken in Prometheus rein?


    Wie schon erwähnt, ist Prometheus etwas anders als ähnliche Systeme. Prometheus ist nämlich pull-based, d.h. man kann sich nicht wie bei einer normalen Datenbank einfach verbinden und Daten "hochladen", sondern man muss sich bei Prometheus als sogenanntes Scrape-Target melden, um dann zu bestimmten Zeitpunkten selbst abgefragt zu werden.


    Prometheus Funktion von Scrape Targets


    Das hört sich erstmal wild an, hat aber den Vorteil, dass der Service, der die Metriken bereitstellt gar nichts von der Prometheus Instanz wissen muss. Andererseits haben wir damit aber das Problem, dass gängige Methoden zum Umgang mit Datenbanken nicht funktionieren, d.h. besser wäre es, wenn wir push-based arbeiten könnten.


    Pushgateway


    Dafür gibt es tatsächlich schon eine vorgefertigte Lösung von den Prometheus-Leuten selbst: Das Pushgateway.

    Das Pushgateway funktioniert so, dass man Metriken an einen API-Endpunkt senden kann und das Gateway dann diese sammelt und als Scrape-Target fungiert. Doch eine wichtige Einschränkung hat es immernoch: Wenn die Metriken die gleichen Labels besitzen, also z.B. job=some_job, server=Lobby-1, dann werden die Metriken, die vorher gepusht wurden, komplett überschrieben. Grund dafür ist, dass das Gateway eigentlich nur für kurze Jobs gedacht sind und diese dann potenziell nur einmal Metriken generieren.

    In unserem Fall kann man bei Minigame-Servern zwar von kleineren Jobs sprechen, trotzdem gehen Runden manchmal bis zu einer Stunde und in der Zeit wollen wir definitiv öfters als einmal Metriken generieren. Das heißt das Pushgateway ist nicht ganz das, was wir brauchen.


    Also mussten wir uns für den langen Umweg entscheiden: Ein eigenes Gateway schreiben, dass uns diese Funktionalität bietet.


    Thor


    Ich werde jetzt nicht näher auf den Entwicklungsprozess eingehen, der Source Code ist aber auf Github verfügbar.


    Thor ist nun so designed wurden, dass es 100% kompatibel mit den API Endpunkten vom Pushgateway ist. Das hat den Grund, dass wir so damit umgehen können, als wäre es ein Pushgateway und wir damit auch die gängigen Prometheus Clients nutzen können, ohne dass es wirklich eins ist.

    Wie funktioniert es aber jetzt genau? Wenn nun eine Anfrage an die API kommt, werden nach Validitätsprüfungen die Daten entweder ganz normal abgelegt, oder, und jetzt kommt die neue Funktion, mit den alten, bestehenden Daten gemerged. Dieses Mergen passiert für jeden Metriken-Typ anders, daher hier einmal eine kleine Übersicht dazu:


    TypMerge Prozess
    CounterBeim Counter wird der vorherige Wert einfach mit dem neuen Wert addiert.
    GaugeHier macht eine Addition überhaupt keinen Sinn, daher wird der alte Wert ersetzt.
    HistogramEin Histogram besteht aus verschiedenen Observationspunkten. Diese werden in der Summe addiert und den jeweiligen Buckets hinzugefügt.
    SummaryEine Summary berechnet aus den Observationspunkten direkt passende Quantile. Das ist aber viel zu komplex und gehört nicht in das Gateway. Es wird einfach ersetzt.


    Und dann stellt Thor den Endpunkt /metrics bereit und darüber kann dann Prometheus die Metriken scrapen. Beispielhaft kann die Response so aussehen:


    GET Request an das Thor Gateway


    Kirby


    Nachdem wir geklärt haben, wie wir generell Metriken in Prometheus persistieren können, müssen wir noch die letzte Problemstellung lösen.

    Unser Ziel war es nämlich, so einfach wie möglich, für uns als Entwickler hier, Metriken zu generieren. Da man aber, wenn man die Prometheus Clients benutzt, viel zu nahe mit Prometheus selbst arbeitet und der Client nicht ganz die Benutzung hat, die wir haben wollen, haben wir eine Library als abstrakte Ebene über den TSDB Clients geschrieben, sodass man selbst nichts mehr mit Prometheus, InfluxDB o.Ä. zu tun hat, sondern nur noch mit der Bibliothek spricht.


    Das haben wir "Kirby" getauft! Den Source Code gibt's auch auf Github.


    So haben wir es nämlich geschafft, dass wir als Entwickler nur noch folgendes schreiben müssen:


    Benutzung von Kirby in Java


    Und schon landet die Metrik bei Prometheus.


    Wir haben diesen Schnipsel Code mal am Sonntag, den 29.11. als Test in CrimeTime laufen lassen und haben dadurch folgenden Graph generieren können:


    Gespielte Spiele in CrimeTime vom 29.11.2020


    Ausblick


    Nachdem wir nun sehr viel Zeit in dieses System gesteckt haben, haben wir jetzt aber die Möglichkeit viel einfacher und präziser Metriken zu generieren und diese dann in Grafana o.Ä. darzustellen. Dadurch können wir viel effektiver Business-Analysen durchführen und haben auch etwas Nettes zum Angucken.


    Als Nächstes werden wir das System auf alle möglichen Plugins ausweiten und als Standard für unsere Entwicklung einführen.


    Schlusswort


    Ich bedanke mich für's Lesen und hoffe euch hat dieser etwas andere Einblick in unsere Systeme gefallen. Falls es Fragen oder Feedback gibt, könnt ihr diese gerne als Kommentar unter diesen Post verfassen - ich freu mich auf eure Antworten.


    Viele Grüße

    Superioz

    wvmyuot.png
    - Jegliches unkonstruktives Kommentieren meiner Posts oder meiner Meinung wird ignoriert. -

  • Superioz

    Approved the thread.