Beliebte Suchanfragen

Cloud Native

DevOps

IT-Security

Agile Methoden

Java

//

Automatisch skaliertes Cloud Native Consent Management in der Google Cloud

28.6.2021 | 15 Minuten Lesezeit

Immer häufiger ersetzen unsere Kunden lokale Rechenzentren durch eine Cloud-Infrastruktur. Die Gründe sind Ausfallsicherheit, Wartbarkeit und vor allem Skalierbarkeit. Mit dem letzten dieser Aspekte befassen wir uns in diesem Blogartikel anhand eines realen Fallbeispiels.

Durch die DSGVO Richtlinien kommt nahezu keine Website, kein Webshop und keine App mehr an der Verwaltung der Datenschutzrechtlichen Einwilligungen der Nutzer vorbei. Gerade in Webshops ist die stabile und schnelle Verarbeitung von Nutzerdaten zu Peak Zeiten wie dem „Black Friday” wichtig, um keinen Kunden zu verlieren. In einer klassischen, hauseigenen Infrastruktur benötigen die Vorbereitungen auf diesen Ansturm zum Teil Wochen (Bestellung neuer Server-Komponenten, Installation, Integration usw.). Am Ende entsteht ein System, was zu jeder Sekunde diese enorm hohen Anfragen bearbeiten kann. Jedoch müssen an > 95 % der Tage im Jahr deutlich weniger Anfragen pro Sekunde verarbeitet werden.

Genau dieses Problem und eine mögliche Lösung mit Hilfe der Google Cloud schauen wir uns in diesem Artikel im Detail an.

Ziel ist es, eine einfache Simulation zu erstellen, bei der > 15.000 Anfragen pro Sekunde in der Cloud verarbeitet und persistiert werden, wobei die Cloud-Dienste automatisch skalieren. Gleichzeitig soll die Zeit des Schreibvorgangs im Consent Management (CM) erfasst werden. Das folgende Beispiel, inklusive Step-by-Step-Anleitung findet sich auf Github . Wir folgen dabei größtenteils Googles Referenzarchitektur für Ad-Tech .

Zunächst betrachten wir den Teil des Consent Managements. Das CM-System selbst hat als Endpoint eine Cloud Function, welche die Anfragen an ein Pub/Sub Topic sendet. Ein Dataflow Job nimmt die Nachrichten entgegen und schreibt diese in eine Bigtable. Zum Auslesen der nutzerspezifischen Consent-Einstellungen kommt wieder eine Cloud Function zum Einsatz.

Um Last (simulierte Client Requests) auf das skizzierte Consent Managementsystem zu bringen, werden wir Dateien mit Request Payloads generieren, diese im Cloud Storage ablegen und dann die einzelnen Payloads per Dataflow Job an einen Pub/Sub Topic schicken.

So, nun aber Schritt für Schritt und ganz von vorne!

Voraussetzungen

Um dieses Beispiel „hands-on” zu verfolgen, gibt es ein paar Voraussetzungen. Zunächst ist ein Google-Konto mit dem Zugriff auf die Google-Cloud-Plattform erforderlich. Zusätzlich muss für das Projekt, auf welchem die Simulation laufen soll, das Billing aktiviert sein. Hierfür ist eine Kreditkarte erforderlich. Google bietet zum Erkunden der Dienste ein Free Tier in Höhe von 300$ . Zusätzlich sollte die Google Cloud CLI installiert sein, da wir viele der verwendeten Dienste einfach per Kommando anlegen. Eine Schritt-für-Schritt-Anleitung findet ihr auf Github .

Achtung! Die verwendeten Cloud-Dienste sind kostenpflichtig. Am Ende weisen wir daher in diesem Beitrag darauf hin, die verwendeten Dienste am Ende wieder abzuschalten bzw. die verwendeten Ressourcen zu löschen.

codecentric Consent Management

Rein in die Cloud, aber wie?

Als erstes müssen wir eine Möglichkeit finden, Daten von überall in die Cloud schicken zu können. Ein sehr einfacher Service, den Google uns anbietet, sind die Cloud Functions. Cloud Functions sind serverlose „pay as you go“-Funktionen, welche komplett ohne Serververwaltung auskommen. Diese Funktionen haben erstmal keine Limitierung in der Anzahl der Aufrufe und skalieren automatisch. Die Funktionen können entweder per HTTP oder anderen Google-Cloud-Ereignissen ausgelöst werden.

