CDNs und die DSGVO

In Zeiten von weltweit verteilten großen Systemen im Internet und der überwiegend mobilen Bedienung von Webseiten ist die schnelle Datenübertragung an alle Orte auf der Welt ein entscheidendes Thema. Kein Deutscher Urlauber in Amerika möchte eine Ewigkeit auf die heißgeliebte online-Ausgabe der Bild-Zeitung länger als ein paar Sekunden warten. Und auch der durchschnittliche Facebook-Nutzer in Deutschland würden durchdrehen, wenn die Ladezeit wenige Sekunden übersteigt. Aber dazu gibt es ja Content-Delivery-Netzwerke, die uns auf der ganzen Welt verteilt den immer gleichen und stets aktuell gehaltenen statischen Inhalt von Webseiten vorhalten. Hauptsächlich CSS, JavaScript, Symbole und Schriftarten.

CDNs bieten also eine super Funktion, wäre da nicht die EU und der großartige Datenschutz der DSGVO und all die zum Teil schwachsinnigen Urteile von klagenden Menschen die wohl einfach nur zu viel Langeweile hatten… <Sarkasmus aus>

Weiterlesen…

Fog Computing: Solving the limitations of Cloud and Edge Computing

Fog computing offers a compromise between cloud and edge computing for real-time, scalable data analysis. Ideal for regional applications and IoT. However, authentication and privacy issues must be addressed.

Most Cloud Developers seem to agree that the best way to gain knowledge and optimize processes is to collect and store data in the cloud and to analyze the collected data after the fact or in almost-real-time. In a world where the number of users and devices is already almost uncountable, the name Big Data becomes more and more a euphemism for the vast landscape we call the internet. On the other hand, we can also execute data analysis tools on the devices locally in a network using edge computing. This allows developers to work on the data of single devices or single users-grade networks.

Apparently, we can either have all the data or only a little data, but either way is difficult to handle efficiently. This is where fog computing comes into play.

Pyramid graph showing edge devices on the bottom, fog nodes in the middle and cloud data centers at the top.
Architecture placing nodes in the fog between the data centers of the cloud and devices at the edge; from “Moving the Cloud to the Edge”; PubNub; 06.06.2017

What is the Fog?

When it comes to internet services like Internet of Things, web services, Infrastructure-as-a-service, or networking solutions, the limiting factors are latency, networking resources, data storage, and computing power. While most of these problems can be solved through more or faster hardware, there are problems that need analytical and clever solutions. More parallelization, e.g. load balancing multiple servers, and chaining of microservices, e.g. message queues, do not solve the logical problems of conflicting data or processes, e.g. database locking. It is simply physically impossible to manage all requests all the time.

In cloud computing, resources are distributed over the world and information is shared across that ominous, obscure cloud. This makes it possible to handle requests on devices that have availability, in order to handle requests with medium latency and high cumulative computing power. This makes it easy to scale the infrastructure, but makes the analysis of data very time and resource consuming and necessitates to store data permanently from time to time.

Whereas cloud computing handles all requests on a “synchronized” level, where we don’t know and don’t care about the location and type of device that handles a request, edge computing happens at the “edge to the internet”. That means that edge devices or edge networks process data either incoming or outgoing at or close to the user. This approach is often also referred to as “serverless” because no server is needed to handle any requests other than offering the code to create the interface (HTML, CSS etc.) and the code to be executed on edge devices.

With edge computing it is easy to pinpoint the physical and logical “location” and state and type of requests, data and devices. It is possible to handle those requests with extremely low latency, both network and compute, and a low need for computing power and resources. Although the need is low, scalability and access to data is very limited. This means that the calculations and processes done in edge computing are fast, but not always very rich or productive.

How does Fog Computing improve system architecture?

Fog computing places nodes between the edge, where the user with his private devices and networks resides, and the cloud, where nobody can identify any instance for sure, unless one asks the cloud providers themselves. The cloud stores and manages data in a somewhat manageable number of datacenters, and the edge completes processes on billions of devices. The fog on the other hand, consists of servers with numbers in the high thousands or few millions worldwide. This infrastructure allows developers and operators to make a compromise between cloud and edge computing. These servers are physically close to users’ networks.

A table showing a comparison of the characteristics of Cloud, Fog and Edge. The attributes are latency, scalability, distance, data analysis, computing power and interoperability. The relevant content is described in the following paragraphs.
Difference between Cloud, Fog and Edge Computing in IoT; Digiteum; 04.05.2022

The compromise has the following characteristics:

  • The shorter physical distance also implies a low network latency and low need for computing resources due to limited data amounts.
  • Data can be analyzed locally and in real time.
  • Nodes can be scaled flexibly in response to growing numbers of users in small networks and regions with a small priority of single nodes.
  • Nodes in the fog act as handlers between edge networks and the cloud. Urgent requests are forwarded to the cloud, and requests that can be processed locally are.

Connecting edge devices and the cloud through an independent actor allows for more and better control over the incoming and outgoing data. Information can be separated regionally and the usage of the cloud can be controlled in the fog. Forwarding requests is a concern because both the storage of sensitive information and the limitation of needed computing resources is detrimental to the overall quality of the cloud.

Data security and media rights management can be provided through those nodes on a regional basis. Sensitive information is stored locally on fog level and never forwarded to the cloud. The urgency of a request has to be determined on fog nodes. Under normal circumstances, networking bandwidth and computing resources are spared in the cloud by storing data locally whenever necessary and storing it in the cloud when the localization of data is no concern.

Smart Cities: An example for Fog Computing applications

One example for the use of fog nodes are smart cities, meaning independent traffic systems interacting to reduce traffic jams and incidents. Cars, traffic signals and road barriers are installed with sensors to collect data and send that data to a fog node. It is not necessary for a traffic light in New York to gain information about a car in Los Angeles, but the general gist of traffic optimization is the same. Fog computing allows real-time data analysis that enables traffic signals to rapidly change according to the traffic situation.

AI illustration of connected smart cities, Stabillity AI, 22.02.2023

To this end, it is more useful to connect every device in the city to a fog node that is only responsible for that city. That way, millions of fog nods are placed to control traffic in a city or wider city area with its own scaled infrastructure. For example, New York City will most likely need more resources per square mile than Austin, Texas.

This separation of fog nodes leads to a separation of private data, like location data, e.g. home addresses, from the cloud service providers, and between each fog node. Only essential data is forwarded to the cloud and used to generate predictions, update traffic models, and generate newer algorithms and usage analysis.

With this method, degrading performance due to high demand only affects one city. High loads can be forwarded to the cloud, where requests are distributed globally to free resources. This way, the averaged load of the cloud can be used to cushion the impact of high local demand.

Obstacles for the implementation of Fog Computing

  • Authentication and trust issues: Just like cloud service providers, fog service providers can be different parties with varying trust levels. One provider can pose as a trustworthy entity and manipulate the integrity of the fog for its connected end-users.
  • Privacy: Decentralizing data security and media rights management to the fog and outsourcing the responsibility to a third party instead of the cloud or edge devices endangers user’s privacy due to the number of fog nodes.
  • Security: Due to the number of devices connected to fog nodes, it is difficult to ensure the security of all gateways and the protection of personal information.
  • Node placement: The physical and logical location of fog nodes should be optimized to reach the maximum service quality. The data for the right placement has to be analyzed and chosen carefully.

Conclusion

Fog Computing places nodes logically between edge networks and devices and the cloud.

It provides 4 main advantages:

  1. Network latency: Lower distance to the end-user and smaller data consumption leads to lower network delay and lower computing time.
  2. Data analysis: Low data amounts allow for real-time data analysis and the limitation of cloud usage.
  3. Security: Configuring fog nodes to the data protection needs ensures that cloud service providers only gain access to as much data as needed.
  4. Cost reduction: The regional placement and subdivision can minimize the hardware and energy cost for the fog service providers.

Sources

  1. IBM Blog – What is fog computing?
    https://www.ibm.com/blogs/cloud-computing/2014/08/25/fog-computing/
  2. E-SPIN Group – The Examples of Application of Fog Computing
    https://www.e-spincorp.com/the-examples-of-application-of-fog-computing/
  3. TechTarget – What is fog computing?
    https://www.techtarget.com/iotagenda/definition/fog-computing-fogging
  4. YourTechDiet – Brief About The Challenges with Fog Computing
    https://yourtechdiet.com/blogs/fog-computing-issues/
  5. GeeksForGeeks – Difference Between Cloud Computing and Fog Computing
    https://www.geeksforgeeks.org/difference-between-cloud-computing-and-fog-computing/
  6. Sam Solutions – Fog Computing vs. Cloud Computing for IoT Projects
    https://www.sam-solutions.com/blog/fog-computing-vs-cloud-computing-for-iot-projects/
  7. AWS Documentation – Regions and Zones
    https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html
  8. AWS Documentation – AWS Outposts Family
    https://aws.amazon.com/outposts/
  9. AWS Documentation – AWS Global Infrastructure
    https://aws.amazon.com/about-aws/global-infrastructure/
  10. Heavy.ai – Fog Computing Definition
    https://www.heavy.ai/technical-glossary/fog-computing
  11. Akamai – What is edge computing?
    https://www.akamai.com/our-thinking/edge-computing
  12. Cloudflare – What is edge computing?
    https://www.cloudflare.com/learning/serverless/glossary/what-is-edge-computing/
  13. Cloudcomputing Insider – Was ist Fog Computing?
    https://www.cloudcomputing-insider.de/was-ist-fog-computing-a-736757/
  14. CloudPing – AWS Latency Monitoring
    https://www.cloudping.co/grid/p_50/timeframe/1W
  15. Vercel – Edge Functions
    https://vercel.com/features/edge-functions
  16. SpiceWorks – What Is Fog Computing? Components, Examples, and Best Practices
    https://www.spiceworks.com/tech/edge-computing/articles/what-is-fog-computing/
  17. Welotec – Edge Computing, Fog Computing or both?
    https://www.welotec.com/edge-computing-fog-computing-or-both/

Die Zukunft ist Serverless?

Überblick

Die “Cloud” ist ein Begriff, der in den letzten Jahren immens an Bedeutung gewonnen hat. Häufig wird sie für die Bereitstellung von Diensten und Services genutzt. Im Lauf der Zeit haben sich dabei verschiedene Architekturen entwickelt, die in der Cloud eingesetzt werden und unterschiedliche Ansätze für die Handhabung des Codes der Entwickler und die Anfragen von Nutzern haben. Eine davon ist die sogenannte Serverless-Architektur. Doch wie genau funktioniert dieser Ansatz, welche Vor- und Nachteile bietet er und handelt es sich dabei um die Zukunft der Cloud-Entwicklung?

Wie bereits erklärt handelt es sich bei Serverless (auf deutsch serverlos) um eine Architektur zur Entwicklung von Anwendungen in der Cloud. Der Name lässt vermuten, dass es bei diesem Ansatz darum geht, auf die Verwendung von Servern zu verzichten. Ganz ohne Server funktioniert es natürlich nicht, da die Anwendung und Services trotzdem über das Internet (und damit über irgendeine Art von Server) für die Nutzer erreichbar sein müssen.

Funktionsweise

Das “Serverlose” bezieht sich darauf, wie die Entwickler mit der Cloud umgehen und mit ihr interagieren. Denn im Gegensatz zum klassischen Cloud-Computing muss sich das Entwicklerteam hier keine Gedanken über die Bereitstellung der Anwendung für die Nutzer, die Skalierung oder die Verwaltung des Codes machen. Diese Aufgaben übernimmt alle der Cloud-Anbieter und folgt damit ein bisschen dem Prinzip “aus dem Auge aus dem Sinn”, da die Entwickler ihren Code nur hochladen müssen und sich danach keine, bzw. nur noch wenige Gedanken mehr machen müssen.

Man unterscheidet dabei zwischen BaaS (Backend-as-a-Service) und Faas (Function-as-a-Service). Die zwei Möglichkeiten differenzieren, was genau der Cloud-Anbieter bereitstellt.
Bei BaaS kann man auf bereits vorhandene Dienste zurückgreifen, die der Cloud-Anbieter schon im Repertoir hat. AWS Amplify beispielsweise ermöglicht es den Entwicklern, einfach Datenbanken, Authentifizierung, etc. nach ihren Wünschen zu konfigurieren und einzubauen.
Bei FaaS wird auf diese Art der Drittanbieter-Services verzichtet und die Entwickler schreiben ihre Logik und System selbst. Statt das Ganze dann auf einem eigenen Server laufen zu lassen, wird der Code bei dem Cloud-Anbieter hochgeladen, der diesen dann verwaltet. Je nach Bedarf wird dann durch Events der passende Code aufgerufen und ausgeführt, ohne dass der Entwickler dies manuell handhaben muss.

Abb. 1: Monolithische Architektur und Microservice-Architektur [4]

Zusammenfassend bietet Serverless also wie in Abbildung 1 dargestellt den Zugang zu vordefinierten Services, zu Möglichkeiten der Datenverwaltung, zu Monitoring über das System mit Metriken und Dashboards und natürlich eigene Zugangspunkte, die die Services und Funktionen der Entwickler ausführen.

Doch wie sieht der Code, beziehungsweise das gesamte System eigentlich aus, dass so ein Ansatz funktionieren kann? Im Grunde MUSS der Code keine besondere Struktur aufweisen. Serverless bezieht sich lediglich auf die Bereitstellung des Systems über einen Cloud-Anbieter, der sich um die Verwaltung dessen kümmert und diese Aufgabe von den Entwicklern abnimmt.
Es hilft jedoch, das System in einzelne, kleinere Services oder sogar einzelne Funktionen zu unterteilen, die unabhängig voneinander funktionieren, um die Vorteile von Serverless besser nutzen zu können. Hier kommt der Vergleich zwischen der traditionellen,  monolithischen Software-Architektur und der Microservice-Architektur ins Spiel.

Abb. 2: Monolithische Architektur und Microservice-Architektur [3]

Bei einer monolithischen Software-Architektur (Abbildung 2 links) wird das System klassischerweise in ein Frontend, Backend und die Datenbankanbindung unterteilt. Die drei Teile arbeiten eng zusammen und sind stark voneinander abhängig. Sie lassen sich kaum auseinanderziehen oder separieren.
Im Gegensatz dazu nutzt die Microservice-Architektur (Abbildung 2 rechts) hinter dem Frontend mehrere, unabhängige Services (daher der Name). Diese können auch unterschiedliche Datenbanken verwenden. Mit diesem Ansatz wird die Logik des Backends in kleinere Teile aufgedröselt. Besonders wichtig ist dabei die angesprochene Unabhängigkeit. Die Services sind dazu in der Lage, ihre Aufgabe selbstständig zu erfüllen, können dazu auf ihre Datenbank zugreifen und müssen nicht andere Services hinzuziehen. Beispiele für solche Services könnten die Authentifizierung von Nutzern, das Suchen nach Produkten oder das Kaufen von Produkten sein.

