# Lab 12: Tracing und Logging In dem Ihnen zur Verfügung gestellten Cluster ist der sogeannte ELK-Stack (Elasticsearch + Kibana) vorbereitet. Genauer gesagt folgende Komponenten: - [Elasticsearch](https://www.elastic.co/de/elasticsearch) Zeitreihendatenbank - [FileBeat](https://www.elastic.co/de/beats/filebeat) Sammelt Logdaten des Kubernetes Clusters und laufender Pods und leitet diese an ES weiter. - [APM](https://www.elastic.co/de/apm) Tracing und Monitoring Server - [Kibana](https://www.elastic.co/de/kibana) Observability UI, um auf die so gesammelten Daten für Analysen zugreifen zu können. Wir nutzen diesen Stack als Typrepräsentant, um das Tracing von verteilten Transaktionen in Service-Architekturen zu demonstrieren und die Möglichkeiten der Log-Konsolidierung zu veranschaulichen. Der ELK-Stack hat den großen Vorteil, dass er aus einer Hand stammt ([Elastic](https://www.elastic.co)) und daher recht einfach und schnell mittels [Helm-Charts](https://github.com/elastic/helm-charts) in beliebigen Kubernetes Clustern installiert werden kann. ```Bash # Nicht durch Sie in diesem Lab durchzuführen!!! # Der Cluster ist bereits für Sie so vorbereitet. > helm repo add elastic https://helm.elastic.co > helm install elasticsearch elastic/elasticsearch > helm install kibana elastic/kibana > helm install filebeat elastic/filebeat > helm install apm-server elastic/apm-server ``` Für die oben genannten Komponenten gibt es jedoch auch eine Vielzahl alternativer Produkte wie bspw. [Prometheus](https://prometheus.io), [Fluentd](https://www.fluentd.org), [Jaeger](https://www.jaegertracing.io)/[Zipkin](https://zipkin.io) oder [Grafana](https://grafana.com). Auch Managed Monitoring Services wie bspw. [Datadog](https://www.datadoghq.com) erfreuen sich zunehmender Beliebtheit. Ferner bieten insbesondere die kommerziellen Hyperscaler wie [AWS](https://aws.amazon.com/de), [Azure](https://azure.microsoft.com/de-de), [Google Cloud](https://cloud.google.com), etc. entsprechende Monitoring Lösungen (auch zum Zwecke der Kundenbindung) an, die daher häufig eng und tief mit den entsprechenden Infrastrukturen integriert sind (dafür dann jedoch auch entsprechendes Vendor Lock-In Potenzial mitbringen). Die in diesem Lab veranschaulichten Prinzipien lassen sich dennoch auf alle oben genannten Produkte übertragen. Welcher Produktstack letztlich in konkreten Projekten eingesetzt wird, hängt oft entscheidend davon ab, welche Monitoring-Produkte bereits in Unternehmen für andere Projekte eingesetzt werden. Meist vermeidet man es mehrere Monitoring-Stacks parallel zu fahren. Aus diesem Lab kann daher keine Empfehlung für eines der genannenten Produkte abgeleitet werden. Die Wahl des ELK-Stacks erfolgte aus rein pragmatischen Gründen im Kontext dieses Moduls. ## Inhaltsverzeichnis - [Lab 12: Tracing und Logging](#lab-12-tracing-und-logging) - [Inhaltsverzeichnis](#inhaltsverzeichnis) - [Vorbereitung](#vorbereitung) - [Übung 01: Instrumentieren von Microservices zum Zwecke des Tracings](#übung-01-instrumentieren-von-microservices-zum-zwecke-des-tracings) - [Übung 02: Propagieren von Traces zwischen Microservices](#übung-02-propagieren-von-traces-zwischen-microservices) - [Übung 03: Konsolidiertes Logging](#übung-03-konsolidiertes-logging) - [Links](#links) - [Was sollten Sie mitnehmen](#was-sollten-sie-mitnehmen) ## Vorbereitung Sie benötigen auf Ihren lokalen Entwicklungsrechner: - [Python 3](https://www.python.org/downloads) - [Docker](https://www.docker.com/get-started) - [Lens](https://k8slens.dev) - [salvo](https://github.com/tarekziade/salvo) `python3 -m pip install salvo` *Sie können mit anderen HTTP Load Generatoren arbeiten. Diese Anleitung basiert jedoch auf `salvo`.* Führen Sie anschließend bitte folgende Schritte aus: 1. [Forken](https://git.mylab.th-luebeck.de/cloud-native/lab-tracing/-/forks/new) Sie sich bitte dieses [Repository](https://git.mylab.th-luebeck.de/cloud-native/lab-tracing). 2. Erstellen Sie anschließend in Gitlab unter `Einstellungen -> Repository -> Bereitstellungstoken` für das in Schritt 1 geforkte Repository einen **Bereitstellungstoken**, um selbstgebaute Container Images deployen zu können. - **Name:** `Registry read access (deployment)` - **Username:** `image-registry` (bitte exakt so!) - **Scope:** `read-registry` (nicht mit *read repository* verwechseln!) - Klicken Sie anschließend auf `Bereitstellungstoken erstellen` und kopieren Sie sich dieses geheime Token in die Zwischenablage! 3. Hinterlegen Sie nun für Gitlab Build-Pipelines dieses geheime Token unter `Einstellungen -> CI/CD -> Variables` als CI/CD-Variable. - Klicken Sie auf `ADD Variable` - **Key:** `REGISTRY_READ_TOKEN` (exakt so) - **Value:** Fügen Sie hier das geheime Token (Schritt 2) aus der Zwischenablage ein. - **Type:** `Variable` (nichts anderes) - **Flags:** Selektieren Sie `Mask Variable` damit das geheime Token in Log-Dateien maskiert wird. 4. Setzen Sie bitte analog zu Schritt 3 die `KUBECONFIG` Variable in `Einstellungen -> CI/CD -> Variables` auf den Inhalt der Kubeconfig-Datei, die Sie für dieses Modul als Access Credential für den bereitgestellten Kubernetes Cluster erhalten haben (Type: `File`). Hierdurch erhält die Build Pipeline für das Deployment Zugriff auf den Cluster. ## Übung 01: Instrumentieren von Microservices zum Zwecke des Tracings Die Deployment-Pipeline dieses Projekts ist so eingestellt, dass Ihnen eine Kibana Oberfläche in der `prepare`-Stage automatisch deployed wird. Kibana hat Zugriff auf den zentralen Elasticsearch Log- und Tracing-Store des Clusters. Um Zugriff auf Kibana zu bekommen, geben Sie in einem Lens-Terminal folgendes ein: ```Bash Terminal 1:> kubectl port-forward svc/kibana-kibana 55601:5601 ``` Anschließend können Sie unter [http://localhost:55601](http://localhost:55601) die Kibana Oberfläche für dieses Lab einsehen. ![UI](Kibana-Observability.png) **Halten Sie bitte das Terminal 1 für die gesamte Dauer des Labs offen!** Die Instrumentierung mit APM ist extrem einfach und wesentlich komfortabler als bspw. mit [OpenTracing](https://opentracing.io/guides/python) o. ähnl. Bibliotheken, da APM viel Instrumentierung schon abnimmt, wenn es mit unterstützen [Webframeworks](https://www.elastic.co/guide/en/apm/agent/python/current/supported-technologies.html) eingesetzt wird. Für [Flask](https://flask.palletsprojects.com) muss man bspw. nur die folgenden Zeilen nutzen, um das Flask `app`-Objekt mit einem Tracing Layer zu "umgeben". Dadurch werden bereits alle Requests an den Service getraced und auch automatisch Spans für ausgehende HTTP-Requests erzeugt. ```Python app = Flask(__name__) apm = ElasticAPM(app, service_name="my-service") ``` Den zu verwendenden APM Tracing Server können Sie dem Prozess mittels der Umgebungsvariable `ELASTIC_APM_SERVER_URL` mit geben. Das Service Deployment in `deploy/space-deploy+svc.yaml` ist bereits entsprechend vorbereitet. Der Tracing Server selber ist in der `.gitlab-ci.yml` Datei in der Umgebungsvariablen `TRACING_SERVER` hinterlegt. 1. Lesen Sie sich vorher etwas in die Instrumentierung mittels APM/Python/Flask in diesem [Tutorial](https://www.elastic.co/guide/en/apm/agent/python/current/flask-support.html) ein. Weitere Spans können mittels den Hinweisen zu [Instrumenting Custom Code](https://www.elastic.co/guide/en/apm/agent/python/current/instrumenting-custom-code.html) eingefügt werden. 2. Ergänzen Sie nun in der Datei `space/service.py` das Tracing mittels APM. Der `service_name` soll dabei aus der Umgebungsvariable `SERVICE` ausgelesen werden. 3. Committen und Pushen Sie Ihre Änderungen nun an Gitlab. 4. Warten Sie bis die Deployment Pipeline durchgelaufen ist. Und starten Sie dann in Gitlab den manuellen `space-svc`-Job (Play Button drücken) in der `deploy`-Stage. Dies erzeugt den schon bekannten "Space-Service" aus den vorhergehenden Labs, der die ISS-Position und weitere Daten abfragt. Forwarden Sie nun den Service auf Ihren lokalen Rechner. Wenn der `space-svc`-Job durchgelaufen ist, öffnen Sie in Lens ein weiteres Terminal. ```Bash Terminal 2:> kubectl port-forward svc/space-svc 58080:80 ``` Halten Sie nun dieses Terminal für die Dauer dieser Übung offen und prüfen Sie, ob Sie JSON-Daten unter [http://localhost:58080](http://localhost:58080) erhalten. Sie sollten nun aktuelle People-, Passtime- und Position-Daten der ISS sehen. Geben Sie nun etwas Last in das System. Öffnen Sie dazu ein drittes Terminal in Lens, um diesen Service nun 20-mal durch drei User abzufragen. ```Bash Terminal 3:> salvo -c 3 -n 20 http://localhost:58080 ``` Sehen Sie sich nun in [http://localhost:55601/app/apm](http://localhost:55601/app/apm) (Kibana) die Tracings an. Sie sollten Ihren Service (und ggf. weitere Tracings von anderen Kursteilnehmern und Cluster-Nutzern sehen). - Wählen Sie den Zeitraum `letzte 15min aus` - Klicken Sie auf den von Ihnen verantworteten Service im Format `space--svc`. - Klicken Sie auf die Transaction `GET /space`. Sie sollten dann vermutlich eine Auswertung sehen, die so ähnlich wie diese hier aussieht. ![Space Tracing](kibana-space-tracings.png) Spielen Sie nun gerne ein wenig mit Kibana herum. Sehen Sie sich die Statistiken und Auswertungen an, um ein Gefühl zu bekommen, was man mit Tracing so alles machen kann. Stellen Sie sich auch gerne einmal die folgenden Fragen: __Verständnisfragen:__ - Wie unterscheiden sich die Spans am kurzen und am langen Ende der Latenz-Distrubtion? - Woran könnte das liegen? - Wie könnte man die Wartezeiten zwischen den Spans vermutlich reduzieren (vor allem am langen Ende)? - Umwieviel Prozent ließe sich die Gesamtlatenz damit in etwa reduzieren? - Könnte man die Gesamtlatenz noch anders reduzieren? - Was hat mehr Effekt auf die Performance (Reduktion der Latenz) im vorliegenden Fall: Paralleles Absetzen der HTTP-Requests mittels asychronen Sprachmitteln (async/await)? Horizontales Upscaling der Pods? > __Hinweis:__ > Wenn Sie gar nicht weiterkommen, können Sie mit > `> cp/cheat/service-ue1.py space/service.py` > ggf. etwas abkürzen. Sie können nun den Forwarding Prozess in Terminal 2 löschen. ## Übung 02: Propagieren von Traces zwischen Microservices Für diese Übung ist Ihnen einen "Hiphop"-Service gegeben, der Service Interaktion entlang von Aufrufketten von Services demonstrieren soll. Der Hiphop Service ist wie folgt aufgebaut. Vollziehen Sie bitte die Implementierung unter `hiphop/service.py` und das Deployment unter `deploy/hiphop-deploy+svc.yml` nach. ``` +----------+ +----------+ +----------+ +----------+ | hh-svc-1 | ---> | hh-svc-2 | ---> | hh-svc-3 | ---> | hh-svc-4 | +----------+ +----------+ +----------+ +----------+ ``` 1. Deployen Sie dieses "Service-Orchester" nun mit der Deployment Pipeline über den manuellen `hiphop-svc`-Job der `deploy`-Stage. 2. Starten Sie dann in Lens in einem 2. Terminal folgendes Port Forwarding: ```Bash Terminal 2:> kubectl port-forward svc/hiphop-svc 58888:80 ``` 3. Unter [http://localhost:58888](http://localhost:58888) sollte nun `"Hip -> hop-1 -> hop-2 -> hop-3 -> hop-4 -> END"` stehen. Ein Request an den ersten Service geht als bis zum Ende der Service-Kette durch. Dies soll Services "simulieren", die von Upstream-Services abhängig sind (die wiederum von weiteren Upstream-Services abhängen, usw.). Wie in Übung 01 geben wir mittels eines dritten Lens Terminals ```Bash Terminal 3:> salvo -c 5 -n 100 http://localhost:58888 ``` nun etwas Last von 5 Usern mit insgesamt 100 Requests ins System. 1. Sehen Sie sich nun wieder unter [http://localhost:55601/app/apm](http://localhost:55601/app/apm) (Kibana) die Tracings an. Sie sollten Ihren Hip Hop Service (und ggf. weitere Tracings von anderen Kursteilnehmern und Cluster-Nutzern sehen). 2. Inspezieren Sie nun wie in Übung 01 die Latenzdistribution. Schauen Sie sich die Traces von kurzen Latenzen an. Dort wird vermutlich primär die letzte Stufe der Kaskade zu finden sein? Schauen Sie die Traces mit langen Latenzen an. Dort werden vermutlich die Requests zu finden sein, die durch die komplett Kaskade gehen? Die Latenzen mittlerer Dauern sind vermutlich die Requests gegen Tier 2 und 3. 3. Das Problem ist, dass wir die kompletten Traces von vier Services unter einem Servicenamen haben und somit nicht gut trennen können. Schreiben Sie also bitte Ihre Instrumentierung so um, dass jeder Ihrer Services die Tier-Nummer ergänzt bekommt, damit man die Traces auch den einzelnen Services zuordnen kann. 4. Committen und Pushen Sie Ihre Änderung in die Deployment Pipeline. Und führen Sie den manuellen `hiphop-svc`-Job der `deploy`-Stage der letzten Pipeline erneut aus. 5. Warten Sie ein wenig, bis der Service redeployed wurde (verfolgen Sie dies in Lens). Terminieren Sie dann das Port Forwarding in Terminal 2 (`CTRL-C` bzw. `CMD-C`) und geben Sie nun wieder etwas Last ins System. ```Bash Terminal 2:> kubectl port-forward svc/hiphop-svc 58888:80 Terminal 3:> salvo -c 5 -n 100 http://localhost:58888 ``` Wenn Sie sich nun wieder unter [http://localhost:55601/app/apm](http://localhost:55601/app/apm) (Kibana) die Tracings ansehen, werden Sie die einzelnen Tiers getrennt sehen. ![UI](Kibana-UI.png) Wenn Sie auf Ihren `hiphop-svc-1` und dort die `GET /hip`-Transaktion klicken, sollten Sie eine derartige Auswertung sehen. Insbesondere die Latency Distribution ist hier von Interesse, da Sie diese nutzen können, um die Verteilung von Latenzen in Ihren einzelnen Services zu inspezieren. ![Tracing](Kibana-Tracing.png) Vergleichen Sie doch einmal die Spans von Requests mit kurzen Latenzen mit den Spans von langen Latenzen. __Verständnisfragen:__ - Was fällt Ihnen dabei auf? - Wie entstehen vermutlich die Wartezeiten in den einzelnen Tiers? - Könnten Sie hier mittels parallelen asynchronen Requests wie in Übung 01 die Requestverarbeitung beschleunigen? - Welche andere Möglichkeit haben Sie in diesem Fall, die Latenzen zu reduzieren? __Last-Experimente:__ Bringt ein unterschiedliches Skalieren der einzelnen Tier-Stufen etwas? Fahren Sie zur Beantwortung dieser Frage ein paar Lasteteste mit unterschiedlichen Skalierungen durch, und überprüfen Sie Ihre Hypothesen! Welche Skalierungsform erbringt den meisten Performance-Gewinn? > __Hinweis:__ > Wenn Sie mit der Instrumentierung gar nicht weiterkommen, können Sie mit > `> cp/cheat/service-ue2.py hiphop/service.py` > ggf. etwas abkürzen. ## Übung 03: Konsolidiertes Logging > __Hinweis:__ Für diese Übung ist `` durch Ihren Namespace zu ersetzen, also z.B. `max-mustermann`. Sehen Sie sich nun mittels Kibana unter [http://localhost:55601/app/logs](http://localhost:55601/app/logs) die konsolidierten Logs des gesamten Clusters, d.h. aller Pods und aller Hosts an. Wenn Sie nun alle Logs von hiphop-1 Pods sehen wollen, können Sie in der Suchmaske bspw. folgendes eingeben. ``` kubernetes.namespace: "" and kubernetes.labels.app: "hiphop-1" ``` Um sich nur alle 404 Messages dieser Pods anzusehen, geben Sie dies hier ein: ``` kubernetes.namespace: "" and kubernetes.labels.app: hiphop-1 and message: 404 ``` Mit solcher Art filtern, kann man also schnell die komplette Log-Datenbank auf einen relevanten Bereich eingrenzen. Unter `hiphop/service.py` finden Sie eine `print()`-Anweisung, die simuliert ein generiertes Passwort loggen soll (was keine gute Idee ist). - Finden sich diese `print()`-Ausgaben im Log? - Wie könnte man die 404 Query so anpassen, dass Sie nach Ihrem persönlichen Passwort fahndet? - Muss man die Query auf Ihre Person einschränken? - Was finden Sie raus, wenn Sie einfach nur nach `message: pwd` suchen? Hoppla, da hätten Sie ja die Passwörter aller Ihrer Kursteilnehmer gehabt? Upps! __Transferaufgabe:__ Finden Sie über dieses Logging doch mal raus, wer von Ihnen im Kurs welche Aufgabe wirklich bearbeitet hat? ;-) __Tipp:__ Sie müssen den Zeitraum auf ein oder zwei Monate setzen, um ein vollständigeres Bild zu bekommen. ## Links - [ELK Stack Helm-Charts](https://github.com/elastic/helm-charts) - [Elasticsearch](https://www.elastic.co/de/elasticsearch) - [FileBeat](https://www.elastic.co/de/beats/filebeat) - [APM](https://www.elastic.co/de/apm) - [APM Python Agent](https://www.elastic.co/guide/en/apm/agent/python/current/index.html) - [Flask](https://flask.palletsprojects.com) - [Kibana](https://www.elastic.co/de/kibana) - [OpenTracing](https://opentracing.io) ## Was sollten Sie mitnehmen - Tracing Instrumentierungen ermöglichen das dynamische Verhalten von Request-Verarbeitungen besser zu inspezieren. - Tracing Instrumentierung kann dabei helfen, lange Latenzen zu identifizieren und mittels Verarbeitungsänderungen zu minimieren (z.B. mittels aschronen Requests, vgl. Übung 01, oder mittels horizontalem Skalieren, vgl. Übung 02). - Zentralisiertes und konsolidiertes Logging hat viele Vorteile, beinhaltet aber auch Sicherheitsrisiken (vgl. Übung 03), insbesondere dann, wenn Services Credentials o. ähnl. loggen. Passwörter, usw. gehören nie in Logs! Denn Sie wissen nicht, in welchem Kontext diese Logs verarbeitet werden. In unserem Fall, konnten Sie alle ihre künstlich generierten Passwörter aller Kursteilnehmer in den Logs herausfinden, nachdem Sie wussten wonach Sie suchen müssen. - Zentralisiertes und konsolidiertes Logging kann aber auch zur nachträglicher Analyse von IT-Security Breaches oder im Kontext der IT-Forensik eingesetzt werden. Z.B. wenn Sie rausfinden wollen, welcher Ihrer Kommilitonen wirklich in diesem Praktikum mitgearbeitet hat ;-)