Kommen wir nun zum Entrypoint unseres Consent Managements, welche in einer echten Umgebung von Millionen von Clients aufgerufen werden könnte: der Cloud Function.

Normalerweise würde das Consent-Managment-System anderen Diensten über eine HTTP-Schnittstelle zur Verfügung gestellt werden. In unserem hands-on-Beispiel nutzen wir jedoch einen Daten-Generator über Google Pub/Sub, wodurch das auslösende Ereignis ein Pub/Sub-Topic ist.
Dieses Topic ist die Voraussetzung um die Entrypoint Cloud Function zu erstellen. Auf das Topic gehen wir im Kapitel zum Daten-Generator weiter unten näher ein. Daher stellen wir die Cloud-Function nur kurz vor, legen sie aber noch nicht an.

Der folgende Quellcode zur Entrypoint Cloud Function ist bereits auf das Auslesen der Daten aus Pub/Sub ausgerichtet. Um die Function über HTTP auszulösen, muss an dieser Stelle nur die Verarbeitung der Input Message angepasst werden.

Die Nachrichten werden von dem point der Cloud Function „cm-scale-endpoint” entgegengenommen, mit einem Zeitstempel versehen und an das folgende Pub/Sub Topic „request_buffer” weitergereicht.

Als auslösendes Ereignis haben wir das PubSub-Topic „simulator-client-request“ gewählt. Dadurch ist unser Input kein HTTP-Request, sondern eine PubSubMessage.
Durch das Format der Nachrichten müssen wir an dieser Stelle den String mit dem Key ‚Data‘, base64, decodieren, um diesen manipulieren zu können. Zusätzlich fügen wir zur Zeitmessung des Schreibvorgangs einen Zeitstempel hinzu und schicken die Nachricht in das nächste Pub/Sub Topic „request-buffer“.

1import base64
2import json
3import os
4 
5from google.cloud import pubsub_v1
6 
7# TODO(dev): Set your PROJECT_ID here
8project_id = ""
9topic_name = "request_buffer"
10 
11publisher = pubsub_v1.PublisherClient()
12 
13def publish_to_request_buffer(event, context):
14   """Background Cloud Function to be triggered by Pub/Sub.
15   Args:
16        event (dict):  The dictionary with data specific to this 
17        type of event. The `data` field contains the PubsubMessage
18        message. The `attributes` field will contain custom
19        attributes if there are any.
20        context (google.cloud.functions.Context): The Cloud 
21        Functions event metadata. The `event_id` field contains
22        the Pub/Sub message ID. The `timestamp` field contains
23        the publish time.
24   """
25   print(
26       f"This Function was triggered 
27         by messageId {context.event_id} 
28         published at {context.timestamp}"
29   )
30   data = event["data"]
31   if data:
32       # Base64 to JSON string
33       message_str = base64.b64decode(data).decode('utf-8')
34       # JSON string to dict
35       message_dict = json.loads(message_str)
36   else:
37       raise Exception(f"No data in message {event['messageId']}")
38 
39   topic_path = publisher.topic_path(project_id, topic_name)
40 
41   # Retrieve message from data and add timestamp field
42   message_dict["message_timestamp"] = context.timestamp
43   message_json = json.dumps(message_dict)
44   message_bytes = message_json.encode('utf-8')
45 
46   try:
47       publish_future = publisher.publish(topic_path, data=message_bytes)
48       publish_future.result()  # Verify the publish succeeded
49       return 'Message published.'
50   except Exception as e:
51       raise e

ACHTUNG: Zur richtigen Ausführung muss im Quell-Code zur Cloud Function die
{Projekt Id} auf die des eigenen Projekts gesetzt werden.

Wie verarbeiten wir Millionen von Nachrichten ohne Verluste?

Was ist das Schlimmste, das einem Shop passieren kann? Verlorene Nachrichten, ein überlastetes System zu Peak-Zeiten, was zu Request Timeouts oder sogar einer Downtime des gesamten Systems führt.

On-Premises-Systeme sind bis zu einer festen Ressourcen-Obergrenze dimensioniert. Sofern alles richtig konfiguriert ist, können die Systeme bis zu dieser Obergrenze skalieren. Der Nachteil besteht darin, dass hier laufende Kosten für Systemressourcen anfallen, die zu einem großen Teil der Zeit ungenutzt bleiben. In Peak-Zeiten kann die Obergrenze des Systems überschritten werden, wodurch Nachrichten/ Daten verloren gehen.
Unter Umständen kann es sogar zu Downtimes des gesamten Systems kommen. Die Auswirkungen wären unzufriedene Nutzer, die zu einem anderen Dienst wechseln, was zu großen monetären Verlusten führt. Um den Empfang jeder Nachricht sicherzustellen, sollte der Empfang der Nachrichten von der Verarbeitung entkoppelt werden.

Google bietet hierzu den Message-Broker-Dienst Pub/Sub an.
Pub/Sub ist ein vollständig verwalteter Echtzeit-Messaging-Dienst, mit dem wir Nachrichten zwischen lose gekoppelten Diensten senden und empfangen können.

Vereinfacht gesagt können riesige Mengen an Nachrichten an den Messaging-Dienst gesendet werden und von dessen Subscribern wieder konsumiert werden. Um keine Nachrichten zu verlieren, werden nur die Nachrichten aus der Warteschlange im Messaging-Dienst gelöscht, welche vom Subscriber bestätigt wurden. Ein entscheidender Vorteil bei der Verwendung von Pub/Sub ist, dass Millionen von Nachrichten asynchron von den Subscribern verarbeitet werden können und es so zu Peakzeiten nicht zu Request Timeouts kommt. Die Nachrichten selbst können von den Subscribern aktiv gepulled, oder von Pub/Sub an die Subscriber gepusht werden.
In unserem Beispiel nutzen wir das Pub/Sub-Topic „request-buffer“, um die eingehenden Nachrichten zu sammeln und weiterzuleiten.

Das Topic legen wir mit folgendem Kommando an:

gcloud pubsub topics create request-buffer

Auf ein Pub/Sub-Topic hört immer mindestens ein konsumierender Subscriber. Um den Subscriber mit Nachrichten zu beliefern, wird eine Subscription benötigt, die das Topic mit dem Subscriber verbindet.

Mit dem o.g. CLI-Kommando wird keine Subscription angelegt, wodurch eingehende Nachrichten erst einmal verworfen werden. Erst wenn ein Subscriber mit einer Subscription mit einem Topic verbunden ist, wird eine Warteschlange von Nachrichten aufgebaut.

Wie arbeiten wir unsere Warteschlange effektiv ab?

Hier haben wir sogar mehrere Möglichkeiten. Wir könnten wieder eine Cloud Function nutzen, jedoch bietet der Google-Service Dataflow für diese Aufgabe eine höhere Skalierbarkeit . Zusätzlich garantiert der Dataflow-Ansatz jedes Element nur einmal zu verarbeiten, sodass in unserem Beispiel keine doppelten Einträge persistiert werden.

Google Cloud Dataflow ist ein Service zum Ausführen von Data Processing Patterns. Mit Dataflow können sowohl Streaming als auch Batch Prozesse ausgeführt werden. Für eine Vielzahl von Anwendungsfällen stellt Google bereits Templates zur Verfügung. Sobald die Anwendungsszenarien komplexer werden, lassen sich eigene Batch- oder Streaming Jobs mit dem Apache Beam SDK erstellen.
Der Hauptunterschied zwischen der Batch- oder Streaming-Arbeitsweise liegt in den verwendeten Datenquellen. Die Streaming-Verarbeitung ist für eine theoretisch unendliche Datenquelle gedacht, wogegen die Batch-Verarbeitung in Verbindung mit endlichen Datenquellen genutzt wird.
In unserem Beispiel verwenden wir die build-in Entwicklungsumgebung der Google Cloud im Dataflow-Bereich, um einen Streaming-Job aufzusetzen. Darüberhinaus sind noch zwei weitere Ansätze dokumentiert um einen Dataflow-Job zu entwickeln.
Zunächst lässt sich im Dataflow-Bereich eine Entwicklungsumgebung auf Basis eines Jupyter-Notebooks erstellen. Dort kann die folgende Pipeline-Definition in einem Jupyter-Notebook übernommen und ausgeführt werden.

1import apache_beam as beam
2import json
3from apache_beam.options.pipeline_options import PipelineOptions
4from apache_beam.io.gcp.bigtableio import WriteToBigTable
5 
6import logging
7 
8# TODO(dev): Set your PROJECT_ID here
9PROJECT_ID = ""
10# The Google Cloud PubSub topic that we 
11# are reading from for this example.
12readFromTopic = f"projects/{PROJECT_ID}/topics/request_buffer"
13 
14class MyOptions(PipelineOptions):
15  @classmethod
16  def _add_argparse_args(cls, parser):
17    parser.add_argument(
18    '--bigtable-project',
19    help='The Bigtable project ID, this can be different than your '
20    'Dataflow project')
21    parser.add_argument(
22    '--bigtable-instance',
23    help='The Bigtable instance ID')
24    parser.add_argument(
25    '--bigtable-table',
26    help='The Bigtable table ID in the instance.')
27 
28class CreateRowFn(beam.DoFn):
29  def process(self, record):
30    from google.cloud.bigtable import row
31    record_dict = json.loads(record)
32    logging.info(record_dict)
33    logging.info(type(record_dict))
34 
35    keycombined = f"{record_dict.get('domain')}#{record_dict.get('user')}"
36    direct_row = row.DirectRow(row_key=keycombined)
37    import datetime as dt
38    direct_row.set_cell(
39    "cf1", #column_family muss zuvor angelegt sein
40    "services", #column_name
41    json.dumps(record_dict['services']), # cell_value
42    timestamp =dt.datetime.utcnow())
43    direct_row.set_cell(
44    "cf1", #column_family muss zuvor angelegt sein
45    "message_timestamp", #column_name
46    json.dumps(record_dict['message_timestamp']), # cell_value
47    timestamp =dt.datetime.utcnow())
48    return [direct_row]
49 
50def run(argv=None):
51  """Build and run the pipeline."""
52  options = MyOptions(
53  flags=argv,
54  runner='DataflowRunner',
55  project=PROJECT_ID,
56  job_name='buffer-to-bigtable',
57  temp_location=f'gs://{PROJECT_ID}-scale/dataflow/tmp',
58  region='europe-west1',
59  autoscaling_algorithm='THROUGHPUT_BASED',
60  streaming=True)
61 
62  with beam.Pipeline(options=options) as p:
63    p | "read" >> beam.io.ReadFromPubSub(topic=readFromTopic) | 
64                                beam.ParDo(CreateRowFn()) | 
65                                WriteToBigTable(
66  project_id=PROJECT_ID,
67  instance_id="scale-bigtable",
68  table_id="scale-traffic")
69 
70run()

In Zeile 4 muss die aktuelle Projekt-Id gesetzt werden, damit die Beschreibung des Dataflow-Jobs funktionieren kann. Die Pipeline wird am Ende des Codes beschrieben und besteht aus drei Schritten. Als erstes nutzen wir eine Apache Beam integrierte Funktion, um uns auf das „request_buffer”-Pub/Sub-Topic zu subscriben und damit alle Nachrichten der Dataflow-Pipeline zuzuführen.
Der nächste Pipeline-Schritt parsed eingehende Nachrichten in ein JSON-Objekt und generiert aus den vorliegenden Informationen ein Row-Set für die BigTable. Als letztes sorgt die build-in Funktion „WriteToBigTable” aus dem Paket “apache_beam.io.gcp.bigtableio” dafür, dass das Row-Set in die BigTable geschrieben wird.

Aber wohin nur mit den ganzen Daten ¯\_(ツ)_/¯ ?

Ok, die Antwort haben wir eigentlich gerade schon gegeben, trotzdem lohnt es sich, einmal genauer hinzuschauen. Google bietet eine Vielzahl von Möglichkeiten, Daten zu persistieren. Um die richtige Entscheidung treffen zu können, schauen wir uns zunächst die Datensätze an, welche wir speichern wollen – und wie viele Daten wir speichern wollen.

In unserem Szenario gehen wir davon aus, dass unser Service von mehreren großen Online-Shops verwendet wird und dabei viele Millionen Datensätze in Echtzeit verarbeitet werden müssen. Dazu können die Informationen, welche die einzelnen Shops zur Verfügung stellen, variieren. Nutzerkennung, genehmigte Services und Shopkennung sind zwar konsistent, allerdings könnten auch von einzelnen Shops Daten über die Dauer der Zustimmung übermittelt werden. Eine traditionelle relationale Datenbank kommt bei großen Datenmengen schnell an ihre Grenzen, eine Verarbeitung in Echtzeit zu garantieren. Zusätzlich stellt das starre Datenmodell bei der Anbindung von neuen Partnern mit neuen Anforderungen oder bei stark differenzierten Datenstrukturen ein weiteres Hindernis dar. Auch Dokumenten-basierte Datenbanken erreichen bei riesigen Datenmengen schnell ihr Limit an Effektivität.

In unserem Fall, mit voneinander abweichenden Datenstrukturen, in dem es auf schnelle Reaktionszeit ankommt und riesige Datenmengen persistiert werden müssen, bietet Google die Speicherung in der BigTable an. Diese haben wir im Dataflow Job bereits angebunden.

Die BigTable wurde von Google designed, um die Web-Suche zu indexieren. Die Hauptanforderungen waren Performance, Skalierbarkeit und Konsistenz (strong consistency). Hierbei handelt es sich um eine NoSQL (no-join) Datenbank, die nur genau einen Index besitzt, den Row-Key. Im Gegensatz zu relationalen Datenbanken sind die Columns in der Bigtable nicht festgelegt.

Eine Table besteht aus Rows und Column Families (CF), welche Columns logisch gruppieren. Dabei können beliebig viele neue Columns zur Laufzeit zu einer Column Family hinzugefügt werden. Die Columns selbst werden über einen Column Identifier (CI) adressiert. Durch die Limitierung auf einen Index ist das richtige Design des Row-Keys entscheidend, da jede Anfrage mit Referenz auf einen Wert einer Column einen Full Table Scan erzwingt. Der Key sollte dabei so erstellt werden, dass alle Abfragen, ohne weiteren Bezug auf Spalten der Zeile, möglich sind. In unserem Fall wollen wir abfragen können, welche Cookies der User in einem bestimmten Shop freigegeben hat. Unseren Row Key haben wir daher folgendermaßen designed:

DOMAIN#USER-HASH z.B.: codecentric.de#43126438-86af-4ac7-8f34-c4eb62b7c5a4

Zusätzlich sind in der BigTable alle Zellen automatisch historisiert. Bei der Erstellung der Bigtable kann konfiguriert werden, wie viele Versionen pro Zelle behalten werden sollen. Alte Zellen werden automatisch über den Garbage Collector aufgeräumt. So wird durch eine EDIT-Operation automatisch eine neue Version der Zelle erzeugt, selbst wenn per Konfiguration definiert wurde, dass nur eine Version behalten werden soll.

Für unser Beispiel legen wir über folgendes Kommando die Bigtable-Instanz an:

gcloud bigtable instances create scale-bigtable \
--cluster=scale-bigtable \
--cluster-zone=europe-west1-b \
--display-name=scale-bigtable \
--cluster-storage-type=SSD

In dieser Instanz legen wir nun eine neue Tabelle an mit dem Namen „scale-traffic” an. Hierbei soll der GC alle Einträge wegräumen, die entweder älter als 10 Tage sind, oder die eine neuere Version haben.

cbt createtable scale-traffic "families=cbt:maxage=10d||maxversions=1"

Im Anschluss müssen wir noch die Column Family anlegen:

cbt createfamily scale-traffic cf1

Wie lesen wir die Daten aus?

Hier bedienen wir uns wieder an einer Google Cloud Function. Diese wird diesmal mit Trigger HTTP angelegt und enthält folgenden Code:

1from google.cloud import bigtable
2from google.cloud.bigtable.row_set import RowSet
3 
4client = bigtable.Client()
5 
6 
7def bigtable_read_data(request):
8    instance = client.instance("scale-bigtable")
9    table = instance.table("scale-traffic")
10    request_json = request.get_json(silent=True)
11    domain=request_json.get("domain")
12    uid= request_json.get("uid")
13    row_key = domain+"#"+uid
14    row_data = table.read_row(row_key)
15 
16    timestamp = row_data.cell_value("cf1","message_timestamp".encode("utf-8"))
17    services = row_data.cell_value("cf1","services".encode("utf-8"))
18 
19    return f"services: {services}, timestamp: {timestamp}"

Damit steht die Architektur unseres Consent Management Systems.

Simulated Client Requests

Jetzt sorgen wir für den Input …

Die Anwendung hat jetzt einen Stand erreicht, eingehende Daten zu verarbeiten. Damit die automatische Skalierung in den verwendeten Google-Diensten sichtbar wird, müssen wir nun für den notwendigen Input sorgen.
In unserem Beispiel definieren wir folgende Struktur als einen Input-Request für unsere Anwendung:

1{
2  "user": "b42116de-19cf-42e0-ac9d-d7fc594a6b7f",
3  "domain": "codecentric.de",
4  "services": [
5    "essential-2",
6    "essential-3",
7    "marketing-3",
8    "personalization-3"
9  ]
10}

Die Struktur soll einen Request-Payload darstellen, der den User, eine Domain und die verbundenen Consent-Einstellungen enthält. Der User wird durch eine eindeutige ID identifiziert, gefolgt von der Domain des Service-Partners. Die Consent-Einstellungen sind in einem Array zusammengefasst und sollen die aktivierten Services des Website-Besuchers ausdrücken, welchen er zugestimmt hat.

Um unsere Request-Payloads zu erzeugen verwenden wir ein einfaches Python-Script . Dieses kann mehrere Dateien erzeugen, in denen pro Zeile der Payload für je einen Request als JSON abgelegt wird. In unserem Beispiel haben wir vier Dateien mit insgesamt 2.000.000 Zeilen erstellt, um den Workload zu simulieren.

Als nächstes machen wir uns die nahtlose Integration der Google-Services zu nutze. Wie bereits am Anfang erwähnt existieren verschiedene Möglichkeiten unseren Einstiegspunkt, die Cloud Function „ml-scale-endpoint” auszulösen. In unserem Simulator nutzen wir ein Pub/Sub-Topic aufgrund der losen Kopplung und der automatischen Skalierung. Für unsere Simulationszwecke stellt Pub/Sub einen sehr einfachen Weg dar, die Nachrichten in das System zu laden.

Das Erstellen des Topics „simulator-client-request” erfolgt mit dem folgenden Kommando:

gcloud pubsub topics create simulator-client-request

Zu Beginn des Artikels haben wir in der „ml-scale-endpoint” Function darauf hingewiesen, dass ein Trigger vom Typ „Cloud Pub/Sub” ausgewählt werden muss.

Da nun das Topic „simulator-client-request” erstellt wurde, kann die Eingangs-Funktion ebenfalls erstellt werden. Hierbei ist nochmal genau darauf zu achten, dass als Trigger-Topic „simulator-client-request” ausgewählt wird.

Wo liegen unsere Simulationsdaten?

Die generierten Dateien speichern wir in die Google Cloud, um die Nachrichten ohne Flaschenhals in das Pub/Sub-Topic „simulator-client-request” zu laden.
Der Google Cloud Storage-Dienst bietet das dauerhafte Speichern von Dateien oder Objekten und den Zugriff von nahezu überall. Hierbei kann, je nach Last auf den Objekten, zwischen unterschiedlichen Speicherklassen gewählt werden.
In unserem Fall verwenden wir den Cloud Storage, um unsere generierten Request Files abzulegen. Dazu legen wir einen neuen Bucket in der Region europe-west1 (günstigste europäische Region) mit der Speicherklasse `STANDARD` an.

gsutil mb -p $PROJECT_ID -c STANDARD -l europe-west1 gs://${PROJECT_ID}-scale

Um die lokal generierten Dateien in dem Bucket zu speichern, kann einfach folgendes Kommando verwendet werden:

gsutil cp example_requests.txt gs://${PROJECT_ID}_scale/messages/

Hierbei wird die Lokale Datei example_requests.txt in einen neuen Ordner `messages` im eben erstellten Bucket gespeichert.

Dataflow – Templates / Wie lade ich die Daten in das System?

Der Dataflow Service ist stark integriert in andere Google-Cloud-Dienste und gewährt den Zugriff auf verschiedene Templates, um schnell einen passenden Batch-Job zu erstellen, ohne das Rad neu erfinden zu müssen. Die Template-Auswahl kann im Dataflow-Bereich der Google Cloud Console unter „Create Job From Template” eingesehen werden.

An dieser Stelle nutzen wir folgendes CLI-Kommando:

gcloud dataflow jobs run FileToRequestSimulator
--gcs-location gs://dataflow-templates-europe-west1/latest/GCS_Text_to_Cloud_PubSub
--region europe-west1
--max-workers 5
--num-workers 1
--worker-region europe-west1
--staging-location gs://${PROJECT_ID}-scale/temp
--parameters
inputFilePattern=gs://${PROJECT_ID}-scale/messages/*.txt,
outputTopic=projects/${PROJECT_ID}/topics/simulator_client_request

In der Google Cloud Console wird nun ein Batch-Job mit dem Namen „FileToRequestSimulator“ angezeigt, der auf dem GCS_Text_to_Cloud_PubSub-Template basiert.

Ergebnis Skalierung – Wer kümmert sich drum und wie sieht das aus?

Durch das Autoscaling-Feature der Google Cloud werden die Ressourcen unserer Beispiel-Anwendung automatisch skaliert. Im Folgenden haben wir die Last eines Testlaufs dokumentiert.

Der Screenshot zeigt den Dataflow-Job „buffer_to_bigtable“, wie er automatisch während der Verarbeitung skaliert.

Die Skalierungsänderungen werden protokolliert und können über die Dienst-Metriken eingesehen werden. Es wird die Zahl der Worker und der Grund für die Skalierungsänderung angegeben.

Das Autoscaling überwacht fortlaufend die Last auf dem System und fügt automatisch VM-Instanzen hinzu, um die Verarbeitung zu beschleunigen. Ebenso werden Rückgänge in der Last erkannt und die Zahl der Worker-VMs wieder reduziert.

In unserem Testlauf haben wir den Dataflow-Job für das Einladen des Beispiel-Requests in den PubSub viermal parallel gestartet, um eine hohe Last zu erzeugen.

Aus den Throughput-Metriken lässt sich ebenfalls entnehmen, wie viele Elemente je Sekunde verarbeitet wurden.

Ein anderes Beispiel für Metriken sind unsere Cloud Functions, welche nach dem gleichen Prinzip ausbalanciert werden.

In welchem Rahmen die Skalierung arbeitet, kann über Autoskalierungs-Richtlinien konfiguriert werden.

Wichtig: Am Ende alles löschen, sonst wird’s teuer …

gcloud pubsub topics delete simulator_client_request &&
gcloud pubsub topics delete request_buffer &&
gcloud functions delete scale_endpoint &&
gcloud dataflow jobs cancel buffer-to-bigtable &&
gcloud bigtable instances delete scale-bigtable &&
gcloud compute instances delete scale-notebook &&
gsutil rm -r gs://${PROJECT_ID}-scale

Ein schnellerer Weg, die verwendeten Ressourcen abzuschalten, ist das gesamte Projekt zu löschen.

Fazit und Ausblick:

An diesem Beispiel lässt sich gut erkennen, warum Cloud-Lösungen immer beliebter werden. Gerade durch die automatische Skalierung der verwendeten Ressourcen, sowohl nach oben, als auch das Freigeben nicht genutzter Ressourcen, ist eine Cloud-Lösung oft nicht nur kosteneffizienter als ein eigener Serverraum, oder eigenes Rechenzentrum, sondern auch deutlich robuster gegen nicht vorhersehbare Lastspitzen. Dieses Szenario bietet eine solide Ausbau-Basis für weitere Services der Google Cloud.
Was fehlt an dieser Stelle noch? Zum Beispiel haben wir die Möglichkeiten der Analyse der Daten hier noch gar nicht behandelt. Hier bietet Google mit ihrem Data Warehouse BigQuery die Möglichkeit, skalierbare und schnelle Analysen über riesige Datenmengen zu erstellen. Außerdem unterstützt BigQuery Datenanalysen über Machine-Learning-Modelle, um datenbasierte Vorhersagen treffen zu können.
Zwei weitere Themen, um an dieses Beispiel anzuknüpfen, wären zum einen die Kostenoptimierung der Cloud Dienste und die Möglichkeit des Cachings , um die Daten noch effizienter zu verarbeiten. Wir hoffen durch unseren Artikel und das Hands-On im Git einen praktischen Einblick in die Möglichkeiten der Google-Cloud-Dienste gegeben zu haben und stehen gerne für einen Austausch zur Verfügung.

Beitrag teilen

Gefällt mir

0

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.