Betrachtet man nun eine Microservice-Architektur und lässt diese Serverless in der Cloud betreiben, kann man den Nutzen von Serverless gut erkennen: Die einzelnen Services können separat voneinander gestartet und ausgeführt werden. Die Entwickler definieren eventbasierte Funktionen (siehe “Event-driven Computing”), um beim Eintreten eines Events den passenden Service zu starten und auszuführen. Wenn beispielsweise ein Nutzer nach einem Produkt auf unserer Webseite sucht, wird der Such-Service hochgefahren, dieser durchsucht die Datenbank nach passenden Treffern und gibt diese an das Frontend zurück. Nachdem der Service diese Aufgabe erledigt hat, wird er durch den Cloud-Anbieter wieder heruntergefahren, wenn keine weiteren Anfragen hinzukommen.

Zusammenfassend lässt sich zu der Funktionsweise von Serverless sagen, dass es sich dabei um eine Architektur oder ein Modell zur Verwaltung und Bereitstellung des Systems über einen Cloud-Anbieter handelt. Dieser übernimmt Funktionen, wie das Hoch- und Runterskalieren von Services/Funktionen nach Bedarf durch die Bereitstellung von passender Rechen- und Speicherkapazität, die Wartung der darunterliegenden Hardware und stellt bei Bedarf auch vordefinierte Services bereit.

Vorteile

Nun stellt sich die Frage, welche Vorteile der Serverless-Ansatz mit sich bringt und wann es Sinn macht, ihn einzusetzen:

  • Skalierbarkeit: Ein bereits angesprochener und auch einer der zentralsten Punkte ist die Skalierbarkeit. Es können je nach Bedarf neue Ressourcen für das System bereitgestellt und auch wieder zurückgenommen werden. Dadurch ist es möglich, die Anfragen an das System effektiv zu beantworten und gleichzeitig effizient mit den Ressourcen umzugehen.

  • Geringe Kosten: Das führt auch direkt zu dem zweiten Vorteil, nämlich den geringeren Kosten. Da die Rechenleistung nur dann verwendet wird, wenn sie auch wirklich benötigt wird, zahlt der Kunde des Cloud-Anbieters auch nur für die wirklich genutzten Ressourcen. Das bedeutet, dass wenn wenig Last vorhanden ist und dementsprechend auch nur wenig Rechenleistung benötigt wird, die Kosten für das System gering gehalten werden und man nicht beispielsweise einen eigenen Server, der dauerhaft erreichbar ist, bereitstellen muss.

  • Geringerer Aufwand: Für die Entwickler selbst verringert sich der Aufwand durch die wegfallenden Verwaltungs- und Wartungsaufgaben. Es muss keine Zeit für die Instandhaltung, Administration oder Konfiguration der Server aufgebracht werden, wodurch mehr Zeit in die eigentliche Entwicklung der Software gesteckt werden kann. Bei der Verwendung von BaaS lässt sich zudem auf bereits vorhandene Services des Anbieters zurückgreifen, wodurch noch mehr Zeit eingespart werden kann.

  • Sicherheit und Zuverlässigkeit: Die Cloud-Anbieter stellen Sicherheitsmaßnahmen zum Schutz des Systems und der Daten bereit. Nicht nur sind die Daten dadurch professionell geschützt, sondern auch hier sparen sich die Entwickler wieder Zeit, da sie sich nicht selbst um die Sicherheit und Zuverlässigkeit des Systems kümmern müssen, sondern diese durch den Anbieter umgesetzt werden.

Ihre Vorteile kann die Serverless-Architektur am Besten ausspielen, wenn die dafür genutzte Applikation auch auf die genannten Vorteile eingeht. So macht es beispielsweise Sinn, Serverless einzusetzen, wenn man mit sehr unterschiedlichem Verkehrsaufkommen rechnen muss. Hier kann Serverless durch die einfache Skalierbarkeit und auch die dadurch effizientere Kostenverteilung besonders hilfreich sein, da man bei geringem Verkehr nur wenig bezahlt und bei vielen Anfragen diese trotzdem schnell behandeln kann.

Nachteile

Natürlich hat Serverless auch seine Nachteile und Probleme, die in diesem Abschnitt aufgegriffen und erläutert werden.

  • Startzeit: Da die einzelnen Services/Funktionen erst ausgeführt werden, wenn sie auch wirklich gebraucht werden, müssen sie zu Beginn einer Anfrage und wenn noch kein anderer, freier Service derselben Art gerade läuft, erst gestartet werden. Dies wird als “Cold Start” bezeichnet. Das Starten eines solchen Services beinhaltet das Starten eines Containers mit dem passenden Code, das Laden der dazugehörigen Packages und die eigentliche Ausführung des Services. Bei Anwendungen, die auf Serverless verzichten, fällt dieser Cold Start weg, da das System ja bereits komplett läuft.

  • Weniger Kontrolle: Der größte Nachteil ist wahrscheinlich der Verlust von Kontrolle in vielerlei Hinsicht. Zum einen hat man keine Möglichkeit mehr, die Hardware selbst zu konfigurieren und zu managen. Darunter fällt auch die Macht zu entscheiden, welche Sicherheitsvorkehrungen genau implementiert werden, welche Updates und Versionen von Software benutzt werden und vieles mehr. Bei der Nutzung von BaaS ist man zudem an den Code des Cloud-Anbieters gebunden und kann keine eigenen Optimierungen oder Anpassungen vornehmen.
    Außerdem liegen die verwendeten Daten auch nicht mehr bei den Entwicklern selbst auf einem Server, sondern müssen dem Cloud-Anbieter anvertraut werden.

  • Abhängigkeit: Ein weiterer Nachteil ist die Abhängigkeit vom Cloud-Anbieter. Lässt man sich auf sein System ein und verwendet möglicherweise von ihm bereitgestellte Services und Funktionen, ist ein Wechsel zu einem anderen Anbieter schwierig und kompliziert, da dieser eventuell nicht dieselben Funktionen oder Workflows anbietet.

Evaluierung

Betrachtet man die genannten Vor- und Nachteile von Serverless, so lässt sich sagen, dass Serverless durchaus viele relevante Vorteile mit sich bringt, die es als die Cloud-Architektur der Zukunft rechtfertigen würde. Besonders die Einfachheit, die durch die automatische Skalierung ermöglicht wird und damit gleichzeitig das System kostengünstiger und einfacher bereitzustellen macht, sind wichtige Kriterien.

Natürlich muss man die Abhängigkeit vom Cloud-Anbieter und den Kontrollverlust in Betracht ziehen. Besonders letzteres schließt Serverless für manche Unternehmen aus, die den vollen Zugang zu den Servern benötigen und selbst an dem System aktiv arbeiten wollen. Für viele Unternehmen eröffnet Serverless jedoch eine Möglichkeit, sich voll und ganz auf die Entwicklung der Anwendung zu konzentrieren.

Wie bei vielen Aspekten ist Serverless ein Angebot für einen Tausch. Man bekommt Effizienz und Kostenersparnis, büßt dabei aber Kontrolle und Zugänglichkeit ein. Im Endeffekt muss jedes Unternehmen selbst entscheiden, mit welcher Architektur sie arbeiten wollen.
Ich persönlich denke, dass der Ansatz besonders für kleine Unternehmen und Startups eine gute Möglichkeit bietet, ihr Produkt auf den Markt zu bringen, ohne sich über die Verwaltung der Server Gedanken machen zu müssen, sondern sich auf ihr Produkt selbst fokussieren zu können. Denn besonders für diese Unternehmen ist es sehr aufwändig und vor allem teuer, die benötigte Hardware bereitzustellen und auch funktionstüchtig und sicher zu halten. Fällt diese Komponente weg, so können mehr Entwickler das tun, was sie wirklich tun wollen und auch können, nämlich entwickeln und müssen sich nicht mehr um die Bereitstellung ihres Systems sorgen.

Dementsprechend halte ich Serverless durchaus für eine Architektur, die die Zukunft weiter mitgestalten wird.

Ausblick

Neben den angesprochenen Aspekten, warum Serverless vermutlich in der Zukunft weiterhin stark vertreten sein wird, sollte man sich auch etwas Gedanken über die nötige Weiterentwicklung machen. Meiner Ansicht nach ist Serverless besonders für die steigende Anzahl an IoT-Geräten und Applikationen eine optimale Lösung. Oft bieten diese nämlich genau das, was Serverless bereitstellt: Kleine Funktionen und Services, die schnell ausgeführt werden, die hochskaliert werden müssen und die dadurch dem Entwickler einen kostengünstigen Weg bieten, diese Aspekte umzusetzen.

Ich könnte mir vorstellen, dass viele Systeme aufgrund dieser Einfachheit in die Cloud verlegt werden. Dadurch könnte es möglich sein, dass in der Zukunft viele dieser Systeme sich auch untereinander vernetzen. Es entsteht quasi eine große Welt aus miteinander verknüpften Services und Diensten, die auch die Entwicklung neuer Systeme einfacher gestaltet. Denn Cloud-Anbieter werden ihr Repertoir kontinuierlich erweitern und eine immer größere Sandbox aus vordefinierten Services erstellen, mit der Entwickler ihre Ideen schnell und einfach umsetzen können.

Quellen

[1] What is serverless? https://www.redhat.com/en/topics/cloud-native-apps/what-is-serverless

[2] What is Function-as-a-Service (FaaS)? https://www.redhat.com/en/topics/cloud-native-apps/what-is-faas

[3] The Pros and Cons of a Monolithic Application Vs. Microservices, https://www.openlegacy.com/blog/monolithic-application

[4] Serverless Architecture – The What, When and Why https://www.cloudnowtech.com/blog/serverless-architecture-the-what-when-and-why/

[5] Serverless vs Microservices: What Should You Choose For Your Product? https://www.ideamotive.co/blog/serverless-vs-microservices-architecture

[6] Was ist Serverless Computing? https://www.vodafone.de/business/featured/technologie/was-ist-serverless-computing/

[7] Introduction to Serverless Services https://blog.camelot-group.com/2022/05/introduction-to-serverless-services/

[8] Why Serverless Is the Future of Application Development https://www.arvato-systems.com/blog/why-serverless-is-the-future-of-application-development

[9] Serverless Computing: Deswegen sind Server nicht die Zukunft https://t3n.de/news/serverless-computing-deswegen-sind-server-nicht-die-zukunft-849986/

Ansätze für nachhaltige Softwareentwicklung in Large-Scale Systems

Einleitung

Der Klimawandel ist in den letzten Jahren weltweit zu einer großen Herausforderung geworden. Der Ausstoß von Treibhausgasen ist nach wie vor hoch. Nach Angaben des Umweltbundesamtes gehen die CO2-Emissionen in Deutschland zwar kontinuierlich zurück, weltweit ist der Trend jedoch weiterhin steigend, so dass die globalen Kohlendioxidemissionen kontinuierlich zunehmen [1]. Aufgrund der Corona-Pandemie konnte zwar ein deutlicher Rückgang der Emissionen in den Vorjahren verzeichnet werden, dennoch steigen die Zahlen wieder an [2]. Sollte es in Zukunft nicht gelingen, den hohen Energieverbrauch und den Ausstoß von CO2 nachhaltig zu reduzieren, muss mit Umweltkatastrophen gerechnet werden. In Zeiten der Digitalisierung, spielt auch die Softwareentwicklung eine große Rolle beim Thema Umweltschutz. Die Auswirkungen der IT auf die Umwelt haben in den letzten Jahrzehnten durch die Zunahme der Digitalisierung zugenommen. Digitalisierung und innovative Technologien sollen CO2-Emissionen reduzieren. Riesige Rechenzentren, Datenzentren und IT-Infrastrukturen sowie große Datenmengen benötigen jedoch viel Energie. Der Energiebedarf in diesem Sektor ist in den letzten Jahren stark angestiegen.

