Commit ca924693 authored by Nane Kratzke's avatar Nane Kratzke
Browse files

Terminate stage added

parent f121630e
......@@ -40,7 +40,6 @@ container:
redis:
stage: deploy
script:
- kubectl delete -f deploy/redis-dep.yaml || true
- kubectl apply -f deploy/redis-dep.yaml
- kubectl apply -f deploy/redis-svc.yaml
......@@ -71,3 +70,17 @@ queueing-producer:
script:
- mo deploy/queueing-producer-dep.yaml | kubectl delete -f - || true
- mo deploy/queueing-producer-dep.yaml | kubectl apply -f -
pubsub:
stage: terminate
when: manual
script:
- mo deploy/pubsub-producer-dep.yaml | kubectl delete -f - || true
- mo deploy/pubsub-consumer-dep.yaml | kubectl delete -f - || true
queueing:
stage: terminate
when: manual
script:
- mo deploy/queueing-producer-dep.yaml | kubectl delete -f - || true
- mo deploy/queueing-producer-dep.yaml | kubectl delete -f - || true
......@@ -19,8 +19,39 @@ Die in diesem Lab am Beispiel von Redis vermittelten Prinzipien sind aber durcha
## 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.io)
Führen Sie anschließend bitte folgende Schritte aus:
1. [Forken](https://git.mylab.th-luebeck.de/cloud-native/lab-rest/-/forks/new) Sie bitte dieses GitLab-Repository in Ihren GitLab-Namensraum.
2. Klonen Sie das in Schritt 1 geforkte Repository bitte anschließend auf ihrem lokalen Rechner (`git clone`).
3. Installieren Sie anschließend bitte lokal auf Ihrem Rechner die Kubernetes-IDE [Lens](https://k8slens.dev/).
4. Laden Sie sich Ihre `kubeconfig` Datei im [Moodle-Kurs](https://to-be-donex) herunter.
5. Starten Sie Lens und fügen Sie der IDE die kubeconfig Datei hinzu, um auf Ihren Cluster zugreifen zu können. Sie sollten dann Ihren Namespace in dem für Sie bereitgestellten K8S-Cluster sehen.
6. 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!
7. Hinterlegen Sie nun für Gitlab Build-Pipelines dieses geheime Token unter `Einstellungen -> CI/CD -> Variables (Aufklappen) -> ADD VARIABLE` als CI/CD-Variable.
- **Key:** `CI_REGISTRY_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.
8. Hinterlegen Sie in Ihrem geforkten GitLab-Repository nun die `kubeconfig`-Datei als CI-Environment-Variable mittels `Einstellungen -> CI/CI -> Variables (Aufklappen) -> ADD VARIABLE` (setzen Sie hierfür folgende Werte)
- **Key:** `KUBECONFIG` (Exakt so eingeben)
- **Value:** Inhalt der kubeconfig (z.B. mittels Copy-Paste aus Editor)
- **Typ:** `File` (Auswählen, WICHTIG!!!)
9. Öffnen Sie Lens und hinterlegen Sie mittels `+` die `kubeconfig`-Datei, die Sie auch im vorherigen Schritt in der Deployment Pipeline hinterlegt haben.
## Übung 01: Pub/Sub Messaging (Fan Out)
In dieser Übung werden wir uns folgenden Publish-Subscribe Kommunikationsmuster widmen.
```
+--M3,M2,M1-> C
|
......@@ -29,8 +60,69 @@ P --M3,M2,M1--+--M3,M2,M1-> C
+--M3,M2,M1-> C
```
Ein Producer *P* erzeugt Messages, die in einen Channel einer Message Queue gegeben werden (Publish). Diese Queue
verteilt dann alle Messages an Consumer *C*, die sich für diesen Channel angemeldet haben (Subscribe). Auf diese Weise erhalten alle Consumer alle Messages, ohne dass Producer die Empfänger kennen muss. Die Message Queue entkoppelt so Producer von Consumer und umgekehrt.
- Studieren Sie bitte sowie den [Publish/Subscribe Abschnitt](https://github.com/andymccurdy/redis-py#publish--subscribe) der Redis Python API.
- Versuchen Sie nun die beiden Dateien `messaging/pubsub-producer.py` und `messaging/pubsub-consumer.py` nachzuvollziehen.
- Versuchen Sie nun die `.gitlab-ci.yaml` sowie die Kubernetes Manifests im Ordner `deploy` nachzuvollziehen. Die Deployment Pipeline dieses Labs erzeugt eine Redis Datenbank in Ihrem Kubernetes Namespace und sieht diverse manuell triggerbare Jobs für unterschiedliche Consumer und Producer vor, um Teile dieses Labs zu starten.
- Triggern Sie nun diese Pipeline (bspw. durch einen Commit dieses Repos).
- Wenn die Redis Datenbank erfolgreich in Kubernetes läuft (checken Sie dies in Lens), wechseln Sie in Gitlab in die Pipelines Ansicht (`CI/CD -> Pipelines` -> auf letzte bestandene Pipeline klicken) und starten Sie manuell die beiden Jobs `pubsub-consumer` und `pubsub-producer` in der Web-Oberfläche (Play Buttons der Jobs).
- Im Lens Terminal (oder der Oberfläche sollten Sie nun drei Pods sehen)
```Bash
> kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-599f4cc796-6pltl 1/1 Running 0 25m
pubsub-consumer-b9756d886-62z8l 1/1 Running 0 19m
pubsub-producer-79bcdbf9d8-bdxrn 1/1 Running 0 19m
```
- Sehen Sie sich in Lens nun die Logs des Producers an:
```
...
Publishing message 'Hi, this is message #4317 from pubsub-producer-79bcdbf9d8-bdxrn.' to channel 'xpubsub'
Publishing message 'Hi, this is message #4318 from pubsub-producer-79bcdbf9d8-bdxrn.' to channel 'xpubsub'
Publishing message 'Hi, this is message #4319 from pubsub-producer-79bcdbf9d8-bdxrn.' to channel 'xpubsub'
Publishing message 'Hi, this is message #4320 from pubsub-producer-79bcdbf9d8-bdxrn.' to channel 'xpubsub'
Publishing message 'Hi, this is message #4321 from pubsub-producer-79bcdbf9d8-bdxrn.' to channel 'xpubsub'
```
- Die Logs des Consumers sollten in etwa so aussehen:
```
...
0.92ms | msg: {"timestamp": 1614764396330193114, "message": "Hi, this is message #4689 from pubsub-producer-79bcdbf9d8-bdxrn."}
0.64ms | msg: {"timestamp": 1614764396731672144, "message": "Hi, this is message #4690 from pubsub-producer-79bcdbf9d8-bdxrn."}
0.79ms | msg: {"timestamp": 1614764397232874681, "message": "Hi, this is message #4691 from pubsub-producer-79bcdbf9d8-bdxrn."}
0.79ms | msg: {"timestamp": 1614764397734203948, "message": "Hi, this is message #4692 from pubsub-producer-79bcdbf9d8-bdxrn."}
0.81ms | msg: {"timestamp": 1614764398035370260, "message": "Hi, this is message #4693 from pubsub-producer-79bcdbf9d8-bdxrn."}
0.52ms | msg: {"timestamp": 1614764398236610874, "message": "Hi, this is message #4694 from pubsub-producer-79bcdbf9d8-bdxrn."}
0.70ms | msg: {"timestamp": 1614764398737878344, "message": "Hi, this is message #4695 from pubsub-producer-79bcdbf9d8-bdxrn."}
```
Die Zeitangaben in ms geben an, wie lange eine Message vom Producer zum Consumer benötigt hat (Latenz). Vergleichen Sie diese Werte mit den Werten, die Sie im letzten Lab zu REST und gRPC basierten Interaktionsverfahren erhoben haben.
- Merken Sie sich in etwa die letzte Messagenummer, die der Consumer bis dahin erhalten hat. Löschen Sie dann in Lens den Consumer und warten Sie ab, bis der Pod wieder durch Kubernetes automatisch neu gestartet wurde. Prüfen Sie im neuen Container Log des regenerierten Pods ab welcher Message Nummer dieser das Streaming wieder auf nimmt?
- Löschen Sie nun das Producer Deployment in Lens, so dass kein neuer Producer Pod gestartet wird? Laufen noch weitere Messages im Consumer auf? Stürzt der Consumer ab?
- Deployen Sie in Gitlab mittels des manuellen Pipeline Jobs erneut den Producer (Play Button des Jobs).
- Der Producer sollte wieder ab 1 nummerierte Messages erzeugen, die der Consumer im Log anzeigt.
- Skalieren Sie nun in Lens den Producer auf 3 Instanzen hoch (`Producer Deployment -> Scale`). Achten Sie nun im Consumer Log auf die Ursprünge der Messages. Sie erhalten nun Messages von mehreren Producern.
```
0.89ms | msg: {"timestamp": 1614765108327074527, "message": "Hi, this is message #95 from pubsub-producer-79bcdbf9d8-ch4w9."}
1.10ms | msg: {"timestamp": 1614765108467772402, "message": "Hi, this is message #610 from pubsub-producer-79bcdbf9d8-lc2rq."}
0.78ms | msg: {"timestamp": 1614765108746772325, "message": "Hi, this is message #100 from pubsub-producer-79bcdbf9d8-nzr2v."}
0.86ms | msg: {"timestamp": 1614765108828138961, "message": "Hi, this is message #96 from pubsub-producer-79bcdbf9d8-ch4w9."}
0.81ms | msg: {"timestamp": 1614765108869005443, "message": "Hi, this is message #611 from pubsub-producer-79bcdbf9d8-lc2rq."}
```
- Skalieren Sie auch die Consumer auf drei Instanzen hoch. Vergleichen Sie die Logs der drei Pods in Lens. Laufen unterschiedliche Messages auf?
- Löschen Sie nun das Producer Deployment und das Consumer Deployment (entweder in Lens oder mit dem Gitlab Pipeline `pubsub`-Job in der `terminate`-Stage).
Diese Übung hat nicht persistentes PubSub-Messaging mittels Redis demonstriert. Sie haben gesehen, dass Sie sowohl Consumer wie auch Producer unabhängig von einander hoch- und herunterskalieren können. Sie haben allerdings auch gesehen, dass diese Form des Messaging stateless ist. Messages, die einmal verschickt wurden, werden von Redis vergessen. Wenn Sie Anwendungsfälle haben, in denen die Historie von Events wichtig ist, weil Sie ggf. wieder abgespielt werden müssen (z.B. um eine Datenbank zu rekonstruieren), benötigen wir persistente Messaging Systeme.
Als Datenbank, kann Redis dies natürlich auch.
## Übung 02: Persistentes Pub/Sub Messaging
Diese Übung zeigt Ihnen wie man Redis als persistentes Messaging System einsetzen kann. Wir nutzen hierfür das `xadd`-[Command](https://redis.io/commands/XADD) (Producer) von Redis mit dem Redis Event Streams aufbauen kann. Mittels des `xread`-[Commands](https://redis.io/commands/XREAD) lässt sich aus solchen Streams lesen (Consumer).
- Lesen Sie sich hierzu als erstes ein wenig in [Redis Streams](https://redis.io/topics/streams-intro) ein.
- Studieren Sie anschließend die Klasse `messaging/MQueue.py`. Hier ist Ihnen eine Wrapper-Klasse um Redis gegeben, die die Commands `xadd`, `xread` und `xgroupread` so kapselt, dass diese komfortabel mittels einer `listen()`-Methode für Consumer und mittels einer `publish()`-Methode für Producer genutzt werden kann.
## Übung 03: Event Sourcing
## Übung 04: Queueing
......@@ -65,12 +157,15 @@ P --M4,M3,M2,M1--+-----M2-> C3
- [Introduction to Redis Streams](https://redis.io/topics/streams-intro)
- Weitere Messaging Lösungen wie [Kafka](https://kafka.apache.org), [Nats](https://nats.io), [RabbitMQ](https://www.rabbitmq.com), [ActiveMQ](https://activemq.apache.org)
- Messaging Standards wie [AMQP](https://www.amqp.org) und [MQTT](https://mqtt.org)
- [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) (Martin Fowler)
- [Event Sourcing](https://de.wikipedia.org/wiki/Event_Sourcing) (Wikipedia)
## Was sollten Sie mitnehmen
- Obwohl Redis eigentlich eine In-Memory Key-Value Datenbank ist, kann Sie (für viele überraschend) auch für (einfaches) Messaging eingesetzt werden. Gegenüber komplexeren Lösungen wie bspw. Kafka kann dies durchaus von Vorteil sein.
- Das Kommunikationsmuster Publish-Subscribe kann für vor allem für Fan-Out Anwendungsfälle eingesetzt werden, d.h. bei der Nachrichten an ALLE Consumer gehen. Wenn Publish-Subscribe Messaging persistiert wird, kann es zum [Event Sourcing](https://de.wikipedia.org/wiki/Event_Sourcing) verwendet werden.
- Das Kommunikationsmuster Queueing wird vor allem für Load Balancing eingesetzt werden, bei der Nachrichten über mehrere Consumer zur Verarbeitung verteilt und dort verarbeitet werden müssen. Sowohl Producer als auch Consumer können dabei unabhängig von einander skaliert werden.
- Persistente Messaging Systeme können als Event Stores im Rahmen des Event Sourcings eingesetzt werden.
- Messaging ist hinsichtlich Übertragungslatenzen meist performanter als gRPC, gRPC ist üblicherweise performanter als REST.
- Sowohl REST als auch Messaging ermöglichen eine lose Kopplung von Services.
- Die bislang betrachteten Technologien kann man aufsteigend anhand des Grads Ihrer Kopplung ordnen:
......
......@@ -13,7 +13,7 @@ while True:
msg = f"Hi, this is message #{ i } from { me }."
i = i + 1
n = random.randint(1, 5)
time.sleep(n / 10)
time.sleep(n)
print(f"Publishing message '{ msg }' to channel '{ channel }'")
try:
stream.publish(channel, json.dumps({
......
......@@ -8,7 +8,7 @@ channel = os.environ.get("CHANNEL", "xqueue")
queue = MQueue(channel, redis.Redis(host=host, port=port, db=0))
for since, msg in queue.listen_as_group(group="consumers"):
for since, msg in queue.listen():
print(f"{ since }: { msg }")
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment