Über unsMediaKontaktImpressum
Kai Wähner 24. April 2018

Apache Kafka und die DSGVO: Wie vergisst ein Logfile?

Dass sich am 25. Mai dieses Jahres einiges ändern wird, ist kein Geheimnis mehr. Mit 99 Artikeln in 11 Kapiteln wird die neue Datenschutzgrundverordnung die Regeln zur Verarbeitung personenbezogener Daten durch private Unternehmen und öffentliche Stellen EU-weit vereinheitlichen. Einen dieser Artikel wollen wir heute genauer betrachten: Das Recht auf Vergessenwerden, das in der Überschrift des Artikel 17 bewusst so genannt wird, ist eines der zentralen Rechte der DSGVO, international mit dem Kürzel GDPR (General Data Protection Regulation) bezeichnet.

Es bedeutet, dass eine Person das Recht hat, die Löschung aller sie betreffenden Daten zu fordern, wenn die Gründe für die Datenspeicherung entfallen. Darüber hinaus muss aber auch der Verarbeiter selbst aktiv die Daten löschen, wenn es für eine Speicherung und Verarbeitung keinen Grund mehr gibt. Hierzu zählen auch Logfiles, die von vielen Unternehmen mithilfe von Apache Kafka verarbeitet werden.

Apache Kafka ist ein Software-Projekt der Apache Software Foundation, das insbesondere der Verarbeitung von Datenströmen dient. Jegliche Art von Events können ausgewertet, analysiert und versendet werden. Kafka wird mittlerweile in vielen Unternehmen als unternehmenskritische, zentrale Streaming-Platfform eingesetzt, um große Datenmengen wie Logs, Sensoren, Social Feeds oder auch Banktransaktionen zu verarbeiten, eine Microservice-Architektur aufzubauen oder eine Machine Learning-Plattform im Unternehmen zu etablieren.

Neben Kafka Connect für die Integration und Kafka Streams für Stream Processing ergänzen weitere wichtige Open Source-Projekte wie die Schema Registry, der REST Proxy, Non-Java Clients oder KSQL die Grundidee von Kafka als zentraler Commit-Log.

Da Kafka nicht nur ein Messaging-System ist, sondern eine hochskalierbare Streaming-Plattform, werden Daten auch längerfristig in seinen sogenannten Topics aufbewahrt – je nach Anwendungsszenario eine Stunde, mehrere Monate oder auch für immer. Aber manchmal ist es auch wichtig, Messages löschen zu können, wie in unserem Fall: Das Recht auf Vergessenwerden. Das wirft eine ganz offensichtliche Frage auf: Wie löscht man beliebige Daten aus Kafka? Schließlich ist der zugrundeliegende Speichermechanismus ein unveränderliches Protokoll, das große Datenmengen auf viele Serverinstanzen und Festplatten verteilt. Dennoch muss man sich mit Kafka keine Sorgen um die GDPR-Compliance machen. In diesem Artikel wollen wir eine Möglichkeit der technischen Umsetzung von GDPR beleuchten. Juristische und unternehmerische Themen stehen dabei im Hintergrund.

Das Regulierungssystem der Datenschutzverordnung legt nicht nur fest, dass die Nutzer das Recht haben, vergessen zu werden, sondern auch das Recht, eine Kopie ihrer persönlichen Daten anzufordern. Unternehmen sind außerdem verpflichtet, detaillierte Aufzeichnungen darüber zu führen, welche Daten für welche Zwecke verwendet werden – eine Anforderung, für die die Aufzeichnung und Verfolgung der Nachrichten, die von Anwendung zu Anwendung wandern, ein Segen ist.

Löschung (oder Zensur) von Daten aus Kafka

Der einfachste Weg, Nachrichten aus Kafka zu entfernen, ist, sie einfach ablaufen zu lassen. Standardmäßig bewahrt Kafka die Daten zwei Wochen lang auf, was auf einen beliebig großen (oder kleinen) Zeitraum angepasst werden kann. Zusätzlich gibt es eine Admin-API, mit der Nachrichten explizit gelöscht werden können, wenn sie älter als eine bestimmte Zeit sind oder einen bestimmten Offset – d. h. eine definierte alte Position im Log – besitzen. Unternehmen wollen gleichwohl zunehmend die Fähigkeit von Kafka nutzen, Daten für längere Zeiträume aufzubewahren, z. B. für Event-Sourcing-Architekturen oder um eine einheitliche Zugriffsstelle für Informationen zu haben. In solchen Fällen ist es wichtig zu verstehen, wie man langlebige Daten in Kafka GDPR-konform machen kann. Dabei sind Compacted Topics das Mittel der Wahl, da sie es ermöglichen, Nachrichten explizit über ihren Key zu löschen oder zu ersetzen.

Daten werden aus Compacted Topics nicht entfernt – wie es in einer relationalen Datenbank der Fall wäre. Stattdessen verwendet Kafka einen Mechanismus, der näher an dem von Cassandra und HBase liegt, bei dem Datensätze zum Entfernen markiert und später gelöscht werden, wenn der Speicherreduzierungsprozess ("Compaction Process") läuft. Das Löschen einer Nachricht aus einem Compacted Topic ist so einfach wie das Schreiben einer neuen Nachricht an das Topic mit dem Key, den Sie löschen möchten, und einem Nullwert als Value. Wenn der Speicherreduzierungsprozess  läuft, wird die Nachricht für immer gelöscht. Hier ein Beispiel mit der Java-API:

//Create a record in a compacted topic in kafka producer.send(new ProducerRecord(CUSTOMERS_TOPIC, “Customer123”, “Donald Duck”)); 
//Mark that record for deletion when compaction runs producer.send(new ProducerRecord(CUSTOMERS_TOPIC, “Customer123”, null));

In diesem Fall werden die Informationen zum Kunde mit der ID "Customer123" gelöscht. Wenn der Key des Topics etwas anderes als die CustomerId ist, wird ein Prozess benötigt, um die beiden abzubilden. Wenn Sie z. B. ein Topic von Orders haben, dann benötigt man eine Zuordnung von Customer zu OrderId, die irgendwo gehalten wird. Um einen Kunden zu "vergessen", schaut man einfach in seinen Bestellungen nach und löscht ihn entweder explizit von Kafka, oder ändert die darin enthaltenen Kundeninformationen. Dies kann in einen eigenen Prozess eingebaut oder mit der Hilfe von Kafka durchgeführt werden.

Da ein Key in Kafka nicht nur ein String, sondern auch ein beliebiges Objekt sein kann, ist es möglich, auch sehr komplexe Szenarien umzusetzen. Ein Beispiel ist der seltenere, aber erwähnenswerte Fall, bei dem der Key (den Kafka für die Bestellung verwendet) völlig anders ist als der Key, der gelöscht werden soll. Nehmen wir an, dass Sie Ihre Bestellungen nach ProductId eingeben müssen. Diese Wahl des Keys erlaubt es Ihnen nicht, Aufträge für einzelne Kunden zu löschen, so dass die oben beschriebene einfache Methode nicht funktionieren würde. Es ist immer noch möglich, indem Sie einen Key verwenden, der aus beiden zusammengesetzt ist: Machen Sie den Key[ProductId][CustomerId], und verwenden Sie dann einen benutzerdefinierten Partitionierer im Producer (s. Producer Config: "partitioner.class"), der die ProductId und die Partitionen nur auf diesen Wert extrahiert. Dann kann die Nachrichten gelöscht werden, indem Sie den zuvor besprochenen Mechanismus verwenden und das Paar[ProductId][CustomerId] als Key verwenden.

Was ist mit den Datenbanken, aus denen ich Daten lese oder in die ich Daten schiebe?

Häufig gibt es eine Pipeline, in der Kafka mit Hilfe von Kafka Consumern – oder einfacher via Kafka Connect und entsprechenden Konnektoren wie beispielsweise JDBC – Daten von einer Datenbank in eine andere verschiebt. In diesem Fall muss der Datensatz in der Ursprungsdatenbank gelöscht und der Datensatz über Kafka an alle Zielsysteme weitergeleitet werden, die nachgeschaltet sind – entweder wieder über Kafka Connect und Konnektoren wie HDFS oder andere Kafka Producer APIs wie Java, C++, Python oder Go. Am einfachsten funktioniert so eine Pipeline unter der Verwendung von Change Data Capture (CDC): Das Löschen wird von der Quelle automatisch übernommen, über Kafka verbreitet und in den Zielsystemen gelöscht. Wenn kein CDC-fähiger Konnektor verwendet wird, benötigt man einen benutzerdefinierten Mechanismus zur Verwaltung von Löschvorgängen.

Wie lange dauert der Speicherreduzierungsprozess, um eine Nachricht zu löschen?

Standardmäßig läuft der "Compaction Process" periodisch ab und gibt Ihnen keinen klaren Hinweis darauf, wann eine Nachricht gelöscht wird. Die Einstellungen können aber für verbindlichere Garantien angepasst werden. Der beste Weg, dies zu tun, ist, den Verdichtungsprozess so zu konfigurieren, dass er kontinuierlich abläuft. Hier sollte zusätzlich eine Bandbreiten-Limitierung hinzugefügt werden, damit er den Rest des Systems nicht übermäßig beeinflusst:

# Ensure compaction runs continuously log.cleaner.min.cleanable.ratio = 0.00001 
# Set a limit on compaction so there is bandwidth for regular activities log.cleaner.io.max.bytes.per.second=1000000

Die Einstellung "log.cleaner.min.cleanable.ratio" auf 0 würde den Speicherreduzierungsprozess kontinuierlich laufen lassen. Hier wird ein kleiner, positiver Wert verwendet, so dass der Log Cleaner nicht ausführt, wenn es nichts zu reinigen gibt, sondern schnell einspringt, sobald es ihn gibt. Ein sinnvoller Wert für den Log Cleaner-Wert "log.cleaner.io.max.bytes.per.second" ist [max I/O des Plattensubsystems] x 0.1 /[Anzahl der komprimierten Partitionen]. Also sagen wir, dass dies mit 1MB/s berechnet wird, dann wird ein Topic von 100GB die entfernten Einträge innerhalb von 28 Stunden bereinigen. Natürlich kann dieser Wert eingestellt werden, um die gewünschten bzw. vom Gesetz geforderten Garantien zu erhalten.

Eine letzte wichtige Überlegung ist, dass die Partitionen eins Kafka Topics aus einer Reihe von Dateien, den sogenannten Segmenten, bestehen und das letzte Segment (dasjenige, in das geschrieben wird) nicht für den Speicherreduzierungsprozess in Betracht gezogen wird. Das bedeutet, dass ein Topic mit niedrigem Durchsatz möglicherweise für längere Zeit Nachrichten im neuesten Segment ansammeln kann, bevor es rollt und die Verdichtung einsetzt. Um dies zu erreichen, können wir das Segment zwingen, nach einer definierten Zeitspanne zu rollen. Zum Beispiel würde die Konfiguration "log.roll.hours=24" erzwingen, letzte Segmente alle 24 Stunden zu reduzieren, auch wenn ihre Größenbeschränkung noch nicht erreicht ist.

Tuning und Monitoring

Es gibt eine Reihe von Konfigurationen für das Tuning des Speicherreduzierungsprozess (. die Konfigurationsmöglichkeiten für die log.cleaner.*-Parameter in den Dokumentationen). Außerdem veröffentlicht der Speicherreduzierungsprozess JMX-Metriken über seinen Fortschritt. Sie können ein Topic so einstellen, dass es sowohl ein Compacted Topic ist und gleichzeitig eine Verfallszeit ("Rentention Time") besitzt, so dass die Daten nie länger als die Verfallszeit gehalten werden.

Zusammenfassung

Kafka bietet unveränderliche Topics, bei denen Einträge nach einer konfigurierten Zeit abgelaufen sind sowie Compacted Topics, bei denen Nachrichten mit bestimmtem Key zum Löschen vorgemerkt werden können. Dabei existiert auch die Möglichkeit, Löschungen aus den Quellsystemen in Zielsysteme zu übertragen. Obwohl Kafka auf unveränderlichen Protokollen als grundlegende Abstraktion basiert, bietet es Werkzeuge, die den Anforderungen von GDPR einfach und elegant gerecht werden.

Neben den beschriebenen Möglichkeiten, das Log von Kafka direkt anzupassen und Daten zu löschen, gibt es noch eine weitere Möglichkeit: Die Daten einfach nicht mehr konsumieren. Dies ist oftmals deutlich einfacher umzusetzen. Die Consumer-Anwendung (egal ob Kafka Streams, KSQL oder ein anderer Kafka-Consumer) kann Daten für den entsprechenden Nutzer filtern und nicht mehr weitergeben/-verarbeiten. Die Daten dieses Nutzers werden dadurch nicht gelöscht, aber nicht mehr verwendet. Dies ist – nach heutigem Verständnis und des GDPR und mangels Gerichtsentscheidungen – auch ausreichend. 

Autor

Kai Wähner

Kai Wähner ist als Technology Evangelist bei Confluent tätig. Seine Schwerpunkte liegen in den Bereichen Big Data Analytics, Machine Learning, Microservices, Internet of Things und Blockchain.
>> Weiterlesen
botMessage_toctoc_comments_9210