Abbildung 1: Energiebedarf von Rechenzentren in Europa pro Jahr (Quelle: https://www.borderstep.de/2020/11/30/deutlicher-anstieg-des-energiebedarfs-der-rechenzentren-im-jahr-2020/)

Die wachsende Zahl von Nutzern und die Nachfrage nach umfangreichen Inhalten (Cloud-Computing und Big-Data-Anwendungen, soziale Netzwerke und Foto-Sharing, Video Streaming auf Abruf usw.) erfordern enorme Bandbreiten, sowie Rechen- bzw. Speicherkapazitäten. Distributed Large-Scale Systems werden daher immer leistungsfähiger und größer. Large-Scale Systems, also softwareintensive Systeme und high-performance Computing Cluster mit einer enormen Menge an Hardware, Quellcodezeilen, Benutzerzahlen und Datenmengen haben daher ebenfalls einen Einfluss auf den CO2-Fußabdruck. Large-Scale Systeme benötigen oft eine große Menge an Energie für die Stromversorgung und Kühlung von Servern, Netzwerkausrüstung und Speichergeräten. Schätzungen zufolge benötigt das Internet durchschnittlich 240 GW Energie [3], was etwa 12,6% der weltweit erzeugten elektrischen Energie entspricht.

Moderne Technologien, sowie verbesserte Hardware, Software und Rechenzentruminfrastrukturen werden zwar immer effizienter, dennoch ist es nicht gelungen, den Anstieg im Energiebedarf zu senken. Um dem entgegenzuwirken gibt es verschiedene Möglichkeiten. Durch grüne und nachhaltige IT (Green IT) und Green Coding können Energieeinsparungen erzielt werden.

“Green IT is the study and practice of designing, manufacturing, using, and disposing of computers, servers, and associated subsystems (such as monitors, printers, storage devices, and networking and communications systems) efficiently and effectively with minimal or no impact on the environment” [4].

Software alleine verbraucht zwar weder Energie, noch stößt sie Emissionen aus, doch das Problem ist, wie die Software entwickelt wird. Es gibt hier verschiedene Techniken, um effizientere und performantere Software zu entwickeln und auch Möglichkeiten Large-Scale Systems nachhaltig zu entwerfen.

Softwareentwicklung und ihre Auswirkungen auf die Umwelt

Beim Menschen denkt man zunächst, dass der größte CO2-Ausstoß durch die Atmung erfolgt. Das ist etwa 1 kg pro Tag. Tatsächlich produziert ein Mensch aber ca. 30 kg CO2 pro Tag, z.B. durch Essen, Reisen und die Nutzung elektronischer Geräte [5]. Der Anteil der digitalen Aktivitäten beträgt 4% der gesamten CO2-Emissionen. Im Jahr 2040 wird dieser Anteil voraussichtlich 14% betragen [6]. Ein Laptop, der an 200 Arbeitstagen täglich acht Stunden in Betrieb ist, verursacht einen jährlichen CO2-Ausstoß von 88 kg. 80% des Datenverkehrs im Netz ist auf Videostreaming zurückzuführen. Netflix gibt z.B. an, dass eine Stunde Video-Streaming einen CO2-Ausstoß von 100 Gramm pro Nutzer verursacht.

Hier muss man den immensen Skalierungsfaktor mitberücksichtigen, so muss dieser Wert auch auf Millionen von Nutzern hochgerechnet werden. Wenn ein Netflix-Entwickler in der Lage wäre, den CO2-Ausstoß pro Stunde um einige wenige Gramm zu reduzieren, könnte eine deutliche Reduktion der Emissionen erzielt werden. Es gibt Best Practices für Entwickler und IT-Architekten, die zu Green Coding beitragen.

Green Coding bedeutet, Software effizienter zu gestalten, um den Energieverbrauch und damit auch die CO2-Emissionen zu reduzieren [7].

Effiziente Softwaretechniken

Allgemein gilt es, Best Practices einzuhalten, um sauberen und effizienteren Code zu schreiben. Code, der für Leistung und Ressourcennutzung optimiert ist, kann dazu beitragen, den Energie- und Stromverbrauch von Software zu senken. Es gibt verschiedene Techniken [8][9].

  • CPU: Ein Ziel ist es, den Verbrauch der CPU zu senken. Dies kann unter anderem mit Hilfe von effizienten mathematischen Operationen (in Bezug auf ihre Zeitkomplexität) erreicht werden. Berechnete Ergebnisse können wiederverwendet werden und bedingte Verzweigungen, wenn möglich reduzieren.
  • Memory: In Bezug auf Memory sollte man eher lokale Variablen, statt globaler Variablen verwenden und wenn möglich die Codegröße verringern.
  • Loops: Optimierungsmethoden verwenden wie z.B. Loop Unrolling, um die Laufzeit eines Programms zu beschleunigen. Mehrere Loops sollten, wenn möglich zusammengeführt werden.
  • Optimale Algorithmen: Im Allgemeinen kann jede Strategie, die die Laufzeit eines Algorithmus verkürzt, hilfreich sein zur Reduzierung des Energieverbrauchs. Die Komplexität des Algorithmus lässt sich in Form der Big O-Notationen berechnen. Algorithmen, die eine lineare Komplexität aufweisen, sind energiebewusster als solche mit exponentieller Komplexität.
  • Effiziente Datenstrukturen: Wie z.B. Linked Lists. Datenstrukturen haben erhebliche Auswirkungen auf die Ausführung eines Programms und die Energieeinsparung.
  • Multithreading: Schnellere Berechnungen durch Parallelisierung ermöglichen.
  • Event-Driven Architectures: Anstatt Request Driven, die oft viele Anfragen haben, sollten eventgetriebene Architekturen bevorzugt werden. Das Ziel ist es die Zahl der Serveranfragen auf ein Mindestmaß zu reduzieren.
  • Caching: Zwischenspeicherungen von Anfragen auf dem Client durch Caching kann dazu beitragen, unnötige Round-Trips innerhalb eines Netzwerks, sowie teure und energieintensive Zugriffe auf Festplatten oder Datenbanken zu vermeiden.
  • Effiziente Programmiersprachen: Einige Programmiersprachen sind effizienter als andere – Die Auswahl der Programmiersprache hat ebenfalls Einfluss auf den Energieverbrauch.
Abbildung 2: Vergleich einiger Programmiersprachen in Bezug auf Energieeffizienz (Quelle: Illustration by Kasper Groes Albin Ludvigsen. Source: Source: Pereira, R. et al. (2017))

Ein Vergleich zeigt, dass die Programmiersprache C in Bezug auf Energie am effizienten abschneidet. Python hingegen verbraucht ca. 70x mehr Energie als C. Die Auswahl einer effizienten Programmiersprache kann somit einen Teil zur Reduzierung des CO2-Fußabdrucks beitragen.

Abbildung 3: Energieverbrauch verschiedener Programmiersprachen (Quelle: https://alanstorm.com/the-thing-about-that-energy-efficiency-of-programming-languages-table/)

Cloud Computing und Nachhaltigkeit

Cloud Computing hat in den letzten Jahren stark an Popularität gewonnen. Ein Cluster aus mehreren Computern mit geringer Kapazität ist in der Regel kosten- und leistungsmäßig günstiger als ein einzelner Hochleistungscomputer, da ein Hochleistungscomputer seine Ressourcen nicht voll ausnutzt und daher mehr Energie verbraucht. Verteilte Systeme oder Cloud Computing können zu einer Leistungssteigerung bei gleichzeitiger Energieeinsparung führen.

Laut einer Studie von Microsoft, Accenture und WSP-Environment & Energy können große Unternehmen durch die Verlagerung ihrer Anwendungen in die Cloud den Energieverbrauch und die Kohlendioxidemissionen um 30% oder mehr senken (im Vergleich zu On-Premise) [10]. Bei kleineren Unternehmen sind es sogar bis zu 90%. Auf den ersten Blick scheint Cloud Computing wegen der riesigen Rechenzentren und des enormen Energieverbrauchs wenig mit Nachhaltigkeit zu tun zu haben. Tatsächlich sind sie aber umweltfreundlicher als On-Premise-Lösungen [11].

  • Höhere Nutzungsrate: Um eventuelle Ausfallzeiten zu vermeiden, setzen Unternehmen traditionell viel mehr Server ein (Redundanz), als sie tatsächlich benötigen. Als Folge, haben die Rechenzentren vor Ort eine extrem niedrige Auslastungsrate und nutzen im Durchschnitt nur 15% ihrer Kapazität.
  • Umstellung auf erneuerbare Energien: Viele Cloud-Anbieter haben in erheblichem Umfang in erneuerbare Energiequellen wie Wind- und Solarenergie investiert. So soll AWS bereits mehr als 50% erneuerbare Energie nutzen. Indem sie ihre Rechenzentren mit erneuerbarer Energie versorgen, können Cloud-Anbieter ihre Kohlenstoffemissionen erheblich reduzieren. Google hat bereits im Jahr 2007 den gesamten CO2-Ausstoß kompensiert, den sie verursacht hat.
  • Skaleneffekte (Economies of Scale): Cloud-Anbieter betreiben riesige Rechenzentren, die auf Energieeffizienz optimiert sind, z. B. durch den Einsatz moderner Kühltechnologien und energieeffizienter Hardware. Diese großen Rechenzentren profitieren vom sogenannten Skaleneffekt, die es ihnen ermöglichen, eine höhere Energieeffizienz zu erreichen als kleinere, vor Ort betriebene Rechenzentren.
  • Geringere Hardwareverschwendung: Durch die Nutzung gemeinsam genutzter Rechenressourcen, kann Cloud-Computing dabei helfen, die Menge der für eine bestimmte Arbeitslast erforderlichen Hardware reduzieren. Dies kann dazu beitragen, die Menge an elektronischem Abfall zu verringern.

Green Computing für Large-Scale Systems

Die Verringerung des Energieverbrauchs kann zur Verringerung der Umweltbelastung beitragen. Neben effizienten Techniken in der Softwareentwicklung kann auch ein hardwarebasierter Ansatz den Stromverbrauch senken, indem alte, ineffiziente Hardware durch neuere, energieeffizientere Alternativen ersetzt wird. Problematisch sind jedoch die erheblichen finanziellen Kosten, die mit der Aufrüstung der Hardware verbunden sind.

Ein Ziel ist es, unter anderem Data Centers so zu gestalten, dass sie möglichst energieeffizient und nachhaltig sind. Dabei können verschiedene Bereiche optimiert werden, wie z.B. der Einsatz von erneuerbaren Energien, die Verbesserung der Energieeffizienz durch bessere Kühlung und Luftstrommanagement, Virtualisierung und Konsolidierung von Servern.

Abbildung 4: Bereiche, in denen der Energieverbrauch von Rechenzentren reduziert werden kann

Nachfolgend sind einige Optimierungsmethoden, die für Large-Scale Systems angewandt werden können [12].

  • Load Balancing: Indem die Arbeitslast auf verschiedene Server verteilt wird, kann der Energieverbrauch jedes Servers optimiert werden. Der Lastausgleich kann dazu beitragen, eine Überbeanspruchung von Ressourcen zu verhindern, die zu einem höheren Energieverbrauch und Kohlendioxidausstoß führen kann.
  • Parallel Processing: Die Parallelverarbeitung kann dazu beitragen, die Zeit zu verkürzen, die für die Ausführung einer Aufgabe benötigt wird, was zu einem geringeren Energieverbrauch und geringeren Kohlendioxidemissionen führen kann. Durch die Aufteilung großer Aufgaben in kleinere Aufgaben und deren parallele Verarbeitung kann der Zeit- und Energieaufwand für die Ausführung der Aufgabe verringert werden.
  • Virtualisierung: Technologien zur Virtualisierung können dazu beitragen, die Ressourcennutzung zu optimieren und die Anzahl der physischen Server zu verringern, die für den Betrieb eines großen Systems erforderlich sind. Durch die Konsolidierung mehrerer Anwendungen auf einem einzigen Server kann die Virtualisierung zur Senkung des Energieverbrauchs und der Kohlendioxidemissionen beitragen.
  • Automatisierte Skalierung: Die automatische Skalierung ermöglicht es, ein System entsprechend der aktuellen Nachfrage zu vergrößern oder zu verkleinern. Dies kann zur Senkung des Energieverbrauchs und der Kohlendioxidemissionen beitragen, da nur die benötigten Ressourcen genutzt werden.
  • Effizientes Datenbank Designing: Durch die Optimierung des Datenbankdesigns kann der Energiebedarf für die Speicherung und den Abruf von Daten gesenkt werden. Dies kann durch die Verwendung effizienter Datenstrukturen, die Minimierung der Anzahl von Abfragen und die Zwischenspeicherung von Daten erreicht werden.
  • Monitoring und Analyse: Durch das Monitoring der Leistung eines Systems und die Analyse der Daten können Bereiche ermittelt werden, in denen der Energieverbrauch und die Kohlendioxidemissionen reduziert werden können. Dies kann bei Entscheidungen über die Optimierung und Ressourcenzuweisung helfen.
  • Erneuerbare Energien: Auch das ist ein wichtiger Punkt. Wie bei den meisten Cloud-Anbietern, kann der Einsatz erneuerbarer Energiequellen wie Sonnen-, Wind- oder Wasserkraft zur Versorgung der Rechenzentren und anderer Infrastrukturkomponenten den CO2-Fußabdruck verringern.
  • Nachhaltige Softwareentwicklungspraktiken: Wie bereits im oberen Abschnitt erwähnt, können effiziente Softwaretechniken zur Förderung der Nachhaltigkeit führen. Auch die Verwendung umweltfreundlicher Materialien für Hardwarekomponenten und die Minimierung von Abfall können zu einer nachhaltigeren Entwicklung beitragen.
  • Verwendung von Cloud-Computing: Es gibt viele quelloffene Software- und Hardware Ressourcen, die von verschiedenen Anbietern als Service zur Verfügung gestellt werden. Die Verwendung dieser vorgefertigten Ressourcen in Programmen ist vorteilhaft in Bezug auf Energiekosten und Zeit. Cloud-Computing bietet zudem eine bedarfsgerechte Skalierbarkeit, die es Large-Scale Systemen ermöglicht, ihre Rechenressourcen je nach Bedarf schnell und einfach nach oben oder unten zu skalieren.

Durch Verwendung dieser Methoden, können Large-Scale Systems ihren Energieverbrauch und ihren ökologischen Fußabdruck verringern.

Fazit

Zusammenfassend lässt sich sagen, dass nachhaltige Softwareentwicklung ein wichtiger Aspekt im Kampf gegen den Klimawandel und zur Reduzierung der CO2-Emissionen ist. Während die Digitalisierung oft als Lösung zur Verringerung des CO2-Fußabdrucks angesehen wird, ist es entscheidend, den Energieverbrauch und die CO2-Emissionen zu berücksichtigen, die mit Large-Scale Systemen und IT-Infrastrukturen verbunden sind. Green IT und Green Coding sind dabei Ansätze, die dazu beitragen können, die Umweltauswirkungen der Softwareentwicklung zu verringern. Oft sind es kleine Maßnahmen die in der Summe den Unterschied bringen.

Effiziente Softwaretechniken, wie die Optimierung des Codes im Hinblick auf Leistung und Ressourcennutzung, können den Energieverbrauch und die CO2-Emissionen senken. Außerdem kann die Optimierung von Systemarchitekturen und Large-Scale Systems durch Load Balancing und Parallelverarbeitung den Energieverbrauch senken und die Nachhaltigkeit verbessern. Dabei können effiziente Softwaretechniken, sowie die Erweiterung in die Cloud, als Basis dienen. Jedoch muss man auch hier beachten, dass durch die horizontale Skalierung auch mehr Komponenten benötigt werden, die ebenfalls teuer in der Beschaffung sind

Des Weiteren sind moderne Technologien und effizientere Hardware ebenfalls gute Optionen, die jedoch oft mit höheren Kosten verbunden sind. Hier müssen Unternehmen entscheiden wie sie vorgehen möchten. Investieren sie in die Nachhaltigkeit und leisten damit einen Beitrag für grüne IT oder wollen sie diesen Aspekt ignorieren, um Kosten einzusparen? Vor dieser Herausforderung stehen viele Unternehmen. Darüber hinaus stellen die eingeschränkte Verfügbarkeit bestimmter wichtiger Ressourcen, das Fehlen der notwendigen Infrastruktur und Vorschriften weitere potenzielle Einschränkungen dar.

Damit Energieeinsparungen aber wirklich effizient durchgeführt werden kann, muss detailiertes Energiemonitoring betrieben werden. Dieser Bereich wird oft noch vernachlässigt, da er meist mit einem Mehraufwand verbunden ist. Hier bleibt abzuwarten, wie die großen Cloud Anbieter in Zukunft die Sache angehen werden.

Eines ist aber sicher: Jeder, auch wir, die in der IT-Welt arbeiten, können einen (wenn auch kleinen) Beitrag zur Nachhaltigkeit leisten. Bereits mit kleinen Veränderungen kann man seinen persönlichen CO2Fußabdruck verkleinern.

Quellen

[1] Statista Research Department, CO2-Emissionen weltweit in den Jahren 1960 bis 2021, 11.11.2022 [online] https://de.statista.com/statistik/daten/studie/37187/umfrage/der-weltweite-co2-ausstoss-seit-1751/ [abgerufen am: 14.02.2023]

[2] Umweltbundestamt, Kohlendioxid-Emissionen, 21.03.2022 [online] https://www.umweltbundesamt.de/daten/klima/treibhausgas-emissionen-in-deutschland/kohlendioxid-emissionen#herkunft-und-minderung-von-kohlendioxid-emissionen [abgerufen am: 14.02.2023]

[3] Raghavan B, Ma J. The energy and emergy of the Internet. Proceedings of the 10th ACM
Workshop on Hot Topics in Networks (HotNets-X); 2011 Nov; Cambridge (MA); 2011.

[4] Murugesan, S. 2010. Making IT Green. IT Professional, IEEE Computer Society, 12, 2, 4–5.

[5] Gasgag, Wie viel CO2 produziert ein Mensch, 14.02.2022 [online] https://www.gasag.de/magazin/nachhaltig/co2-ausstoss-mensch [abgerufen am: 14.02.2023]

[6] Sanjay Podder, Adam Burden, Shalabh Kumar Singh, and Regina Maruca, How Green Is Your Software? 18.09.2020 [online] https://hbr.org/2020/09/how-green-is-your-software [abgerufen am: 13.02.2023]

[7] Ernst & Munsch UG (haftungsbeschränkt), Green Coding: Nachhaltige Software durch CO2-Reduktion, 22.12.2022 [online] https://www.presseportal.de/pm/164270/5400500 [abgerufen am: 14.02.2023]

[8] Gabor Meißner, Aller Anfang ist schwer: Ansätze für Green Software-Engineering, 04.01.2022 [online] https://www.adesso.de/de/news/blog/aller-anfang-ist-schwer-ansaetze-fuer-green-software-engineering.jsp [abgerufen am: 13.02.2023]

[9] Naik K (2010) A Survey of Software Based Energy Saving Methodologies for Handheld Wireless Communication Devices. Tech. Report No. 2010-13. Dept. of ECE, University of Waterloo

[10] Gina Roos, Cloud Computing Can Cut Carbon Emissions by 30% to 90%, 05.11.2010 [online] https://www.environmentalleader.com/2010/11/cloud-computing-can-cut-carbon-emissions-per-user-by-30/ [abgerufen am: 13.02.2023]

[11] Nitin Singh Chauhan, Ashutosh Saxena. A Green Software Development Life Cycle for Cloud Computing. 1 (2013). https://condor.dpu.depaul.edu/yhwang1/Articles_KHU/article_8.pdf

[12] Fakhar, F., Javed, B., ur Rasool, R. et al. Software level green computing for large scale systems. J Cloud Comp 1, 4 (2012). https://doi.org/10.1186/2192-113X-1-4

Multiplayer Game with AWS | StadtLandFluss

Dieser Blogbeitrag soll einen Einblick in die Entwicklung unserer Webanwendung mit den unten definierten Funktionen geben sowie unsere Lösungsansätze, Herausforderungen und Probleme aufzeigen. 

Cloud Computing Vorlesung

Ziel der Vorlesung “Software Development for Cloud Computing” ist es, aktuelle Cloud Technologien kennen zu lernen und diese im Rahmen von Übungen und kleinen Projekten anzuwenden. Unser Team hat sich im Rahmen dieser als Prüfungsleistung zu erbringenden Projektarbeit dazu entschieden, das bekannte Spiel „Stadt, Land, Fluss“ als Multiplayer-Online Game umzusetzen. 

Projekt Idee & Inspiration

Zu Beginn der Vorlesung war sich unsere Projektgruppe noch sehr unsicher, was wir als Projekt mit Cloudkomponenten umsetzen wollten, da wir noch keine bis sehr geringe Vorerfahrung in der Cloud-Entwicklung hatten. Erste Brainstormings hatten ergeben, dass wir gerne eine Webanwendung entwerfen wollten. Jedoch war es gar nicht so leicht Zugriff zu interessanten Daten zu bekommen. 

Letztendlich hat sich unsere Gruppe dazu entschieden, sich nicht von Daten abhängig zu machen, sondern etwas Eigenes zu kreieren. 

Die Inspiration für unsere finale Idee (Stadt-Land-Fluss) war das Online-Spiel Skribbl IO, ein kostenloses Multiplayer-Zeichen- und Ratespiel. Dabei wird in jeder Runde ein Spieler ausgewählt, der etwas zeichnet, das die anderen erraten sollen. Skribbl ermöglicht es dem Spieler auch, einen eigenen Raum zu erstellen und Freunde einzuladen, die einen Link zu diesem Raum teilen.

Im Rahmen unseres Projektes hat uns die Idee gefallen etwas zu entwickeln, was man danach mit Freunden zusammen nutzen kann. Den Multiplayer Ansatz fanden wir spannend, da wir so etwas noch nie umgesetzt haben. Da wir alle Stadt-Land-Fluss Fans sind, fiel unsere Wahl auf dieses Spiel. 

Ziel

Primäres Ziel des Projektes war es für uns, erste Erfahrungen in Cloud-Computing zu sammeln und gleichzeitig unsere Fähigkeiten im Software-Engineering auszubauen. 

Konkret war es die Idee ein Stadt-Land-Fluss Spiel mit den folgenden Funktionalitäten zu entwickeln: 

  • Schritt 1: Raumerstellung
    • Spieler kann einen neuen Raum erstellen, oder über eine Raum-Id einem Raum beitreten
  • Schritt 2: Spieldaten bestimmen
    • Der Spieler, welcher einen Raum erstellt, soll die Kategorien selber bestimmen können, sowie die Zeit, welche man für das Ausfüllen einer Spielrunde hat, ebenfalls sollen Mitspieler- und Rundenanzahl bestimmt werden können
  • Schritt 3: Waiting Room 
    • Nach Erstellen oder Beitreten eines Raumes, kommt der Spieler in einem Warteraum, wo er die festgelegten Parameter der Spielrunden sieht und informiert wird, welche Spieler der Runde schon beigetreten sind 
  • Schritt 4: Letter Generator 
    • Der Buchstabe für eine Runde soll zufällig generiert werden, sich aber nicht wiederholen innerhalb eines Spiels
  • Schritt 5: Spielrunde
    • Auf der Seite der Texteingaben, soll ein Spieler die Runde stoppen können, sobald er alles ausgefüllt hat, dies triggert den Stopp bei allen Mitspielern
  • Schritt 6: Kontrollieren der Eingaben 
    • Alle Spieler sehen nach einer Runde ihre eigenen, aber auch alle anderen Eingaben der Mitspieler sowie die Punkte, die dabei erreicht wurden
    • Dabei werden die Punkte nach folgendem Schema berechnet:
      • Hat ein Spieler als Einziger in dieser Kategorie eine Eingabe und ist diese auch gültig (beginnt mit dem generierten Buchstaben), dann erhält er für dieses Feld 20 Punkte
      • Haben andere in diesem Feld auch Eingaben, erhält der Spieler für eine gültige Eingabe 10 Punkte
      • Hat ein anderer Spieler in der gleichen Kategorie die gleiche Angabe, erhält der Spieler 5 Punkte
      • Ist die Eingabe leer oder beginnt sie nicht mit dem generierten Buchstaben, werden keine Punkte vergeben
  • Schritt 7 : Hall of Fame  
    • Darstellung der Spieler-Ränge und ihrer Punkte nach Abschließen aller Runden

Das erste Mockup der zu erstellenden Webanwendung entsprach folgendem Design und war unser Leitfaden für die Entwicklungsphase: 

Skizze der groben Web Anwendung zu Beginn

Einblick in das Spiel – Demo 

Frameworks – Cloud Services – Infrastructure

Frontend

Aufgrund von vorhandenen Vorerfahrungen wurde die zweite Entscheidung getroffen, das Frontend mit Hilfe des Angular Frameworks umzusetzen. Angular ist ein TypeScript-basiertes Front-End-Webapplikationsframework. Das Backend wurde mit Python als Programmiersprache umgesetzt. Zum einen war hier mehr Vorerfahrung vorhanden bei einigen Teammitgliedern und zum anderen haben wir mehr Beispiele zur Anwendung von Websockets und AWS im Zusammenhang mit Angular gefunden, was uns sehr geholfen hat. 

Backend

Wie zu Beginn schon erwähnt, hat uns die parallel zum Projekt laufende Vorlesung gleich zu Beginn den großen Funktionsumfang von AWS aufgezeigt.  Besonders interessant fanden wir die Einsatzmöglichkeiten von Lambda Funktionen. Im Zusammenhang damit hat uns die Funktion gefallen ein API Gateway aufzubauen zu können. Da man bei der Programmiersprache völlig frei wählen kann, haben wir uns für Python entschieden. In der Python Programmierung hatten wir als Team zwar wenig Erfahrung, haben aber in dem Projekt eine Chance gesehen, uns in dieses Thema weiter einarbeiten zu können und unsere Fähigkeiten zu verbessern. 

Architektur

Architektur

Cloud Komponenten 

Vor dem Projektstart hatten wir zu Beginn die Schwierigkeit zu entscheiden, welchen Cloud-Anbieter wir für die Entwicklung nutzen wollen. Voraussetzungen für die Entscheidungen waren, dass es eine ausführliche Dokumentation der Möglichkeiten und Funktionen gibt (aufgrund der mangelnden Vorerfahrung), ebenfalls wollten wir nicht eine Kreditkarte als Zahlungsoption hinterlegen müssen und auch keine bis sehr wenig Kosten verursachen. 

Zu Beginn der Vorlesung hieß es noch, dass wir eventuell ein Konto bei der IBM-Cloud oder über AWS von der Hochschule bekommen würden. Allerdings war dies leider doch nicht der Fall, weswegen wir nach erstem Warten selbst eine Entscheidung treffen mussten. Wir haben uns schlussendlich für AWS (Amazon Web Services) entschieden, da es einer der führenden Anbieter im Cloud Computing ist. Hierbei hat uns gefallen, dass es sehr viele Tutorials und gute Dokumentation zu den einzelnen AWS Services gab. Ein Nachteil war, dass man beim Anlegen eines Kontos eine Kreditkarte hinterlegen musste. Vorteil war andererseits, dass man mit einem Gratis-Kontingent (Free Tier) an Funktionsaufrufen, Rollen, und DB Kapazitäten etc. startet, weswegen im Rahmen des Projektes dahingehend keine Kosten entstehen sollten. Im späteren Verlauf haben wir herausgefunden, dass man allerdings für die Funktionalität von AmazonCloudWatch, welches ein Service zur Einsicht der Logs ist, zahlen muss. Die Kosten waren nicht hoch, weswegen es kein Problem darstellte, allerdings sollte man sich eindeutig über die Kosten, welche bei der Entwicklung entstehen können, im Klaren sein, um nicht böse überrascht zu werden. 

Amazon Free Tier

Übersicht Free Tier : https://aws.amazon.com/de/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=tier%23always-free&awsf.Free%20Tier%20Categories=*all

AWS Services 

Serverless 

Für das Anlegen unseres Projektes und auch der späteren Möglichkeit einer übersichtlichen Vorlage des Codes zur Bewertung, haben wir nach einer Möglichkeit gesucht, alle für AWS benötigten Konfigurationen sowie das Anlegen der verschiedenen Lambda-Funktionen in unserem GitLab Reporsitory hinterlegen zu können und direkt im Code Editor bearbeiten und ändern zu können. 

Nach einiger Recherche sind wir dabei auf das Serverless Framework gestoßen. Vorteile des Serverless Frameworks gegenüber der AWS Grafikoberfläche sind, dass alle man alle Elemente wie Datenbanken, Buckets, API-Routen und Aufrufe sowie Lambda Functions über eine serverless.yaml Datei verwalten kann. Zudem ist es nicht nötig, AWS-Kontoschlüsseln oder anderen Kontoanmeldeinformationen in Skripte oder Umgebungsvariablen zu kopieren oder einzufügen. Über die serverless.yaml können alle Ressourcen und Funktionen übersichtlich und schnell angelegt und bearbeitet werden. 

Functions

In der serverless.yaml angelegte Lambda Funktionen entsprechen dem folgenden Schema : 

Funktion Definition in serverless.yml 

Die in der Datei definierten Funktionen werden jeweils über einen eigenen „handler“ referenziert. Das bedeutet, dass sie über ein „Event“ aufgerufen werden können. Ein Event entspricht dabei beispielsweise einem API-Aufruf. 

broadcast_to_room Lambda-Funktion

Resources 

Neben unseren Funktionen sind in der serverless.yml ebenfalls alle Tabellen sowie Buckets definiert. Dies erleichtert die Einrichtung neuer Ressourcen, welche benötigt werden und gibt eine gute Übersicht. 

iamRoleStatements

Zudem können IAM-Rollen und Berechtigungen, die auf Lambda-Funktionen angewendet werden, konfiguriert werden. 

Allgemeine Konfiguration 

Es können ganz einfach allgemeine Settings angegeben werden, wie zum Beispiel: 

Übersicht der Möglichkeiten: https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/

Dynamo DB 

Um unser Spiel umsetzen zu können, waren wir darauf angewiesen, bestimmte Daten in einer Datenbank zu speichern, dies waren beispielsweise Elemente wir eine Raum-ID, die Spielernamen, die gewählten Spielparameter, sowie die benutzen Buschstaben und eingetragene Ergebnisse der Spieler. Da die Einträge in der Datenbank immer variieren und keinem starren Muster folgen, beispielsweise ist es möglich eine unterschiedliche Zahl an Kategorien pro Spielraum zu haben, haben wir nach einer NoSQL Lösung gesucht. Wir wollten eine Datenbank mit nicht-relationalen Ansatz, um nicht von einem festen Tabellenschemata abhängig zu sein. Unsere Wahl fiel dabei auf Amazon DynamoDB, welches eine schnelle NOSQL-Schlüssel-Wert-Datenbank für beliebig große Datenmengen ist. 

Es gibt eine Tabelle „game_data“, welche alle relevanten Informationen enthält. 

Datenstruktur

Ein Beispiel unserer Datenstruktur ist in der folgenden Abbildung zu erkennen:

Diese JSON-Datei zeigt ein laufendes Spiel mit insgesamt zwei Spielern. Ganz oben ist die Room-Id gespeichert, welche einen einzigartigen Wert aufweist. Diese ist für die Spielrunde besonders wichtig, da so mehrere Räume generiert und gleichzeitig laufen können. Der darunter gespeicherte Array „categories“ beinhaltet alle Kategorien als String-Elemente, die der Spieler, der den Raum erstellt hat, ausgewählt hat. Das Dictionary „game_players“ enthält die Informationen zu allen Spielern. Dazu gehört bspw. der Username, der als Key im Dictionary agiert, die jeweilige Raum-Id, die Punkteanzahl sowie die Eingaben, die der Spieler für eine Runde eingegeben hat. Daneben werden zwei weitere Werte gespeichert, „next_round“ und „status“, welche dazu dienen, zu erkennen, dass die nächste Runde beginnen kann, also alle Spieler bereit sind, oder ob der Spieler noch aktiv ist, also das Spiel nicht bereits verlassen hat.

Des Weiteren werden die Rundenanzahl, die Spiellänge für eine Runde, sowie in einem Array die generierten Buchstaben der Runden gespeichert. Dieser wird pro Runde immer um den nächsten zufällig generierten Buchstaben erweitert.

Datenbank-Interaktionen

Während eines Spiels müssen immer wieder Spielerdaten abgefragt, verändert, hinzugefügt oder gelöscht werden. Dazu gehören bspw. das Anlegen eines neuen Spiels, das Hinzufügen neuer Spieler zu der jeweiligen Raum-Id, das Aktualisieren der erreichten Punkte etc. Diese Interaktionen haben wir in den Lambda-Funktionen, die später noch einmal genauer erklärt werden, implementiert und sie werden aufgerufen, sobald diese benötigt werden. Da diese in Python implementiert wurden, haben wir die AWS SDK für Python, Boto3 verwendet, die eine Integration von Python-Anwendungen und Bibliotheken in AWS-Services, darunter DynamoDB, ermöglicht.

Quelle: https://aws.amazon.com/de/sdk-for-python/

Abfragen von Daten

Auch wenn Boto3 als AWS-SDK für Python nützliche Methoden bezüglich DynamoDB bereitstellt, war es nicht immer einfach, diese zu verwenden. Da unsere Datenstruktur durch die Verschachtelungen recht kompliziert war, war es aufwendig, die benötigten Informationen abzufragen. 

Die obige Abbildung zeigt einen Ausschnitt aus einer Lambda-Funktion und stellt die Abfrage von Spieldaten dar. Zunächst wird die Verbindung zu der gewünschten Datenbank (game_data) hergestellt, indem Endpoint-Url sowie die Region angegeben werden muss. Mit dieser Datenbank-Instanz kann nun mit Methoden wie „.get()“ auf die gewünschte Information in der Datenbank zugegriffen werden. Das Verwenden dieser Funktionen war demnach einfach, jedoch wurden die Daten in der DynamoDB sehr oft in Dictionaries gespeichert, welches an vielen Stellen gar nicht nötig war, was in der Abbildung durch das sich wiederholende „.get(‚L‘)“, „.get(‚M‘)“ etc. zu erkennen ist.

In Wirklichkeit waren die Daten nämlich so aufgebaut:

Man erkennt, dass die Daten durch DynamoDB zusätzlich in Dictionaries und Arrays geschachtelt werden und es war schwierig zu erkennen, um was es sich nun handelt. Das Abfragen wurde dadurch viel komplizierter gestaltet, als es eigentlich ist und war auch sehr fehleranfällig.

Anlegen von Daten

Das Anlegen neuer Daten in die Datenbank erfolgt mithilfe der Methode „put_item“, welcher ein JSON-Objekt mitgegeben werden muss. Die Abbildung zeigt das Anlegen eines neuen Raumes.

Löschen von Daten

Das Löschen von Daten erfolgt ähnlich wie das Updaten von Daten, da der jeweiligen Methode „delete_item“ mehrere Parameter mitgegeben werden können, die den Befehl spezifizieren. In unserem Projekt haben wir komplizierte Aufrufe jedoch nicht gebraucht, weshalb das Löschen im Vergleich zum Updaten von Daten einfach war.

Update von Daten

Das Updaten von Daten erfolgt mithilfe der Methode „update_item“, dessen übergebene Parameter komplexer sind als die anderen und auch abhängig davon sind, welche Typen (Array, Dictionaries, ein einfacher Wert) aktualisiert werden sollen. Um zu bestimmen, welches Objekt in der Datenbank bearbeitet werden soll, kann ein sogenannter „Key“, welcher in der obigen Abbildung die Raum-Id ist, mitgegeben werden. Zusätzlich können die übergebenen Parameter überprüft (ConditionExpression) und schließlich bestimmt werden, wie das Objekt aktualisiert werden soll. In diesem Beispiel wird dem Array, der die generierten Buchstaben eines Spiels speichert, um einen Wert erweitert, weshalb als Wert für den Parameter „UpdateExpression“ ein „list_append“ verwendet wird.

S3

AWS S3 (Simple Storage Service) ist ein Service für das Speichern von Objekten. In unserem Projekt haben wir S3 benutzt, um unsere Anwendung zu hosten. Hierfür haben wir einen S3-Bucket erstellt und dort unter „Static website hosting“ die Einstiegs- und Fehlerseite unserer Anwendung angegeben. Damit Benutzer über die dabei erzeugte URL auf unsere Seite zugreifen können, haben wir diese anschließend noch öffentlich zugreifbar gemacht und eine Policy hinzugefügt, um im öffentlichen Modus den Inhalt des Buckets lesen zu können.

Über die AWS CLI können danach immer die aktuellen Files der gebauten Anwendung in S3 hochgeladen werden, wenn die Anwendung deployt werden soll (siehe auch CI/CD Kapitel):

Ausschnitt aus der CI/CD Pipeline zum Hochladen der Dateien in S3

Lambda

„AWS Lambda ist ein Serverless-, ereignisgesteuerter Computing-Service, mit dem Sie Code für praktisch jede Art von Anwendung oder Backend-Service ausführen können, ohne Server bereitzustellen oder zu verwalten. Sie können Lambda in über 200 AWS-Services und Software-as-a-Service (SaaS)-Anwendungen auslösen und Sie zahlen nur für das, was Sie nutzen.“

https://aws.amazon.com/de/lambda/?c=ser&sec=srv

Amazon API- Gateway 

Für die definierten Lambda Funktionen hat man bei AWS die Möglichkeit eine eigene Web-API mit einem http-Endpunkt zu erstellen. Dafür kann man das Amazon API Gateway verwenden. Die Funktion des API-Gateway ist es Tools für die Erstellung und Dokumentation von Web-APIs, die HTTP-Anforderungen an Lambda-Funktionen weiterleiten, zu erstellen. 

Websocket API Routen 

Api RouteBeschreibung
broadcast_to_roomSendet eine Nachricht an alle Spieler in einem Raum anhand der Raum-Id und den in der Dynamo DB gespeicherten Connection-Ids der Spieler.
check_roundsetzt den Status der nächsten Runde des jeweiligen Benutzers und sendet die Nachricht für die nächste Runde an die Spieler im Raum, wenn alle Benutzer die nächste Runde angeklickt haben.
create_room Erstellt einen neuen Raum in der Datenbank mit den angegebenen Werten oder Standardwerten, wenn keine Werte angegeben werden.
enter_roomFügt einen Benutzer zu einem Raum hinzu, indem sein Username sowie die entsprechende Connection-Id in der Dynamo DB unter dem Schlüssel der angegeben Raum-Id gespeichert werden. 
get_current_playersPrüft, ob der Spieler den Raum betreten darf und sendet alle Spielernamen des Raumes an den neuen Spieler.
get_results_for_roomSendet Raumdaten und Spielerdaten an alle Spieler des Raums mit der angegebenen Raum-Id.
load_user_inputslädt alle Benutzereingaben aus der Datenbank und sendet alle Werte (z.B. [[“Stuttgart”, “Rhein”, “Deutschland”]]) an die Spieler des Raums mit der angegebenen Raum-Id.
navigate_players_to_next_roomNavigiert alle Spieler in einem Raum zum Spielraum und wird aufgerufen, wenn der Hauptakteur die Taste “Spiel starten” drückt.
play_roundFragt Timer, Kategorien und Rundeneinstellungen für den Spielraum über die Room-Id aus der Datenbank ab. 
remove_player_from_roomSetzt den Spielerstatus auf inaktiv in der Dynamo DB und löscht die Raumdaten aus der Datenbank, wenn alle Spieler dieses Raums inaktiv sind.
save_roundSpeichert Benutzereingaben aus Kategoriefeldern in der Datenbank.
start_roundBeginnt die nächste Runde, indem er einen neuen Buchstaben erzeugt und prüft, ob dieser Buchstabe bereits ausgewählt wurde und speichert den erzeugten Buchstaben in der Datenbank.
stop_roundStoppt eine Spielrunde, wenn jemand die Stopptaste gedrückt hat. 

Amazon CloudWatch 

Amazon CloudWatch entspricht einem Überwachungs- und Beobachtungsservice für Entwickler.  Wir wollten über CloudWatch die Verwendung unserer API protokollieren. Dabei gibt es die Möglichkeit der Ausführungsprotokollierung. Hierbei verwaltet das API-Gateway die CloudWatch-Protokolle. Es können verschiedene Protokollgruppen erstellt werden, welche dann die verschiedenen Aufrufe, Abfragen und Antworten an den Protokollstrom melden. Zu den protokollierten Daten gehören beispielsweise Fehler. 

Protokollgruppen

Logs der Methode load_user_inputs

Testing 

Beim Testing haben wir uns hauptsächlich auf das Testen der Lambda-Funktionen mit Unittests fokussiert. Dafür haben wir die Bibliothek Moto benutzt, mit der man AWS Services mocken kann. Dadurch konnten wir in den Tests unsere Datenbank mocken und beispielsweise auch testen, ob beim Aufruf der Lambdas Datenbankeinträge richtig angelegt oder Daten richtig aktualisiert werden. Allgemein muss für das Mocken der Datenbank nur die Annotation @mock_dynamodb2 über der Testklasse eingefügt werden und anschließend kann in der setUp-Methode die Datenbank definiert werden, die für die Tests benutzt werden soll. Dadurch können auch Testdaten in die Datenbank eingefügt werden, um bestimmte Testfälle zu testen.

Neben Moto haben wir die Bibliothek unittest.mock benutzt, mit der zum Beispiel das Senden einer Nachricht über die Websocket-Verbindung gemockt werden kann, oder auch der Aufruf einer Lambda-Funktion. Zudem kann man mit Methoden wie assert_called() oder assert_called_with(…) überprüfen, ob und mit welchen Argumenten die gemockte Methode aufgerufen wurde. Allgemein war dies bei unserem Projekt sehr hilfreich, da wir in fast jeder Lambda-Funktion Nachrichten über die Websocket-Verbindung schicken und somit auch testen konnten, ob die richtigen Nachrichten geschickt werden.

Für manuelle Tests oder zum kurzen Testen von bestimmten Eingabewerten war auch die Seite https://www.piesocket.com/websocket-tester sehr hilfreich, da man dort die verschiedenen Lambda-Funktionen über eine Websocket-Verbindung aufrufen kann.

CI/CD Pipeline 

CI/CD Pipeline in GitLab auf dem master-Branch

Um unsere Tests automatisiert ablaufen zu lassen und auch andere Schritte wie das Deployen der Lambda-Funktionen nicht immer manuell ausführen zu müssen, haben wir in GitLab eine CI/CD Pipeline erstellt. Da wir alle vor diesem Projekt noch nie eine CI/CD Pipeline angelegt hatten, konnten wir somit durch das Projekt auch Erfahrungen in diesem Bereich sammeln. Allgemein ist unsere Pipeline unterteilt in verschiedene Stages: In der Testing-Stage werden die Unittests für unsere Lambda-Funktionen ausgeführt. In der Build-Stage werden die aktuellen Versionen der Lambda-Funktionen über das serverless-Framework deployt und anschließend wird unsere Angular-Anwendung gebaut. Am Ende wird unsere Anwendung in der Deploy-Stage deployt, sodass sie danach über eine öffentliche Url verfügbar ist. Je nachdem, auf welchen Branch ein Entwickler in unserem Repository pusht, werden unterschiedliche Jobs ausgeführt. So werden zum Beispiel die Unittests auf jedem Branch ausgeführt, das Deployen der Lambda-Funktionen jedoch nur beim Pushen auf den develop- oder master-Branch und das Deployen der Anwendung nur beim Pushen auf den master-Branch.

Insgesamt gibt es in GitLab unter Settings -> CI/CD die Möglichkeit, Variablen anzulegen, die in der CI/CD Pipeline benutzt werden. Wir haben daher in IAM (Identity and Access Management) bei AWS einen Benutzer für die Pipeline angelegt, der nur die Rechte hat, die in der Pipeline benötigt werden. Die Keys dieses Benutzers haben wir anschließend als Variablen in GitLab angelegt, sodass zum Beispiel das Deployen der Lambda-Funktionen in der Pipeline mit diesem Benutzer durchgeführt werden kann.

Schwierigkeiten 

Während unserer Arbeit am Projekt sind uns einige Schwierigkeiten begegnet, die im Folgenden näher beschrieben werden.

Datenstruktur

Wie bereits beschrieben hatte sich das Abfragen von Daten in der DynamoDB als sehr aufwendig und komplex dargestellt, auch wenn der Methodenaufruf an sich leicht zu verstehen und einfach ist. Durch die weiteren Verschachtelungen, die AWS zusätzlich zu unserer bereits komplexen Datenstruktur hinzugefügt hat, wurde das Abfragen von Daten schwieriger dargestellt, als es eigentlich ist und auch der Code, den wir dafür geschrieben haben, wurde durch dieses sehr unleserlich. Dadurch kam es in unserer Gruppe oft zu Fehlern bei den Abfragen, da der Fehler nicht direkt erkannt wurde und die endgültige Datenstruktur in der Datenbank für unsere Gruppe unklar wurde. 

Wir haben diesbezüglich, auch nachdem wir die Struktur festgelegt hatten, mehrmals gelesen, dass DynamoDB für komplexe Datenstrukturen und Interaktionen (auch das Updaten von Daten) schlichtweg nicht geeignet ist. Dennoch haben wir es dabei belassen, da wir bereits im Projekt weit fortgeschritten waren und keine Zeit mehr hatten, eine Alternative zu finden.

Deployen von Lambdas

Damit die Lambdas, die wir in der serverless.yaml definiert und in den jeweiligen Handler-Dateien implementiert hatten, in unserer Anwendung aufgerufen werden konnten, war es nötig, den Befehl „serverless deploy“ aufzurufen, der alle definierten Lambdas in den Handler-Dateien deployed. Das Problem war, dass dadurch bestehende Funktionen, an denen andere Gruppenmitglieder arbeiteten, anschließend überschrieben wurden, was das Arbeiten sehr behinderte, wenn man an dem Projekt gleichzeitig arbeitete.

Um diese Situation weitestgehend zu verhindern, haben wir beschlossen, die Methoden auf dem Development Branch allesamt mit dem Befehl „serverless deploy“ zu deployen. Anschließend wird auf unterschiedlichen Branches gearbeitet und anschließend nur die Funktion, an der man gerade arbeitet, mit dem Befehl „serverless deploy function –function [Name der Funktion]“ deployed. Dieses hat nur teilweise funktioniert, da das Deployen einer einzigen Funktion nur möglich war, wenn diese bereits existiert, also durch „Serverless deploy“ deployed wurde.

Fazit

Alles in allem haben wir durch das Projekt einen Einblick in die Möglichkeiten von Cloud Computing bekommen und konnten verschiedene Dinge in diesem Bereich ausprobieren. Wir konnten vor allem Erfahrungen mit AWS Lambda, API Gateway und dem serverless Framework sammeln, da dies der Schwerpunkt unseres Projektes war. Zudem haben wir einige grundlegende Dinge gelernt, zum Beispiel dass es sinnvoll ist, schon früh im Projekt eine CI/CD Pipeline aufzubauen oder auch CloudWatch zu aktivieren, um Fehler in den Lambda Funktionen schneller zu erkennen und beheben zu können.

Allgemein haben wir durch das Projekt auch gelernt, dass es sehr wichtig ist, sich am Anfang Gedanken zu Themen wie Security oder auch dem generellen Aufbau des Projektes zu machen. Aus Zeitgründen verfolgt man sonst oft die am schnellsten funktionierende Lösung und kann dann später nur mit viel Aufwand grundlegende Dinge ändern. Für das nächste Projekt würden wir daher mehr Zeit für die Einarbeitung und Recherche einplanen, um dies zu vermeiden. Bei unserem Projekt wäre es vor allem sinnvoll gewesen, schon von Beginn an die verschiedenen Umgebungen einzurichten, sodass alle Entwickler lokal unabhängig voneinander entwickeln und testen können und man auf der Entwicklungsumgebung Änderungen durchführen kann, ohne die Produktivumgebung zu beeinflussen.

Migration einer REST API in die Cloud

Artikel von Cedric Gottschalk und Raphael Kienhöfer

Im Rahmen der Endabgabe der Vorlesung “Software Development für Cloud Computing” haben wir uns zum Ziel gesetzt, eine bereits bestehende REST API eines vorherigen Projektes in die Cloud zu migrieren. Dabei haben wir uns dafür entschieden, die Google Cloud zu verwenden. Im Zuge dieses Projektes haben wir uns auch mit Infrastructure as Code mittels Terraform beschäftigt.

Architektur

Vor dem Umzug in die Cloud lebte die API als Container auf einem einzelnen Server, der mittels Docker Compose verwaltet wurde. Hier wurde nginx als Reverse-Proxy eingesetzt, um die Übertragung mittels TLS zu sichern. MariaDB wurde als SQL-Datenbank eingesetzt. Die Verknüpfung der einzelnen Dienste gestaltete sich hier durch den gemeinsamen technischen Unterbau (Docker) sehr simpel.

Continue reading

MealDB Chatbot mit Google Dialogflow

Ein Projekt von Ronja Brauchle, Julia Cypra und Lara Winter

Einleitung

Gab es bereits drei Tage hintereinander Pasta mit Pesto? Ist das einzig abwechslungsreiche in deinem Speiseplan die Entscheidung ob die klassische Tiefkühlpizza mit Salami oder die extravagantere Wahl des Flammenkuchens nach Bauernart in den Ofen geschoben wird?  Unser MealBot hilft genau bei diesen monotonen und inspirationslosen Phasen: Er schlägt Rezepte basierend auf Lebensmitteln vor, die man gerade zuhause liegen hat; er kann nach Länderküchen und Essensvorlieben Mahlzeiten heraussuchen oder einfach zu einer dir bereits bekannten Mahlzeit das Rezept ausgeben.

Wir haben für das Projekt Dialogflow von Google verwendet, das eine Engine basierend auf NLU (Natural Language Understanding) ist und mit der man intelligente Chatbots erstellen kann. Durch das Anbinden an die MealDB (https://www.themealdb.com/) mithilfe der MealAPI kann unser Chatbot dem User hunderte von Rezepten ausgeben. Der MealBot wurde in Telegram integriert und ist somit dort für jeglichen Chitchat anzutreffen.

Entwurf und Architektur

https://cloud.google.com/static/dialogflow/es/docs/images/fulfillment-flow.svg

Die obige Abbildung visualisiert die Auftragsausführung nach einer Benutzereingabe. Wie bereits erwähnt, ist unser Chatbot in den Messengerdienst Telegram integriert, von dort wird die Benutzereingabe weiter an Dialogflow geleitet. Dank der in Dialogflow integrierten KI kann die Benutzereingabe einem der passenden individuell erstellten Intents zugeordnet werden. Dabei werden außerdem auch bestimmte Parameter aus der Eingabe extrahiert. Ein Intent kann eine vorgefertigte statische Antwort beinhalten, es kann aber auch unter dem Punkt „Fulfillment“ ein Webhook Aufruf aktiviert und somit passender Code getriggert werden. Im Falle eines Webhook Aufrufes werden die extrahierten Parameter, sowie weitere Informationen zu dem zugeordneten Intent in einer JSON HTTPS POST Anfrage, dem Fulfillment Request, an den Webhhokdienst geschickt. Der Webhook kann selbst gehostet werden, dieser muss dann HTTPS POST Anfragen in Form von JSON verarbeiten. In unserem Fall haben wir den Code zur Auftragsausführung im Inline-Editor von Dialogflow erstellt und somit in einer Cloud Function bereitgestellt. Die Cloud Function wird dann durch den Dialogflow getriggert und erhält die Daten aus der JSON Anfrage. Die Cloud Function besteht lediglich aus einer index.js, die Funktionen für die jeweiligen Intents enthält. Durch das passende Intent Matching wird dann auch die jeweilige Funktion ausgeführt. In dieser Funktion wird unsere externe API, die MealDB, abgefragt. Dabei wird je nach Intent ein zugehöriger Endpoint mit den passenden Parametern angesprochen. Die Antwort der MealDB Abfrage wird dann zusammen mit anderen Daten in der Webhook Antwort wieder an Dialogflow zurückgeschickt. Dialogflow extrahiert aus der Antwort die benötigten Daten und sendet diese letztendlich an Telegram, sodass der Endbenutzer die Antwort erhält.

Die untenstehende Abbildung verdeutlicht nochmal den Ablauf mit den von uns benutzten Technologien.

Der Vorteil von Cloud Functions ist, dass diese serverless, günstig und skalierbar sind. Das heißt die Cloud Function ist ereignisgesteuert und wird nur bei einem Event getriggert. Dadurch und wegen der Skalierbarkeit halten sich die Kosten gering.

Basics aka die Bausteine von Dialogflow

Intents

Intents ermöglichen es dem Agenten die Motivation hinter einer bestimmten Benutzereingabe zu verstehen. Jeder Intent stellt eine Aufgabe oder Handlung dar, die der Benutzer ausführen möchte. Im folgenden Bild sieht man die von uns für den MealBot erstellten Intents inklusive der Erklärung dieser.

Ausdrücke, die dasselbe bedeuten, aber auf unterschiedliche Weise konstruiert werden, werden demselben Intent zugeordnet. Somit kommen wir direkt zum ersten Bestandteil (von dreien) eines Intents, und zwar zu den Trainingsformulierungen:

1.Trainingsformulierungen

Trainingsformulierungen sind Beispielsätze für das, was der Endnutzer sagen könnte. Wenn der Endnutzerausdruck einer dieser Sätze ähnelt, stimmt Dialogflow mit dem Intent überein. Jedoch muss man nicht jede denkbare Formulierung angeben, da das integrierte maschinelle Lernen die Liste automatisch um ähnliche Äußerungen erweitert.

2. Parameter

Parameter sind die aus dem Benutzerausruck extrahierten Werte (in der ersten Trainingsformulierung wären es “garlic” und “salt”). Jeder Parameter hat einen Entitätstyp (siehe Abschnitt Entities; hier “Ingredients”). Der bzw. die Parameter werden je nach Intent an den passenden Endpoint geschickt. Aus dem von der Meal API zurückgeschickten Datensatz formt man dann eine Antwort, die dem User ausgegeben wird.

3. Antwort

Bei den meisten Intents konstruieren und formatieren wir die Antwort im Inline-Editor, wo wir mit dem von der API zurückgeschickten Datensatz direkt weiterarbeiten können. Bei Smalltalk bezogenen Intents, die keine API-Anfrage benötigen, wird die Antwort per GUI angegeben:

Beispiel Intent “thanks”

Man unterscheidet bei Antworten zwischen zwei Arten:

1.Standard-Antwort: Bot antwortet auf die Frage und ist anschließend bereit weitere unspezifische Fragen zu beantworten. 

Bsp: Intent “random-meal” : Der User fragt, ob der Bot irgendein Rezept vorschlagen könnte -> Der Bot gibt direkt eins aus und ist bereit, andere Fragen zu beantworten.

2. follow-up-intents: Bot antwortet auf die erste Frage, erwartet dann aber vom User weitere Eingaben, die sachlich auf die erste Frage aufbauen.

Bsp: alle “filter-by” Intents: Der User fragt beispielsweise nach chinesischem Essen, woraufhin der Bot ihm 3 Rezepte vorschlägt und er vom User erwartet, eins auszuwählen.

Entities

Entities werden verwendet, um bestimmte Informationen herauszufiltern, die der Benutzer erwähnt. Sie nehmen nützliche Daten auf und stellen sie zur weiteren Verarbeitung bereit. Man unterscheidet zwischen zwei Arten:

1.System entities, die bereits in Dialogflow enthalten sind und mit denen der Agent ohne zusätzliche Konfiguration Informationen zu einer Vielzahl von Konzepten extrahieren kann.

Bsp: Dialogflow erkennt direkt eine Zeitangabe, ein Datum oder eine Zahl (sys.time, sys.date, sys.number)

2. Developer entities definiert der Entwickler anhand seines individuellen Datensatzes selbst. Bezüglich der Parameter, die wir an die Endpoints der Meal API schicken, haben wir folgende Entities definiert:

“Ingredients”-Entität: Lebensmittel, die in der MealDB vorhanden sind und die der Agent bei einem Benutzerausdruck identifizieren sowie extrahieren könnte

Entity Dilemma

Um eine Benutzereingabe richtig zu verstehen und die passenden Parameter herauszufiltern, muss Dialogflow die zugehörigen Entitys kennen. Denn ist ein Entity vorher nicht bekannt, kann Dialogflow dies auch nicht erkennen und somit eine Eingabe auch nicht dem passenden Intent zuweisen. Sollte der Benutzer zum Beispiel eingeben: „I want to cook pancakes.“, Dialogflow aber nicht weiß was pancakes sind, kann er damit nichts anfangen. Aus diesem Grund mussten wir alle möglichen Gerichte, Länder, Kategorien und Zutaten als Entitys abspeichern. Bei den Kategorien und Ländern ging dies auch relativ schnell. Bei dem Entity „AreaAdjective“ mussten wir ein Adjektiv herausfiltern, da der API Endpoint nur diese akzeptiert hat. So funktioniert zum Beispiel eine Abfrage mit „italian“, wohingeben „Italy“ keine Ergebnisse liefert. Durch das Hinzufügen von Synonymen konnten wir das Problem aber schnell lösen, denn so wird „Italy“ automatisch zu „italian“ umgewandelt. Bei den Gerichten und Zutaten hat es sich aber ein bisschen schwieriger gestaltet. Denn es gibt keinen Endpoint bei der MealDB, mit welchem man alle vorhandenen Rezepte abfragen kann, man kann lediglich alle Rezepte mit einem bestimmten Anfangsbuchstaben abfragen. Deshalb haben wir uns eine kleine Hilfsfunktion erstellt, die in einer Schleife Abfragen mit allen möglichen Buchstaben macht. Die einzelnen Zwischenergebnisse haben wir dann in ein Array gepushed, sodass wir zum Schluss alle möglichen Rezepte zusammen hatten. Diese Rezepte haben wir uns dann ausgeben lassen über die Konsole und als CSV Format in dem Entity „Ingredients“ hochgeladen. Dazu der passende Code, der vielleicht nicht der schönste ist, aber funktioniert hat.

Die Zutaten konnten wir zwar alle über einen Endpoint abfragen, da es aber viel zu vielen waren, um sie von Hand als Enitiy einzugeben, haben wir uns dafür auch eine Hilfsfunktion erstellt. Mit dieser Funktion haben wir die Abfrage formatiert und wieder ausgegeben, sodass wir alle Zutaten auf einmal im CSV Format hochladen konnten. Der Code dazu funktioniert ähnlich wie der obere.

Kontext

Schon mal an einer regen Diskussion von Fremden vorbeigelaufen und Phrasen aufgeschnappt, die für einen als Außenstehender überhaupt keinen Sinn ergeben haben? 

So wie Menschen einen Kontext brauchen, um herauszufinden, was ein bestimmtes Wort oder bestimmter Satz bedeutet, benötigen auch Chatbots sie. Ohne Kontext könnte das Wort bzw. der Satz absolut keine Bedeutung haben oder etwas völlig anderes bedeuten als die ursprüngliche Absicht des Benutzers

Unser persönliches (philosophisches) Kontext-Dilemma

Wie bei den Intents oben bereits kurz skizziert, ist unser Rezept-Vorschlag-Konzept wie folgt aufgebaut: Der Benutzer fragt idealerweise nach einem Rezept basierend auf einer Länderküche, Essenskategorie etc. Darauffolgend schlägt der Agent ihm bis zu 3 Rezepte vor, von denen sich der Benutzer für eins entscheiden kann. Wie man bereits schon ahnen könnte, ist hier ein Kontext erforderlich, damit der Chatbot Aussagen wie “Option 2”,“Ich will das erste” auch zuordnen kann.

Mit voller Motivation haben wir für jeden Intent einen follow-up intent zusammengeklickt, der genau diese Auswahl anhand der Nummerierung der vorgeschlagenen Rezepte erkennen sollte. Somit wurde logischerweise für jeden Intent ein eigener Output-Kontext erstellt, der bei dem jeweiligen follow-up Intent als Input-Kontext wieder eingespeist wurde. Schnell wurde uns beim Code-Schreiben bewusst, dass eigentlich jeder follow-up intent die selbe Logik implementiert, und zwar zu erkennen welches Rezept ausgewählt wurde und dieses formatiert an den User zurückzugeben. Die Idee schlich sich herein, alle individuellen Kontexte der Intents in einen zusammenzufassen und diesen einen bequem in einer Funktion zu verwalten und für alle follow-up intents aufzurufen. Wir gaben der Idee nach, da alles nach unseren Erwartungen richtig ausgegeben wurde. Trotzdem fühlt sich die Lösung nicht ganz “clean” an, da sprachwissenschaftlich gesehen eigentlich jeder Intent vom Inhalt her einen eigenen Kontext verdienen sollte: Ob man gerade nach einer Essenskategorie oder Länderküche gefragt hat, sollte in einer echten Konversation schon einen Unterschied machen. Programmatisch hat es uns die Sache jedoch wesentlich erleichtert.

API Routen

axios.get(“https://www.themealdb.com/api/json/v1/1/categories.php “)

Auflisten aller meal categories. Wird genutzt, um die Beschreibung einer vom Endnutzer eingegebenen Kategorie herauszufiltern und auszugeben.

axios.get(“https://www.themealdb.com/api/json/v1/1/random.php“)

Ausgabe des Rezeptes eines random meals.

axios.get(“https://www.themealdb.com/api/json/v1/1/filter.php”, { params: { a: inputArea } })

Filtern der Gerichte nach einem vom Endnutzer eingegeben Land. Wird genutzt um drei random Gerichte dieses Landes als Optionen dem Nutzer zur Verfügung zu stellen

axios.get(“https://www.themealdb.com/api/json/v2/9973533/filter.php”, { params: { i: paramsIngredients } })

Filtern der Gerichte nach einer oder mehreren vom Endnutzer eingegeben Zutaten. Wird genutzt um drei random Gerichte mit diesen Zutaten als Optionen dem Nutzer zur Verfügung zu stellen.

axios.get(“https://www.themealdb.com/api/json/v1/1/search.php”, { params: { s: inputMeal } })

Filtern der Gerichte nach einem vom Endnutzer eingegeben Gericht. Wird genutzt um bis zu drei Arten dieses Gerichtes als Optionen dem Nutzer zur Verfügung zu stellen.

axios.get(“https://www.themealdb.com/api/json/v1/1/filter.php”, { params: { c: inputCategory } })

Filtern der Gerichte nach einer vom Endnutzer eingegeben Kategorie. Wird genutzt um bis zu drei Optionen aller Gerichte aus dieser Kategorie als Optionen dem Nutzer zur Verfügung zu stellen.

axios.get(“https://www.themealdb.com/api/json/v2/9973533/lookup.php”, { params: { i: mealId } })

Suche nach einem bestimmten Gericht durch den Parameter id und Ausgabe des Rezeptes.

Beispiel Cloud Function for Firebase – FilterByArea:

In der Funktion wird als Parameter der Webhook Client agent übergeben. Über diesen bekommt die Variable InputArea als Wert den Parameter des Entitätstyps “AreaAdjective”, der vom Nutzer eingeben und von Dialgogflow erkannt  und extrahiert wurde. Mit Hilfe der Bibliothek axios wird auf die MealDB zugegriffen. Dabei wird die URL mit inputArea übergeben.  Als Antwort kommen bei res nun die Daten aus der MealDB zurück. Diese werden an die Funktion handleResult() übergeben, welche die Daten filtert. Letztendlich werden die gefilterten Daten über agent.add() zur Antwort, die dem Endnutzer dann angezeigt wird.

Chatbot in Action

1. Intents und Entitäten: Die Nutzereingabe wird mit Hilfe der Trainingssätze und dem Parameter mit dem Entitätstyp dem passenden Intent zugeordnet, dem “filter-by-ingredients” Intent. Somit kommt die passende Antwort zurück. Die Nutzereingabe carrot wird als Parameter “Carrots” des Entitätstypen “ingredients” erkannt. Dialogflow kann dies zuordnen, auch mit anderer Groß – und Kleinschreibung bzw. Ein – oder Mehrzahl.

2. FollowUpIntent: Option 0: Dialogflow erkennt die 0 als passend zu den Trainingssätzen vom Intent “filter-by-ingredients – zero”. Dieser ist ein FollowUp Intent auf “filter-by-ingredients”.

3. Kontext:  Die Nutzereingabe wurde dem “filter-by-area” Intent mit dem Parameter “areaAdjective” = thai zugeordnet. Außerdem wurde der Kontext “user-data” hergestellt. Der Nutzer gibt nun die Option 2 an und der Chatbot befindet im FollowUpIntent “filter-by-area – options” mit dem Parameter value = 2. Das passende Rezept wird ausgegeben. Gibt der Nutzer nun die Option 3 ein, so wird wieder das passende Rezept ausgegeben. Der Chatbot kann die 3 deswegen immer noch als number Parameter des FollowUpIntent “filter-by-area – options” erkennen, da er sich noch im Kontext  “user-data” befindet. Für solche Zusammenhänge ist also der Kontext nötig.

4. Problem: Lasagna Problem: Unter der Option Lasagna kann der Chatbot nichts finden. Unter der Option Lasagna Sandwich jedoch schon. Die Erwartung des Endnutzers könnte allerdings sein, bei der Suche nach Lasagna alle Rezepte die Lasagna im Namen haben zu finden, u.a. Lasagna Sandwich. Die Meal Optionen im Chatbot sind allerdings an die in der MealDB gebunden und in der MealDB gibt es kein Gericht Lasagna. Demnach gibt es in der “Meal” Entitäten Liste auch keine Lasagna. Dialogflow sucht die Entitäten nach Lasagna ab, kann aber keinen genauen Treffer finden und den Wortteil “Lasagna” aus “Lasagna Sandwich” nicht extrahieren und als richtig erkennen. Der Nutzer muss also den gesamten Namen des Gerichts angeben.

Testing

Zum Testing haben wir leider nur wenig gefunden, vor allem zu automatischen Tests, um den Dialogflow als Ganzes zu testen. Zwar gibt es in der Dialogflow GUI die Möglichkeit einzelne Sätze zu testen, wobei auch der zugehörige Intent, Context sowie die extrahierten Parameter angezeigt werden, was auch sehr hilfreich zum Debuggen war. Der nebenstehende Screenshot zeigt, wie dies dann aussieht. So werden zum Beispiel unter dem Punkt „Contexts“ die passenden Contexte angezeigt, da es sich um einen follow-up Intent handelt. Außerdem wurde der passende Intent „filter-by-area“ zugeordnet, sowie der Parameter „italian“. Unter dem Punkt „Diagnostic Info“ kann man noch zusätzlich die JSON Anfrage und Antwort vom Webhook anschauen.

Jedoch kann man in der Google Cloud Console die Cloud Function testen, wie der untenstehende Screenshot zeigt.

Dazu kopiert man die JSON Webhook Anfrage aus der obigen „Diagnostic Info“ und startet den Test. Wie man sehen kann, funktioniert die Anfrage und die passende fulfillmentMessage mit der MealDB Antwort kommt zurück. Außerdem werden darunter auch die Logs angezeigt.

Unser Fazit mit Dialogflow

Vorteile

  1. Dialogflow basiert auf einem sehr intuitiven Konzept; Intents, Entities etc. versuchen die Charakteristiken der natürlichen Sprache nachzustellen, sodass es einem leicht fällt die Funktionsweise eines Chatbots nachzuvollziehen.
  2. Die GUI ist sehr einfach und simpel gehalten; man konnte sich auch ohne Hilfe der Dokumentation schnell zurechtfinden.
  3. Da die KI bereits zur Verfügung gestellt wird, sind keine tieferen Kenntnisse in dem Gebiet notwendig.
  4. Der 90 Tage Testzeitraum war mit circa 300$ Guthaben mehr als ausreichend für einen Chatbot in unserem Rahmen

Nachteile

  1. Der größte Nachteil von Dialogflow war das sehr lange Deployen der cloud functions; ein Durchgang hat circa drei Minuten gedauert. Von daher war es ziemlich nervig, wenn auf Anhieb was nicht funktioniert hat und man bei jedem Rumprobieren, selbst wenn man nur eine Kleinigkeit geändert hat, drei Minuten warten musste. Bei einem tiefergehenden/umfangreicheren Problem, dass von mehreren Codestellen verursacht wurde, kann man sich die Frustration bei dem Fixen mit dieser Wartezeit wahrscheinlich gut vorstellen.
  2. Alle Funktionen mussten in einer einzigen file geschrieben werden. Es kam nicht selten vor, dass paar von uns parallel an der Datei gearbeitet haben, sodass wenn einer das Deployen gestartet hat, die Änderung des anderen verworfen wurden. Wenn eine Person das Deployen gestartet hat, konnten die anderen währenddessen auch nichts abspeichern, da es sofort gecancelled wurde.
  3. Der Inline-Editor ist eher rudimentär aufgebaut und weit von einer vollwertigen IDE entfernt. Es fängt bei banalen Kleinigkeiten an, wie dass man nicht erkennt, welche öffnende Klammer zu welcher schließenden gehört. Aber auch, dass keine Liste an verfügbaren Funktionen vorgeschlagen wird und dass viele Syntaxfehler toleriert werden, die beim Deployen aber zu Errors führen.
  4. Die Dokumentation von Dialogflow ist unvollständig und bleibt sehr oberflächlich. Wir haben viel Stackoverflow benutzt aber mussten letztendlich auch viel Herumprobieren.

Ausblick

Rezepte

Unser Zeitplan ist gut aufgegangen. Da unser Zeitraum – u.a. wegen der limitierten Zeit der Testversion – jedoch begrenzt war, konnten  wir nicht alle erdenklichen Intents in den Chatbot einbauen. Eine Möglichkeit den Chatbot zu erweitern wäre beispielsweise das Erstellen eines dritten FollowUpIntents auf die FilterBy Intents. Dieser würde daraus bestehen, dass der Endnutzer die Option wählt, drei weitere random Meals heraus zu filtern, sofern es genug weitere Gerichte gibt. Im Code müsste ein Teil eingebaut werden, der garantiert, dass die ersten drei Gerichte sich nicht wiederholen und nicht noch einmal mit ausgegeben werden. Der Intent würde außerdem voraussetzen, dass vorher mindestens drei Gerichte ausgegeben wurden. 

Webhook

Um Code nach der Zuordnung zu einem passenden Intent auszuführen, haben wir in Dialogflow unter dem Punkt „Fulfillment“ den Inline-Edior benutzt. Dort gibt es eine index.js und eine index.json, in welchen der gesamte benötigte Code erstellt wird. Der Code wird dann automatisch in Form von einer Cloud Function bereitgestellt und wenn nötig getriggert. Wie aber bereits erwähnt, hat sich das Arbeiten an einer einzigen Datei schwer schwierig gestaltet und das lange Deployen die Arbeit zusätzlich erschwert. Deshalb wäre es bei größeren Projekten mit mehr Intents durchaus eine Überlegung wert, den Webhook selbst zu hosten und die JSON Anfragen zu verarbeiten. So hätte man mehr Kontrolle und kann außerdem besser zusammenarbeiten und debuggen, da das lange Deployen entfällt. Um selbst den Webhook zu hosten, muss dieser ein paar Anforderungen erfüllen:

·   Der Webhook muss HTTPS POST Anfragen verarbeiten, HTTP wird nicht unterstützt

·   Die URL für Anfragen muss öffentlich zugänglich sein

·   JSON Anfragen müssen verarbeitet werden und eine JSON Antwort muss zurückkommen

WebAssembly: Das neue Docker und noch mehr?

If WASM+WASI existed in 2008, we wouldn’t have needed to created Docker. That’s how important it is. Webassembly on the server is the future of computing. A standardized system interface was the missing link. Let’s hope WASI is up to the task!

Tweet, Solomon Hykes (Erfinder von Docker), 2019

Dieser Tweet über WebAssembly (WASM) des Docker-Erfinders gibt einen Hinweis auf die mögliche Innovationskraft dieser Technologie. Der vorliegende Artikel vermittelt zunächst die wichtigsten technischen Grundlagen von WebAssembly, welche zum weiteren Verständnis notwendig sind. Anschließend wird der WASI-Standard näher beleuchtet, welcher die Brücke zur Containervirtualisierung schlägt. Schließlich betrachten wir mit Krustlet (Kubernetes) und wasmCloud zwei existierende Cloud-Technologien, die zentral auf WebAssembly basieren.

Continue reading

Games aus der Cloud, wo sind wir und wohin geht die Reise?

Cloud Gaming – flüssiges Zocken auch mit schlechter Grafikkarte? (esports.com)

Was genau ist Cloud Gaming?

Cloud Gaming lässt sich mit Remote Desktops, Cloud Computing und Video on Demand Diensten vergleichen. Im Grunde beinhaltet Cloud Gaming das Streamen von Videospielen aus der Cloud zum Endkunden. Dabei erfasst und überträgt der Client seine Nutzereingaben (bspw. Maus, Tastatur, Controller) an den Server. Während dieser wiederum die Gesamtspielweltberechnung sowie Nutzereingaben-Auswertung bewältigt. Der Client stellt lediglich die Kapazität bereit, die Frames in der gewünschten Qualität gestreamt zu bekommen. Wesentliche Vorteile ergeben sich für leistungsschwache Geräte, während der offenkundige Nachteil in der Notwendigkeit einer gut ausgebauten Dateninfrastruktur liegt.

Neben Game Streaming gehört zum Cloud Gaming auch das Hosten von Serverinstanzen für Onlinespiele, sowie das Bereitstellen von Plattformdiensten (bspw. Bestenlisten, Chatsysteme, Authentifizierung etc.). Wesentlicher Bestandteil ist des Weiteren das Angebot von leistungsfähigen Downloadservern.

Plattformdienste und etablierte Nutzung der Cloud

Plattform- oder Onlinedienste bieten Schnittstellen, welche Spielemetadaten zur Verfügung stellen. Diese Dienste Umfassen in der Regel Funktionalitäten wie Bestenlisten, Chat- und Gruppensysteme sowie Onlinelobbys, aber auch Metadienste wie Authentifizierung, Analyse, Zuordnung von Spieleridentitäten und Matchmaking. Hierbei können Dienste sowohl öffentlich im Internet als auch intern für andere Dienste zur Verfügung stehen. Während diese Systeme zu Beginn ihres Aufkommens noch als einzelne Monolithen bereitgestellt wurden, bieten heutige Cloudbetreiber solche Dienste als Microservices an. Dies sorgt dafür, dass sich die hohe Skalierbarkeit der Cloud auf die Plattformdienste übertragen lässt. Diese Skalierbarkeit ist vor allem deshalb wichtig geworden, da der Spielemarkt in den letzten Jahren stark gewachsen ist und auch die Spiele selbst immer ressourcenintensiver geworden sind. Als gutes Beispiel kann man hierfür eine der größten Spieleplattformen hernehmen: Steam. Steam integriert viele der angebotenen Spiele in die eigenen Plattformdienste. Dies umfasst Beispielsweise eine Freundesliste, Chatsysteme, Matchmaking und Verbindungssysteme. Zusätzlich wird auch die dahinter liegende Infrastruktur für die Vermarktung der Spiele von Steam gestemmt. Dies umfasst einen Webshop und Downloadserver.

Der große Aufwand und die Nachfrage nach diesen Diensten zeigen sich anhand des weltweiten Datenverkehrs von Steam. So kommt zum Zeitpunkt dieses Blogeintrags allein der deutsche Datenverkehr auf über 35 Petabyte innerhalb der letzten 7 Tage. Und dies wiederum entspricht lediglich ca. 4,3% des weltweiten Datenvolumens.

Aufnahme der Steam Statistiken am 16.03.2022 um 14.48 Uhr – Steam: Game and Player Statistics (steampowered.com)

Diese Zahlen steigen vor allem dann rasant an, wenn es zu speziellen Aktionen kommt, wie etwa dem Release eines neuen, stark erwarteten Spieles oder Sale-Aktionen vergleichbar mit einem Black-Friday für Games. Hierbei kommt es dazu, dass teilweise Millionen von Spielern gleichzeitig das neue Produkt erwerben und herunterladen wollen.

Diese starke Belastung spüren aber nicht nur Shop- und Downloadserver, sondern natürlich auch die klassischen dedizierten Spieleserver selbst. Gerade beim Release eines neuen Massive Multiplayer Onlinespiels (MMO) oder einer neuen großen Inhaltserweiterung versuchen sich gleichzeitig mehrere tausende Spieler auf den Spieleservern einzuloggen, während es zum Normalbetrieb meist nur ein Bruchteil der User ist. Auch hier hilft die hohe Skalierbarkeit der Cloud. Da solche Ereignisse normalerweise zu dem Betreiber bewussten und geplanten Zeiten auftreten können allerdings im Vorfeld schon Ressourcen reserviert und bereitgestellt werden.

Technische Herausforderungen des Game Streaming

Während Plattform- oder Onlinedienste auf die heute weit verbreiteten und gut etablierten Microservice Strukturen und Architektur zurückgreifen, eröffnen sich mit Games as a Service oder Game Streaming ganz neue Herausforderungen. Die Simulation des eigentlichen Spiels kann noch problemlos in einer emulierten Umgebung ablaufen und seine Inputs von außen beziehen, sowie das berechnete Resultat nach außen über einen Video stream abgeben. Das wahre Problem liegt allerdings in der Latenz. Bei Games handelt es sich im Gegensatz zu den meisten anderen Medien um ein interaktives Medium. Das heißt auf eine Aktion des Nutzers muss idealerweise eine unmittelbare Reaktion des Mediums erfolgen. Bei Game Streaming sind die Ansprüche daran besonders hoch, wenn es mit anderen interaktiven Medien wie einem Livestream mit Live Chat verglichen wird. Hier sind Verzögerungen von bis zu 1 Sekunde noch akzeptabel. Bei Spielen hingegen wird eine Latenz von wenigen Millisekunden erwartet. In dieser Zeit muss also die Eingabe beim Client registriert, an den Server geschickt, dort verarbeitet und ein neues Bild an den Client zurückgeschickt, decodiert und angezeigt werden.

Eine abstrahierte, allgemeine Darstellung der Abläufe eine Game Streaming Dienstes – CloudRetroPicture.png (787×492) (webrtchacks.com)

Was kann Cloud Gaming?

Viele Vorteile aus Cloud Computing und Video on Demand Diensten können sich direkt auf Game Streaming übertragen lassen.

  • Für das Spielen von Games aus der Cloud wird keine teure, eigene Hardware benötigt. Der Streamingdienst Betreiber stellt die nötige Hardware zur Verfügung, um das gewünschte Spiel auf einer maximalen Qualitätsstufe darstellen zu können.
  • Um die Wartung, Instandhaltung und Modernisierung der Hardwaresysteme kümmert sich der Streaming Anbieter. Für den Endkunden fallen dadurch keine hohen Einzelkosten an.
  • Spiele stehen jederzeit und überall auf vielen verschiedenen Endgeräten zur Verfügung. Auch leistungsschwache Geräte wie Smartphones, Smart TVs oder einfache Laptops können somit zum Spielen Hardwareintensiver Titel verwendet werden
  • Das Manipulieren und Betrügen in Online- und Singleplayerspielen wird durch das Streaming erheblich erschwert. Dies resultiert direkt aus der Begrenzung der Interaktionspunkte mit dem Spiel. Lediglich die Bildausgabe und die Nutzereingabe finden auf dem lokalen Gerät statt. Jegliche weitere Informationsverarbeitung, wie beispielsweise die Position eines Mitspielers bleiben dem Nutzer verborgen.

Neben den Vorteilen gibt es jedoch auch einige Nachteile:

  • Für Gamestreaming ist zwingend eine durchgehende, leistungsstarke Internetverbindung von Nöten. Denn im Gegensatz beispielsweise zum Video on demand kann bei Game Streaming kein Buffering verwendet werden. Dies ist bei Spielen allerdings nicht möglich, da der weitere Verlauf des Spieles direkt von den Eingaben des Spielers abhängig ist. Ein Verbindungsverlust führt somit zwangsläufig zu einer abrupten Unterbrechung der Spielsession.
  • Schwankungen in der Bandbreite führen zu einer Drosselung der Bildqualität und mindern damit das Spielerlebnis.
  • Das Übertragen bereits in Besitz befindlicher Spiele werden von vielen Anbietern nicht unterstützt. Dies kann dazu führen, dass Spiele entweder nicht zur Verfügung stehen oder nochmals auf der Streaming Plattform gekauft werden müssen.
  • Das Modifizieren der eigenen Spieldaten wird unterbunden, da nicht lokal auf die Spielinhalte zugegriffen werden kann. Dies verhindert, dass Spieler Modifikationen erstellen und ihr Spielerlebnis bei Bedarf individuell anpassen können.
  • Der Anbieter entscheidet welche Publisher und Franchise in seinem Portfolie aufgenommen werden. Dies erschwert vor allem kleine Studios oder Indie Entwickler sich auf dem Markt zu etablieren.
  • Spiele stehen zusätzlich nur solange zur Verfügung so lange sie sich im Angebot des Streamingdienst befinden oder dieser die Lizenzen hierfür besitzt.

Fazit

Cloud Gaming umfasst mehr als nur Game Streaming. Es ist bereits fester Bestandteil der heutigen Infrastruktur, da ein Großteil der Spiele auf Cloud-Dienste zurückgreift oder durch Plattformen wie bspw. Steam in diese integriert wird. Zwar steht Game Streaming an sich gerade erst in den Startlöchern, doch es würde mich nicht verwundern, wenn immer mehr Nutzer umsteigen oder zumindest teilweise auf dessen Vorteile zugreifen würden. Meiner Meinung nach wird es in absehbarer Zeit kein kompletter Ersatz für alle Spieler werden. Allerdings bin ich der Überzeugung, dass Game Streaming zum Netflix der Spieler wird, da es aufgrund der vorhandenen Technologien, Infrastruktur und Kunden über ein hohes Potential verfügt. Die Entwicklung der Streaming Ttechnologien steht allerdings erst an ihrem Anfang. Eine Weiterverfolgung wird sich in jedem Fall lohnen.

Quellen

Überblick über die Cloud-Gaming-Infrastruktur  |  Cloud Architecture Center  |  Google Cloud
Microservices – Wikipedia
Unreal Pixel Streaming in Azure – Azure Gaming | Microsoft Docs
Azure for Gaming – Azure Gaming | Microsoft Docs
Cloud-Gaming: Die besten Anbieter im Vergleich | heise Download
Cloud-Gaming im Vergleich: Die besten Spiele-Streamingdienste | NETZWELT
Cloud Gaming – flüssiges Zocken auch mit schlechter Grafikkarte? (esports.com)
Cloud gaming – Wikipedia
Xbox Cloud Gaming – Wikipedia
What is cloud gaming and how does it work? – Dot Esports
How Cloud Gaming and Streaming Games on Stadia and xCloud Works (makeuseof.com)
Open Source Cloud Gaming with WebRTC – webrtcHacks
Youtube: Google WebRTC project update & Stadia review

Discord Monitoring System with Amplify and EC2

Abstract

Discord was once just a tool for gamers to communicate and socialize with each other, but since the pandemic started, discord gained a lot of popularity and is now used by so many other people, me included, who don’t necessarily have any interest in video gaming. So after exploring the various channels on discord, I found that most of them have some kind of rules that users have to adhere to. But with channels that have more than 100 members, moderating becomes really tedious. The idea behind this project, which is a part of the lecture Software Development for Cloud Computing, is to automate this process by creating a discord monitoring system.

Features

With this system, an admin can add words that they want to be prohibited in the chat. These words are then used to delete messages that don’t adhere to the rules or to flag these messages for an admin to review when the system is not 100% certain that the message contains harmful content.

Architecture

Frontend

The client side is fairly simple, it is built with React.js and only has two sections. The first is flagged messages, where a moderator is able to review messages the system has flagged.

The second is where a moderator can add or remove censored words as shown below.

Backend

The server side of this monitoring system is built using express.js. The library discord.js was used to communicate with the discord api, and for the database I used dynamoDB.

The main part of the app is the following code:

discord.client.on("ready", () => {
  console.log(`Logged in as ${discord.client.user?.tag}!`);
  discord.client.on("message", (msg) => {
    const status = monitorSys.getScannedMessageStatus(msg.content);
    if (status === "FLAG") {
      flaggedMessagesController.addFlaggedMessage(msg);
    } else if (status === "HARMFUL") {
      messagesController.deleteHarmfulMessage(msg.id);
    } else {
      console.log("SAFE");
    }
  });
});
start();
discord.client.login(process.env.DISCORD_KEY);

Here the bot goes online and starts scanning every message that goes into the chat. With a helper method the system then either flags a message or deletes it right away if it contains harmful content.

EC2

The backend is deployed on an ec2 instance that is based on an Amazon Linux image. When creating this instance we add a script, as shown below, to install the CodeDeploy Agent that we are going to use later for the CI/CD.

#!/bin/bash
sudo yum -y update
sudo yum -y install ruby
sudo yum -y install wget
cd /home/ec2-user
wget https://aws-codedeploy-us-east-1.s3.amazonaws.com/latest/install
sudo chmod +x ./install
sudo ./install auto

Then we need to add these rules as shown below. The one with port 80 is necessary to be able to download data from the internet and the one with port 3000 is for the client to have access to our server.

CI/CD

Frontend

CI/CD On the frontend was done using GitHub actions and amplify. With GitHub I created an action that runs whenever a branch gets pushed. This pipeline runs our tests first and checks if all succeed. If one or more tests fail, this branch can’t be merged to main. This restrictions can be activated directly from the settings of our repository on GitHub. To create this action, create a file ./.github/workflows/tests.yml with the following:

name: Node.js CI
on:
  pull_request:
    branches: [ main ]
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16.x]
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm install
    - run: npm test

After a branch gets merged with main, Amplify builds the new version and deploys the app when the build is ready. Amplify provides a really straightforward way to deploy a React app. We just have to create a new app on Amplify and connects it with a specific branch on GitHub.

Amplify then detects the Javascript library that’s being used e.g. React, Vue or Angular and adjust the settings accordingly. Environments variables can be added directly on Amplify.

Backend

Here I used CodeDeploy to deploy the newest version of our backend whenever it’s pushed to the main branch. To achieve this, we need to first create a pipeline on CodeDeploy and connect it to our repository on GitHub, which is similar to what we did on Amplify. After that we need to add some scripts to our project for CodeDeploy. These files should be stored inside of ./scripts.

The first script file we need is called before_install.sh

#!/bin/bash

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
. ~/.nvm/nvm.sh
nvm install node

DIR="/home/ec2-user/discord-monitoring-server"
if [ -d "$DIR" ]; then
  echo "${DIR} exists"
else
  echo "Creating ${DIR} directory"
  mkdir ${DIR}
fi

With that we first download node and npm and then we create a working directory if it does not already exist.

Then we create a file called application_start.sh

#!/bin/bash
#give permission for everything in the discord-monitoring-server directory
sudo chmod -R 777 /home/ec2-user/discord-monitoring-server
#navigate into our working directory where we have all our github files
cd /home/ec2-user/discord-monitoring-server
#add npm and node to path
export NVM_DIR="$HOME/.nvm" 
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # loads nvm 
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # loads nvm bash_completion (node is in path now)
#install node modules
npm install
#start our node app in the background
npm start > app.out.log 2> app.err.log < /dev/null &

And lastly, we have application_stop.sh:

#!/bin/bash
#Stopping existing node servers
echo "Stopping any existing node servers"
pkill node

After creating these scripts we also need to add the appspec.yml file, which specifies the target folder in our ec2 instance and 3 hooks, each with its respective script file we created earlier.

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/discord-monitoring-server
hooks:
  ApplicationStop:
    - location: scripts/application_stop.sh
      timeout: 300
      runas: ec2-user
  BeforeInstall:
    - location: scripts/before_install.sh
      timeout: 300
      runas: ec2-user
  ApplicationStart:
    - location: scripts/application_start.sh
      timeout: 300
      runas: ec2-user

Conclusion

During the Software Development for Cloud Computing lecture, we learned a lot of theory about cloud computing and the different services Amazon and IBM provides. We also got to see some examples on the different subjects that were discussed during the semester. But with this project I was only able to implement and work on some of these topics. The first and main one was deploying the app on Amplify and EC2 and the second, which I personally found very interesting, is continuous integration and continuous delivery. This lecture gave me a really good overview on what can be achieved with cloud computing and I am hoping to learn way more about the topic in the near future.