AWS-Abrechnungsdaten grafisch darstellen
Ein Weg mittels InfluxDB und Grafana
AWS bietet eine Vielzahl von Tools und Dashboards, um die Abrechnungsdaten seiner Services zu verstehen, von der Verfolgung über die Visualisierung bis hin zur Alarmierung. Viele Anwendungsfälle werden mit den nativen AWS-Tools abgedeckt. In der Realität haben jedoch viele Unternehmen und Betriebsteams sehr individuelle Anwendungsfälle für die Überwachung und Alarmierung, bei denen Abrechnungsdaten berücksichtigt werden müssen. Einige könnten:
- nicht in der komfortablen Lage sein, sich nur auf einen einzigen Cloud-Anbieter zu konzentrieren und verschiedene Abrechnungsdatenquellen oder -formate vereinheitlichen müssen
- ihr bestehendes Überwachungs- und Warnsystem, das auf ihre Bedürfnisse zugeschnitten ist, auch für die Verarbeitung von Abrechnungsdaten nutzen wollen
- ihre eigene Machine-Learning-Anwendung trainieren wollen, um mögliche Optimierungen in ihren eigenen oder in den Abrechnungsdaten ihrer Kunden zu finden.
Glücklicherweise bietet AWS die Möglichkeit, die Abrechnungsdaten in einem einfachen CSV-Format zu exportieren. Dies ermöglicht es uns, einen maßgeschneiderten Ansatz zur Verarbeitung von AWS-Abrechnungsdaten zu entwickeln. In diesem Artikel erfahren Sie, wie Sie AWS-Abrechnungsdatensätze verarbeiten, bei Bedarf umwandeln und in influxDB, eine beliebte Zeitreihendatenbank, übertragen können. Anschließend können Sie diese Werte mit Grafana grafisch darstellen.
Identifizieren und verstehen Sie die Quellfelder der Kostenberichte
Bevor wir mit der Verarbeitung von CSV-Daten beginnen können, müssen wir die Bedeutung der Felder verstehen. Beachten Sie, dass AWS selbst ein Legacy-Format mit der Bezeichnung "Detailed billing report" (DBR) und das aktuelle Format "Cost and Usage Report" (CUR) anbietet. Der CUR selbst kann mehr als 120 Felder in seiner CSV-Datei enthalten. Die Dokumentation zur Hand zu haben, ist auf jeden Fall hilfreich: Kostenberichterstattung. Wenn Sie einen detaillierten Business-Intelligence-Bericht anstreben, benötigen Sie eine Data-Warehousing-Anwendung. In diesem Fall können diese Berichte von Amazon Redshift-Fakturierung mit AWS Redshift importiert und abgefragt werden. Da wir jedoch ein Alarmierungs-Tool und ein Betriebs-Dashboard erstellen möchten, werden wir die Felder deutlich kürzen, um unsere Überwachungsinfrastruktur nicht zu zerstören. Interessante Werte, die man aus dem CUR berücksichtigen sollte, sind:
- lineItem/LineItemType: Die CUR enthält nicht nur On-Demand-Kosten, sondern auch Steuern, Erstattungen, Vorabgebühren und mehr. Wenn Sie nur die On-Demand-Kosten benötigen und die Steuern dem Unternehmenscontrolling überlassen wollen, filtern Sie dieses Feld auf den Wert "Verwendung".
- lineItem/ProductCode: der Produktcode vermerkt den verwendeten AWS-Service, z.B. "AmazonEC2".
- lineItem/UsageAccountId: das Konto, das die Ressource genutzt hat
- lineItem/UnblendedCost: die Kosten für den jeweiligen Posten. Abhängig von Ihrem Anwendungsfall gibt es auch die Felder UnblendedRate und UnblendedUsage sowie BlendedCosts/Rates, die Durchschnittspreise für Organisationen angeben. Im Zweifelsfall sollten Sie die Dokumentation konsultieren.
- identity/TimeInterval: Die Berichte geben keinen Zeitstempel an, wann das abgerechnete Ereignis eingetreten ist, sondern das Intervall auf Stundenbasis. Wir verwenden dieses Intervall und extrahieren einen Zeitstempel, der für ein Dashboard ausreicht. Für die Verfolgung der Lebenszeit einer Ressource sind die Felder lineItem/UsageStartDate und lineItem/UsageEndDate möglicherweise besser geeignet.
Planung des Datenmodells
InfluxDB ist die Datenbank unserer Wahl in diesem Beispiel, um die Daten zu speichern und als Backend für unsere Visualisierung und Überwachung zu dienen. Eine andere beliebte Wahl für diese Aufgabe wäre Prometheus. InfluxDB unterteilt die Daten in zwei Kategorien: Tags und Felder.
Tags dienen der Erstellung eines Index und der Partitionierung von Datensätzen, sie sind hauptsächlich dazu gedacht, Metadaten zu enthalten und die Daten zu strukturieren. Die "eigentlichen" Daten sind in Feldern enthalten. Ein Datensatz mit Feldern und Tags benötigt außerdem einen Messungsnamen, um eine Reihe von Werten zu gruppieren, und einen Zeitstempel für jeden Datenpunkt, da InfluxDB eine Zeitreihendatenbank ist.
Die Auswahl der richtigen Datenmenge für eine Überwachungsprobe ist schwer zu beurteilen. Ein optimistischer Datenanalyst möchte vielleicht alle Daten sammeln und die Anwendungsfälle später formulieren. Ein Betriebsingenieur hingegen möchte seine Überwachungsinfrastruktur so weit wie möglich in "Echtzeit" betreiben, wobei kleinere Datensätze definitiv einfacher zu handhaben und weniger verschwenderisch sind.
Extrahieren, Transformieren, Laden
Da wir mehrere unterschiedliche Eingabequellen und möglicherweise mehrere unterschiedliche Überwachungsanwendungen haben, müssen wir die Daten transformieren. Für eine eingehende CSV-Datei müssen wir festlegen, welche CSV-Felder als Tags und welche als Felder verwendet werden sollen. Die Grundlage wird ein benutzerdefiniertes Schema sein:
csv_format = {
'dbr-cost-allocation': {
'tags': [
'productcode',
'linkedaccountname'
],
'fields': [
'totalcost'
],
'time': 'invoicedate'
},
"cur_report": {
'tags': [
'lineitem/lineitemtype',
'lineitem/productcode',
'lineitem/usageaccountid'
],
'fields': [
'lineitem/unblendedcost'
],
'time': 'identity/timeinterval'
},
....
Jeder Abschnitt definiert eine eigene InfluxDB-Messung, die angibt, welche CSV-Werte als Zeitstempel, Feld oder Tag verwendet werden sollen. Alle anderen CSV-Werte werden verworfen. Die obigen Beispiele verdeutlichen bereits die Unterschiede zwischen dem alten DBR-Format und dem neueren CUR-Format, obwohl es sich bei beiden um native AWS-Abrechnungssätze handelt. Um beide zu vereinheitlichen und einheitliche Dashboards erstellen zu können, werden wir eine weitere Tabelle verwenden, um Felder bei Bedarf umzubenennen:
map_fields = {
'linkedaccountname': 'accountname',
'lineitem/lineitemtype': 'chargetype',
'lineitem/productcode': 'productcode',
'lineitem/unblendedcost': 'totalcost',
'lineitem/usageaccountid': 'accountname'
}
Ein aufmerksamer Leser wird in der obigen Auflistung über eine Zeile stolpern: unblendedcost wird in totalcost umbenannt, obwohl oben gesagt wurde, dass unblendedcosts Kosten von ganz bestimmten Posten sind, auf jeden Fall keine Gesamtsumme. Dies wird erst mit dem nächsten Abschnitt sinnvoll:
reduce_values = {
....
"cur_report":
"fields": (add, 'totalcost')
…
(Wobei map und reduce Funktionen höherer Ordnung bedeuten: Fold_(higher-order_function, nicht Googles MapReduce im Speziellen.)
Die oben genannten Felder werden mit jedem anderen Eintrag, der denselben Primärschlüssel hat, mit der genannten Funktion kombiniert. In diesem Fall werden alle nicht kombinierten Kosten (die im obigen Listing bereits umbenannt wurden!) addiert, um die Gesamtkosten zu berechnen. InfluxDB betrachtet einen eindeutigen Schlüssel als das Produkt aus Tags x Messung x Zeitstempel. Ein weiterer möglicher Anwendungsfall wäre die Berechnung der teuersten Servicenutzung, indem eine 'max'-Funktion anstelle einer 'add'-Funktion verwendet wird.
Um nun alle oben genannten Punkte zu kombinieren, können wir einen Algorithmus erstellen, der jede CSV-Datei umwandelt und ein benutzerdefiniertes Datenmodell erstellt, das als Pseudocode dargestellt wird:
for csv in csv_list:
reduce(rename(filter(format, csv)))
# für jede csv-Zeile einen Filter/ein Schema anwenden, die Felder umbenennen und
# schließlich Felder summieren
Jetzt haben wir also einen Plan, aber wir müssen noch:
Abrufen von Berichten aus S3
Ein Hauptmerkmal von AWS und anderen Cloud-Anbietern ist die Benutzerfreundlichkeit und die Möglichkeit, fast jede Aktion mit gut dokumentierten (und implementierten!) APIs durchzuführen. Für alles, was mit AWS zu tun hat, ist die zentrale Anlaufstelle für Python Boto3: boto3.amazonaws.com
Das Herunterladen einer csv-Datei ist so einfach wie:
s3client.download_file(Bucket=bucket, Key=file_key, Filename=local_filename)
... wenn wir nicht auf unterschiedliche Dateinamen, Speicherorte und Formate Rücksicht nehmen müssen. Aber wir können für die Dateiabfrage dasselbe Schema verwenden wie für das Datenmodell. Zunächst wird festgestellt, ob es sich bei einer Datei im s3-Bucket um einen Abrechnungsbericht handelt, indem der Dateiname mit einem Muster abgeglichen wird; anschließend werden alle Dateien gescannt und die gefundene Liste zurückgegeben.
regex_pattern = {
'3rd-party-preprocessed': '^\d\d\d\d-\d\d-\d\d.zip$',
#will match f.e.: 2019-01-01.zip
'native_dbr': '^\d\d\d\d\d\d\d\d\d\d\d\d-aws-cost-allocation-.*.csv',
#will match <AWS-account-id>-aws-cost-allocation-2019-01-01.csv
'cur_report' = "{}/{}/{}/{}-Manifest.json".format(prefix,report_name,billing_period_regex,report_name)
# will match CUR report path using the regex:
# billing_period_regex = '\d\d\d\d\d\d\d\d-\d\d\d\d\d\d\d\d'
}
...
finished = False
response = s3client.list_objects_v2(Bucket=bucket)
while not finished:
for c in response['Contents']:
if re.match(regex_pattern[csv_formatting], c['Key']):
result.append(c['Key'])
#more items to process?
finished = not response['IsTruncated']
if not finished:
logging.info("Fetching next 1000 items")
continue_token = response['NextContinuationToken']
response = s3client.list_objects_v2(Bucket=bucket,ContinuationToken=continue_token)
return result
Das Gleiche wie für den Abgleich von Dateinamensmustern wird wahrscheinlich auch für all die anderen Details benötigt, die ein Programmierer und/oder Dateningenieur sehr schätzt: Zeitstempel und Zeitzonen, Dateikodierung, CSV-Stile (nicht immer kommagetrennt...) und mehr.
So kann es beispielsweise vorkommen, dass eine CSV-Datei mit ; als Trennzeichen, Unix-Zeilenterminator und latin-1-Kodierung formatiert ist, während die AWS-eigene Datei den Windows-Zeilenterminator, getrennte Felder mit , und utf-8-Kodierung verwendet. Die folgende Auflistung veranschaulicht dieses Problem:
if '3rd-party-csv_file' in filename:
csv_file = file_obj.readlines()
csv_decoded = [ x.decode('latin-1') for x in csv_file]
dialect = csv.unix_dialect
dialect.delimiter = ';'
csv_reader = csv.DictReader(csv_decoded, dialect=dialect)
Für jemanden, der einen solchen Ansatz implementiert: Denken Sie immer an Bobby Tables -
Graph AWS Billing Data
Nachdem die Daten entdeckt, heruntergeladen, extrahiert, formatiert, umbenannt, addiert und in die influxDB übertragen wurden, können wir endlich schicke Dashboards und scharfe Alarme erstellen. Dies ist jedoch eine Kunst für sich und könnte in einem anderen Blogartikel ausführlich behandelt werden. Da dieser Artikel jedoch den Titel Graphing AWS Billing Data trägt, zeige ich Ihnen ein einfaches Dashboard, das das oben vorgeschlagene Datenmodell verwendet. Die folgende Abfrage gegen influxDB in einem Grafana-Panel:
Sie erhalten einen schönen Graphen, der bereits in der Lage ist, entweder nach Produkt, Konto oder Gebührentyp aufzuschlüsseln. Wie unten zu sehen:
Von hier aus können Sie Ihre eigene Visualisierung für den von Ihnen benötigten KPI erstellen. Die Spitzen, die Sie in den Diagrammen sehen, sind Dienste, die auf Tagesbasis abgerechnet werden, z. B. S3. Wenn Sie nur die Gesamtkosten pro Tag benötigen, können Sie diese als Mittelwert pro Tag darstellen, um die Spitzen abzuflachen.