Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Datentransfer zwischen Compute Engine und Cloud Storage optimieren

By Matthew PorterSep 21, 202119 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

Sind die Übertragungsraten zwischen Ihrer GCE VM und einem Cloud Storage Bucket langsamer als erwartet? Dann lesen Sie weiter – wir zeigen Ihnen, wie Sie den Upload- und Download-Durchsatz für Linux- und Windows-VMs ans Limit bringen.

Überblick

Vor Kurzem fragte mich ein Kunde von DoiT International, warum die Daten von der lokalen SSD seines Google Compute Engine (im Folgenden GCE) Windows Servers deutlich langsamer als erwartet in einen Cloud Storage Bucket hochgeladen wurden. Zunächst wollte ich nur ein simples Benchmarking der gsutil-Befehle durchführen, um zu zeigen, wie wirkungsvoll die laut GCP-Dokumentation idealen gsutil-Argumente sind. Aus dem geplanten "kurzen" Blick wurde jedoch eine vollumfängliche Untersuchung der Datentransfer-Performance zwischen GCE und GCS – denn meine ersten Ergebnisse waren ungewöhnlich und alles andere als erwartet.

Wenn Sie nur wissen möchten, mit welchen Methoden Sie Daten zwischen GCE und GCS auf einem Linux- oder Windows-Rechner am besten verschieben, scrollen Sie einfach ganz nach unten zu "Effective Transfer Tool Use Conclusions".

Wenn Sie sich dagegen über die bisweilen bizarren und oft kontraintuitiven Durchsatzraten gängiger Befehle und Argumente wundern wollen, bleiben Sie dran: Wir tauchen in die Details ein, die zu meiner umfangreichen Empfehlungsliste am Ende dieses Artikels geführt haben.

Linux-VM-Performance mit gsutil: Große Dateien

Auch wenn es beim Anliegen des Kunden um Datentransfer auf einem Windows Server ging, habe ich zunächst dort gemessen, wo ich mich am wohlsten fühle:

Linux, mit dem öffentlichen GCE-Image "Debian GNU/Linux 10 (buster)".

Da der Kunde bereits Dateitransfers von lokalen SSDs durchführte und ich ausschließen wollte, dass Netzwerk-Disks die Übertragungsgeschwindigkeit beeinflussen, habe ich zwei VM-Größen konfiguriert – n2-standard-4 und n2-standard-80 – jeweils mit einer angeschlossenen lokalen SSD, auf der die Benchmarks laufen.

Sowohl der GCS Bucket als auch sämtliche in diesem Artikel beschriebenen VMs wurden als regionale Ressourcen in us-central1 angelegt.

Um das Upload-Szenario des Kunden mit großen Dateien nachzustellen, habe ich eine leere Datei mit 30 GB Größe erzeugt:

fallocate -l 30G temp_30GB_file

Anschließend habe ich zwei häufig empfohlene gsutil-Parameter getestet:

  • -m: Führt parallele, multi-threaded Kopiervorgänge aus. Sinnvoll für die parallele Übertragung vieler Dateien, nicht aber für den Upload einzelner Dateien.
  • -o GSUtil:parallel_composite_upload_threshold=150M: Teilt große Dateien, die den angegebenen Schwellwert überschreiten, in Teile auf, lädt diese parallel hoch und fügt sie nach Abschluss aller Teile wieder zusammen.

Die geschätzte Maximal-Performance der lokalen SSD auf beiden VMs sieht so aus:

Lese-/Schreib-Durchsatzgrenzen der lokalen SSD

Mit gsutil sollten wir also bis zu 660 MB/s lesen und 350 MB/s schreiben können. Schauen wir uns die Upload-Benchmarks an:

time gsutil cp temp_30GB_file gs://doit-speed-test-bucket/
# n2-standard-4: 2m21.893s, 216.50 MB/s
# n2-standard-80: 2m11.676s, 233.30 MB/stime gsutil -m cp temp_30GB_file gs://doit-speed-test-bucket/
# n2-standard-4: 2m48.710s, 182.09 MB/s
# n2-standard-80: 2m29.348s, 205.69 MB/stime gsutil -o GSUtil:parallel_composite_upload_threshold=150M cp temp_30GB_file gs://doit-speed-test-bucket/
# n2-standard-4: 1m40.104s, 306.88 MB/s
# n2-standard-80: 0m52.145s, 589.13 MB/stime gsutil -m -o GSUtil:parallel_composite_upload_threshold=150M cp temp_30GB_file gs://doit-speed-test-bucket/
# n2-standard-4: 1m44.579s, 293.75 MB/s
# n2-standard-80: 0m51.154s, 600.54 MB/s

Wie es die gsutil-Dokumentation von GCP nahelegt, profitieren Uploads großer Dateien deutlich von -o GSUtil. Sobald mehr vCPUs für den parallelen Upload der Dateiteile zur Verfügung stehen, sinkt die Upload-Zeit drastisch: Auf der n2-standard-80 erreichen wir mit konstant 600 MB/s nahezu den maximalen SSD-Durchsatz von 660 MB/s. -m bringt bei nur einer Datei lediglich wenige Sekunden Zeitgewinn. Bis hierhin alles im erwartbaren Rahmen.

Werfen wir einen Blick auf die Download-Benchmarks:

time gsutil cp gs://doit-speed-test-bucket/temp_30GB_file .
# n2-standard-4: 8m3.186s, 63.58 MB/s
# n2-standard-80: 6m13.585, 82.23 MB/stime gsutil -m cp gs://doit-speed-test-bucket/temp_30GB_file .
# n2-standard-4: 7m57.881s, 64.28 MB/s
# n2-standard-80: 6m20.131s, 80.81 MB/s

Moment mal…

Die Download-Performance auf der 80-vCPU-VM erreichte gerade einmal 23 % des maximalen Schreibdurchsatzes der lokalen SSD. Außerdem bringt Multi-Threading mit -m bei diesem Einzeldatei-Download keinen Vorteil – und obwohl beide Maschinen weit unter ihrem maximalen Durchsatz liegen (10 Gbps bei der n2-standard-4, 32 Gbps bei der n2-standard-80) – bringt die größere Maschine derselben Familie immerhin eine ca. 30 % schnellere Download-Geschwindigkeit. Seltsam, aber bei Weitem nicht so seltsam wie die Tatsache, dass eine absurd teure VM nur ein Viertel des SSD-Schreibdurchsatzes erreicht.

Was läuft hier schief?

Nach langer Recherche zu diesem Thema fand ich keine Antworten – stattdessen stieß ich auf s5cmd, ein Tool, das Uploads in und Downloads aus S3 Buckets drastisch beschleunigen soll. Laut Eigenwerbung läuft es 12-mal schneller als die entsprechenden AWS-CLI-Befehle (z. B. aws s3 cp) – vor allem, weil es in Go geschrieben ist, einer kompilierten Sprache, im Gegensatz zur in Python geschriebenen AWS CLI. Wie der Zufall so will: Auch gsutil ist in Python geschrieben. Wird gsutil also durch seine Sprachwahl ausgebremst oder schlicht schlecht optimiert? Da sich GCS Buckets mit S3-API-Interoperabilität konfigurieren lassen: Lassen sich Uploads und Downloads schon allein dadurch beschleunigen, dass man auf ein kompiliertes Tool wie s5cmd setzt?

Linux-VM-Performance mit s5cmd: Große Dateien

Bis s5cmd lief, hat es ein wenig gedauert – vor allem, weil ich auf die harte Tour herausfinden musste, dass die GCS-Interoperabilität die Multipart-Upload-API von S3 nicht unterstützt. Da das Tool ausschließlich mit Blick auf AWS entwickelt wurde, scheitert es bei großen Datei-Uploads in GCP. Sie müssen -p=1000000 übergeben – ein Argument, das den Multipart-Upload zwingend deaktiviert. Mehr Infos in den s5cmd-Issues #1 und #2.

Beachten Sie auch, dass s5cmd einen Parameter -c für die Anzahl gleichzeitig übertragener Teile/Dateien bietet, mit einem Standardwert von 5.

Mit diesen beiden Argumenten im Hinterkopf habe ich folgende Linux-Upload-Benchmarks durchgeführt:

time s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 temp_30GB_file s3://doit-speed-test-bucket/
# n2-standard-4: 6m7.459s, 83.60 MB/s
# n2-standard-80: 6m50.272s, 74.88 MB/stime s5cmd --endpoint-url https://storage.googleapis.com cp -p=1000000 temp_30GB_file s3://doit-speed-test-bucket/
# n2-standard-4: 7m18.682s, 70.03 MB/s
# n2-standard-80: 6m48.380s, 75.22 MB/s

Wie zu erwarten, schneiden Uploads großer Dateien mangels Multipart-Strategie deutlich schlechter ab als mit gsutil. Wir sehen 75–85 MB/s im Vergleich zu den 200–600 MB/s, die gsutil liefert. Concurrency 1 statt des Standardwerts 5 wirkt sich nur minimal auf die Performance aus. Da s5cmd AWS als First-Class-Citizen behandelt und GCP nicht im Blick hat, lassen sich Uploads damit also nicht beschleunigen.

Hier die s5cmd-Download-Benchmarks:

time s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 s3://doit-speed-test-bucket/temp_30GB_file .
# n2-standard-4: 1m56.170s, 264.44 MB/s
# n2-standard-80: 1m46.196s, 289.28 MB/stime s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 s3://doit-speed-test-bucket/temp_30GB_file .
# n2-standard-4: 3m21.380s, 152.55 MB/s
# n2-standard-80: 3m45.414s, 136.28 MB/stime s5cmd --endpoint-url https://storage.googleapis.com cp -p=1000000 s3://doit-speed-test-bucket/temp_30GB_file .
# n2-standard-4: 2m33.148s, 200.59 MB/s
# n2-standard-80: 2m48.071s, 182.78 MB/stime s5cmd --endpoint-url https://storage.googleapis.com cp s3://doit-speed-test-bucket/temp_30GB_file .
# n2-standard-4: 1m46.378s, 288.78 MB/s
# n2-standard-80: 2m1.116s, 253.64 MB/s

Was für ein Sprung! Zwar schwanken die Download-Zeiten etwas, doch offenbar erreichen wir die optimale Geschwindigkeit, wenn wir -c und -p weglassen und auf den Standardwerten belassen. Den maximalen Schreibdurchsatz von 350 MB/s erreichen wir nicht ganz, aber die rund 289 MB/s auf einer n2-standard-4 liegen deutlich näher dran als die rund 64 MB/s, die gsutil auf derselben Maschine liefert. Das ist eine 4,5-fache Steigerung der Download-Geschwindigkeit – allein durch den Wechsel des Datentransfer-Tools.

Zusammengefasst für Linux:

  • Da s5cmd mit GCS keine Multipart-Uploads ermöglicht, ist es sinnvoll, weiterhin gsutil für Uploads zu GCS zu verwenden – sofern Sie -o GSUtil:parallel_composite_upload_threshold=150M setzen.
  • Beim Download lässt s5cmd mit Standardparametern gsutil meilenweit hinter sich. Allein der Wechsel auf ein Datentransfer-Tool in einer kompilierten Sprache bringt einen Geschwindigkeitszuwachs um das 4,5-Fache.

Windows-VM-Performance mit gsutil: Große Dateien

Falls Ihnen das oben Beschriebene noch nicht ungewöhnlich genug war: Schnallen Sie sich an, jetzt geht es mit Windows ans Eingemachte. Da der DoiT-Kunde mit Windows Server arbeitete, war es Zeit, dieses Betriebssystem zu benchmarken. Ich begann zu ahnen, dass das Problem nicht zwischen Tastatur und Stuhl saß.

Nachdem für Linux feststand, dass gsutil mit den richtigen Parametern beim Upload und s5cmd mit Standardparametern beim Download brilliert, war es an der Zeit, diese Befehle unter Windows auszuprobieren – wo mir mein dürftiger PowerShell-Erfahrungsstand wieder einmal seine Grenzen aufzeigen sollte.

Schließlich konnte ich Benchmarks von einer n2-standard-4-Maschine mit angeschlossener lokaler SSD erfassen, die auf dem GCE-VM-Image "Windows Server version 1809 Datacenter Core for Containers, built on 20200813" lief. Wegen der vCPU-basierten Lizenzgebühren von Windows Server habe ich in diesem Versuch auf Messungen mit einer n2-standard-80 verzichtet.

Eine wichtige Anmerkung, bevor wir auf die Metriken eingehen:

Die GCP-Dokumentation zum Anschließen lokaler SSDs empfiehlt für "alle Windows Server" den SCSI-Treiber statt des typischerweise unter Linux verwendeten NVMe-Treibers, da SCSI besser auf maximalen Durchsatz optimiert sei. Ich habe daraufhin zwei VMs mit je einer lokalen SSD aufgesetzt – eine via NVMe, eine via SCSI – um deren Performance zusammen mit den bisher untersuchten Tools und Parametern zu vergleichen.

Hier die Upload-Benchmarks:

Measure-Command {gsutil cp temp_30GB_file gs://doit-speed-test-bucket/}
# NVMe: 3m50.064s, 133.53 MB/s
# SCSI: 4m7.256s, 124.24 MB/sMeasure-Command {gsutil -m cp temp_30GB_file gs://doit-speed-test-bucket/}
# NVMe: 3m59.462s, 128.29 MB/s
# SCSI: 3m34.013s, 143.54 MB/sMeasure-Command {gsutil -o GSUtil:parallel_composite_upload_threshold=150M cp temp_30GB_file gs://doit-speed-test-bucket/}
# NVMe: 5m54.046s, 86.77 MB/s
# SCSI: 6m13.929s, 82.15 MB/sMeasure-Command {gsutil -m -o GSUtil:parallel_composite_upload_threshold=150M cp temp_30GB_file gs://doit-speed-test-bucket/}
# NVMe: 5m55.751s, 86.40 MB/s
# SCSI: 5m58.078s, 85.79 MB/s

Mir fehlen die Worte für meine Gefühle gerade

Ohne Argumente liegt der gsutil-Upload-Durchsatz bei rund 60 % des Wertes auf einer Linux-Maschine. Jede Kombination zusätzlicher Argumente verschlechtert die Performance. Wenn der Multipart-Upload aktiviert wird – was unter Linux eine Steigerung um 42 % brachte – sinkt die Upload-Geschwindigkeit hier um 35 %. Außerdem fällt auf: Lässt man -m weg und überlässt gsutil den optimalen Upload einer einzelnen großen Datei, ist der Upload vom NVMe-Laufwerk schneller als vom SCSI-Laufwerk – obwohl Letzteres laut Doku besser für Windows Server optimierte Treiber haben soll. Was ist hier los?!

Mit Werten um 80–85 MB/s lag die schwache Upload-Performance genau in dem Bereich, den der DoiT-Kunde beobachtete – sein Problem war also zumindest reproduzierbar. Schon das Weglassen des von GCP empfohlenen Arguments

-o GSUtil:parallel_composite_upload_threshold=150M für Uploads großer Dateien beseitigte beim Kunden eine Performance-Einbuße von 35 %. 🤷

Das Download-Benchmarking erzählt eine noch dramatischere Geschichte:

Measure-Command {gsutil cp gs://doit-speed-test-bucket/temp_30GB_file .}
# NVMe 1st attempt: 11m39.426s, 43.92 MB/s
# NVMe 2nd attempt: 9m1.857s, 56.69 MB/s
# SCSI 1st attempt: 8m54.462s, 57.48 MB/s
# SCSI 2nd attempt: 10m1.023s, 51.05 MB/sMeasure-Command {gsutil -m cp gs://doit-speed-test-bucket/temp_30GB_file .}
# NVMe 1st attempt: 8m52.537s, 57.69 MB/s
# NVMe 2nd attempt: 22m4.824s, 23.19 MB/s
# NVMe 3rd attempt: 8m50.202s, 57.94 MB/s
# SCSI 1st attempt: 7m29.502s, 68.34 MB/s
# SCSI 2nd attempt: 9m9.652s, 55.89 MB/s

Konsistente Download-Benchmarks waren mir aus folgendem Grund nicht möglich:

  • Jeder Download-Vorgang hing vor dem eigentlichen Start bis zu 2 Minuten lang.
  • Sobald er anlief, lief er mit etwa 68–70 MB/s, bis…
  • er gelegentlich erneut für unbestimmte Zeit pausierte.

Dieses ständige Hängen und Wiederanlaufen führte dazu, dass die durchschnittliche Download-Geschwindigkeit auf identischer VM und identischer Festplatte zwischen 23 MB/s und 58 MB/s schwankte. Bei diesen zufälligen, langen Aussetzern war es schlicht nicht möglich, sauber zu beurteilen, ob NVMe oder SCSI besser für Downloads geeignet ist. Mehr dazu später.

Windows-VM-Performance mit s5cmd: Große Dateien

Frustriert von der wilden und chaotischen gsutil-Download-Performance habe ich schnell zu s5cmd gewechselt – vielleicht löst es das Hänger-Problem oder mildert zumindest dessen Auswirkungen?

Zunächst die s5cmd-Upload-Benchmarks:

Measure-Command {s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 temp_30GB_file s3://doit-speed-test-bucket/}
# NVMe: 6m21.780s, 80.46 MB/s
# SCSI: 7m14.162s, 70.76 MB/sMeasure-Command {s5cmd --endpoint-url https://storage.googleapis.com cp -p=1000000 temp_30GB_file s3://doit-speed-test-bucket/}
# NVMe: 12m56.066s, 39.58 MB/s
# SCSI: 8m12.255s, 62.41 MB/s

Wie schon beim s5cmd-Upload unter Linux bremst die fehlende Multipart-Upload-Unterstützung das Tool aus. Die Upload-Performance mit Concurrency 1 entspricht in etwa der unter Linux. Bei Concurrency auf dem Standardwert 5 bricht die Performance dramatisch ein und schwankt stark. Der Effekt der Concurrency ist hier ungewöhnlich heftig – aber da die s5cmd-Upload-Performance ohnehin deutlich schlechter ist als die von gsutil (was auch ohne Multipart-Uploads bemerkenswert ist), wollen wir s5cmd sowieso nicht zum Upload nutzen. Diese Concurrency-Eigenheit von s5cmd beim Upload können wir also schlicht ausklammern.

Weiter mit den s5cmd-Download-Benchmarks:

Measure-Command {s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 s3://doit-speed-test-bucket/temp_30GB_file .}
# NVMe 1st attempt: 2m17.954s, 222.68 MB/s
# NVMe 2nd attempt: 1m44.718s, 293.36 MB/s
# SCSI 1st attempt: 3m9.581s, 162.04 MB/s
# SCSI 2nd attempt: 1m52.500s, 273.07 MB/sMeasure-Command {s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 s3://doit-speed-test-bucket/temp_30GB_file .}
# NVMe 1st attempt: 3m18.006s, 155.15 MB/s
# NVMe 2nd attempt: 4m2.792s, 126.53 MB/s
# SCSI 1st attempt: 3m37.126s, 141.48 MB/s
# SCSI 2nd attempt: 4m9.657s, 123.05 MB/sMeasure-Command {s5cmd --endpoint-url https://storage.googleapis.com cp -p=1000000 s3://doit-speed-test-bucket/temp_30GB_file .}
# NVMe 1st attempt: 2m17.151s, 223.99 MB/s
# NVMe 2nd attempt: 1m47.217s, 286.52 MB/s
# SCSI 1st attempt: 4m39.120s, 110.06 MB/s
# SCSI 2nd attempt: 1m42.159s, 300.71 MB/sMeasure-Command {s5cmd --endpoint-url https://storage.googleapis.com cp s3://doit-speed-test-bucket/temp_30GB_file .}
# NVMe 1st attempt: 2m48.714s, 182.08 MB/s
# NVMe 2nd attempt: 2m41.174s, 190.60 MB/s
# SCSI 1st attempt: 2m35.480s, 197.58 MB/s
# SCSI 2nd attempt: 2m40.483s, 191.42 MB/s

Auch wenn es bei den Downloads ähnliche Hänger und Schwankungen wie bei gsutil gibt, ist s5cmd auch hier wieder deutlich schneller. Zudem traten kürzere und/oder seltenere Hänger auf – ganz verschwunden sind sie aber nicht.

Anders als auf der Linux-VM, wo ich die maximale Performance durch das Weglassen von -c und -p erreicht habe, scheint hier die optimale Performance gerade durch deren explizite Angabe mit -c=1 -p=1000000 zustande zu kommen. Angesichts der zufälligen Hänger ist es schwer, diese Konfiguration als endgültig optimal zu deklarieren, aber mit diesen Argumenten läuft es solide. Wie bei gsutil lässt sich auch hier wegen der Hänger nicht eindeutig sagen, ob NVMe oder SCSI besser optimiert ist.

Um die Download-Geschwindigkeiten auf NVMe und SCSI mit den optimalen s5cmd-Argumenten besser einordnen zu können, habe ich eine Funktion geschrieben, die Durchschnitt, Minimum und Maximum der Laufzeit aus 20 wiederholten Downloads ausgibt – mit dem Ziel, kurzzeitige Hänger herauszumitteln:

Measure-CommandAvg {s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 s3://doit-speed-test-bucket/temp_30GB_file .}
### With 20 sample downloads
# NVMe:
# Avg: 1m48.014s, 284.41 MB/s
# Min: 1m23.411s, 368.30 MB/s
# Max: 3m10.989s, 160.85 MB/s
# SCSI:
# Avg: 1m47.737s, 285.14 MB/s
# Min: 1m24.784s, 362.33 MB/s
# Max: 4m44.807s, 107.86 MB/s

Die Laufzeit desselben Downloads schwankt also weiterhin – klar ist aber: SCSI bietet bei Downloads großer Dateien gegenüber NVMe insgesamt keinen Vorteil, obwohl es angeblich der ideale Treiber für lokale SSDs auf einer Windows-VM sein soll.

Prüfen wir noch, ob Uploads über NVMe tatsächlich performanter sind, indem wir dieselbe Mittelwertfunktion auf 20 wiederholte Uploads anwenden:

Measure-CommandAvg {gsutil cp temp_30GB_file gs://doit-speed-test-bucket/}
# NVMe:
# Avg: 3m23.216s, 151.17 MB/s
# Min: 2m31.169s, 203.22 MB/s
# Max: 4m13.943s, 121.42 MB/s
# SCSI:
# Avg: 5m1.570s, 101.87 MB/s
# Min: 3m2.649s, 168.19 MB/s
# Max: 35m3.276s, 14.61 MB/s

Damit bestätigen sich unsere früheren Einzelmessungen: NVMe ist beim Upload tatsächlich performanter als SCSI. Im Durchschnitt über zwanzig Wiederholungen ist NVMe sogar deutlich überlegen.

Bei Windows-VMs sollten wir also – entgegen der GCP-Doku – nicht nur auf -o GSUtil:parallel_composite_upload_threshold=150M bei gsutil-Uploads zu GCS verzichten, sondern auch SCSI vermeiden und NVMe als Treiber für die lokale SSD bevorzugen, um Uploads (und möglicherweise auch Downloads) zu beschleunigen. Außerdem zeigt sich, dass es bei Downloads und Uploads häufig unvorhersehbare Pausen gibt, die zwischen 1–2 Minuten und bis zu 10–30 Minuten dauern können.

Was sage ich dem Kunden …

An dieser Stelle teilte ich dem Kunden mit, dass es beim Datentransfer mit einer Windows-VM systembedingte Beschränkungen gibt, die sich aber teilweise so abmildern lassen:

  • Optionale Argumente bei gsutil cp für Uploads großer Dateien auf den Standardwerten belassen – entgegen der Empfehlung der GCP-Dokumentation.
  • Für Downloads s5cmd -c=1 -p=1000000 statt gsutil verwenden.
  • Für lokalen SSD-Speicher den NVMe- statt des SCSI-Treibers nutzen, um sowohl die Upload- als auch möglicherweise die Download-Geschwindigkeit zu verbessern – entgegen der Empfehlung der GCP-Dokumentation.

Gleichzeitig wies ich den Kunden darauf hin, dass sich Uploads und Downloads drastisch beschleunigen lassen, wenn man die Windows-bedingten Hänger schlicht komplett umgeht: Daten per Disk-Snapshot auf eine Linux-Maschine übertragen und die Sync-Operationen mit GCS dann von einer an Linux angeschlossenen Festplatte aus durchführen. Das erwies sich am Ende als der schnellste Weg, den erwarteten Durchsatz zwischen einer GCE-VM und GCS zu erreichen – und führte zu einem zufriedenen Kunden, der dennoch frustriert über die kuriosen Performance-Probleme seines Windows Servers blieb.

Mein Fazit aus dieser Erfahrung: gsutil ist nicht nur erschreckend schlecht für Operationen auf Windows-Servern optimiert – offenbar gibt es zudem ein grundlegendes Problem damit, wie GCS Daten von und nach Windows überträgt. Verzögerungen und Hänger treten sowohl bei gsutil als auch bei s5cmd auf, und zwar bei Downloads wie bei Uploads.

Das Kundenproblem war gelöst – meine Neugier allerdings noch nicht gestillt. Welche Bandbreiten-Kuriositäten würden mich erwarten, wenn ich statt einiger weniger großer Dateien viele kleine Dateien überträgt?

Linux-VM-Performance mit gsutil: Kleine Dateien

Zurück zu Linux: Ich habe die 30-GB-Datei in 50K (genauer: 50.001) Dateien aufgeteilt:

mkdir parts
split -b 644245 temp_30GB_file
mv x* parts/

Anschließend habe ich die Upload-Performance mit gsutil gemessen:

nohup bash -c 'time gsutil cp -r parts/* gs://doit-speed-test-bucket/smallparts/' &
# n2-standard-4: 71m30.420s, 7.16 MB/s
# n2-standard-80: 69m32.803s, 7.36 MB/snohup bash -c 'time gsutil -m cp -r parts/* gs://doit-speed-test-bucket/smallparts/' &
# n2-standard-4: 9m7.045s, 56.16 MB/s
# n2-standard-80: 3m41.081s, 138.95 MB/s

Wie erwartet beschleunigt -m für multi-threaded, parallelen Datei-Upload den Vorgang dramatisch – ohne diese Option sollte man einen großen Ordner mit Dateien gar nicht erst hochladen. Je mehr vCPUs die Maschine hat, desto mehr Datei-Uploads laufen parallel.

Hier die Download-Benchmarks mit gsutil:

nohup bash -c 'time gsutil cp -r gs://doit-speed-test-bucket/smallparts/ parts/' &
# n2-standard-4: 61m24.516s, 8.34 MB/s
# n2-standard-80: 56m54.841s, 9.00 MB/snohup bash -c 'time gsutil -m cp -r gs://doit-speed-test-bucket/smallparts/ parts/' &
# n2-standard-4: 7m42.249s, 66.46 MB/s
# n2-standard-80: 3m38.421s, 140.65 MB/s

Auch hier ist -m Pflicht – versuchen Sie nicht, einen großen Ordner ohne diese Option herunterzuladen. Wie beim Upload profitiert die gsutil-Performance von parallelen Übertragungen mit -m und vielen verfügbaren vCPUs.

Bei Massen-Uploads und -Downloads kleiner Dateien mit gsutil war unter Linux nichts Außergewöhnliches zu beobachten.

Linux-VM-Performance mit s5cmd: Kleine Dateien

Da bereits feststeht, dass s5cmd nicht für Datei-Uploads zu GCS taugt, beschränke ich mich im Folgenden auf die Linux-Download-Benchmarks:

nohup bash -c 'time s5cmd --endpoint-url https://storage.googleapis.com cp s3://doit-speed-test-bucket/smallparts/* parts/' &
# n2-standard-4: 1m19.531s, 386.26 MB/s
# n2-standard-80: 1m31.592s, 335.40 MB/snohup bash -c 'time s5cmd --endpoint-url https://storage.googleapis.com cp -c=80 s3://doit-speed-test-bucket/smallparts/* parts/' &
# n2-standard-80: 1m29.837s, 341.95 MB/s

Auf der n2-standard-4 ist der Massendownload kleiner Dateien mit s5cmd rund 6,9-mal schneller als mit gsutil. Es ist also sinnvoll, s5cmd sowohl für viele kleine als auch für größere Dateien zum Download zu verwenden.

Bei Massendownloads kleiner Dateien mit s5cmd wurde unter Linux nichts Außergewöhnliches beobachtet.

Windows-VM-Performance mit s5cmd: Kleine Dateien (und zusätzliche Tests mit großen Dateien)

Da s5cmd auf jedem Betriebssystem deutlich schnellere Downloads liefert als gsutil, betrachte ich bei den Windows-Download-Benchmarks für kleine Dateien ausschließlich s5cmd:

Measure-CommandAvg {s5cmd --endpoint-url https://storage.googleapis.com cp s3://doit-speed-test-bucket/smallparts/* parts/}
# NVMe:
# Avg: 2m39.540s, 192.55 MB/s
# Min: 2m35.323s, 197.78 MB/s
# Max: 2m44.260s, 187.02 MB/s
# SCSI:
# Avg: 2m45.431s, 185.70 MB/s
# Min: 2m40.785s, 191.06 MB/s
# Max: 2m50.930s, 179.72 MB/s

Wir sehen, dass das Herunterladen von 50K kleineren Dateien auf eine Windows-VM stabiler und vorhersehbarer abläuft als das Herunterladen deutlich größerer Dateien. NVMe schlägt SCSI nur knapp.

In diesem Anwendungsfall fällt eine bemerkenswerte Konsistenz und das Ausbleiben langer Hänger auf – ganz im Gegensatz zu den zuvor gesehenen einzelnen Kopiervorgängen großer Dateien. Um die These zu untermauern, dass die Hänger eher bei großen Dateien auftreten, habe ich die Mittelwertfunktion über 20 wiederholte Downloads der 30-GB-Datei laufen lassen:

Measure-CommandAvg {s5cmd --endpoint-url https://storage.googleapis.com cp -p=1000000 s3://doit-speed-test-bucket/temp_30GB_file .}
### With 20 sample downloads
# NVMe:
# Avg: 3m3.770s, 167.17 MB/s
# Min: 1m34.901s, 323.70 MB/s
# Max: 10m34.575s, 48.41 MB/s
# SCSI:
# Avg: 2m20.131s, 219.22 MB/s
# Min: 1m31.585s, 335.43 MB/s
# Max: 3m43.215s, 137.63 MB/s

Die Windows-Download-Laufzeit auf NVMe schwankt zwischen 1m37s und 10m35s, während bei 50K kleinen Dateien auf demselben Betriebssystem die Download-Zeit nur zwischen 2m35s und 2m44s lag. Es scheint also ein Windows- oder GCS-spezifisches Problem mit der Übertragung großer Dateien auf einer Windows-VM zu geben.

Bemerkenswert: Die durchschnittliche NVMe-Download-Zeit ist rund 73 % länger (3m3s vs. 1m46s) als beim Ausführen von s5cmd unter Linux.

Auf Basis der obigen Ergebnisse könnte man meinen, SCSI sei für Massendownloads kleiner Dateien vorteilhafter als NVMe. Da der Durchschnitt aber durch zufällige Hänger im Download-Prozess verzerrt wird, bleibe ich bei NVMe als bevorzugtem Treiber – wegen seiner nachweislich besseren Performance beim Hochladen vieler kleiner Dateien (siehe unten) und seiner vergleichbar starken Performance beim Herunterladen großer Dateien (siehe oben).

Windows-VM-Performance mit gsutil: Kleine Dateien

Hier die Werte für das Hochladen vieler kleiner Dateien von einer Windows-VM:

Measure-CommandAvg {gsutil -q -m cp -r parts gs://doit-speed-test-bucket/smallparts/}
# NVMe:
# Avg: 16m36.562s, 30.83 MB/s
# Min: 16m22.914s, 31.25 MB/s
# Max: 17m0.299s, 30.11 MB/s
# SCSI:
# Avg: 17m29.591s, 29.27 MB/s
# Min: 17m5.236s, 29.96 MB/s
# Max: 18m3.469s, 28.35 MB/s

NVMe schlägt SCSI – und auch hier sind die Geschwindigkeiten deutlich niedriger als auf einer Linux-Maschine. Unter Linux dauern Massen-Uploads kleiner Dateien rund 9m7s; die durchschnittliche Windows-NVMe-Upload-Zeit von 16m36s ist also etwa 82 % langsamer als unter Linux.

Reproduzierbarkeit der Benchmarks

Falls Sie eigene Benchmarks zur Reproduktion meiner Ergebnisse durchführen möchten, finden Sie unten die von mir verwendeten Shell- und PowerShell-Skripte samt Kommentaren mit den von mir gemessenen Durchsatzwerten:

Linux-Benchmarking-Skript

Windows-Benchmarking-Skript

Effective Transfer Tool Use Conclusions

Alles in allem ist die Frage, wie man Daten zwischen GCE-VMs – mit Daten auf einer lokalen SSD – und GCS am besten überträgt, weitaus komplexer, als sie sein müsste.

Die Performance-Unterschiede zwischen den verschiedenen GCE-Betriebssystemen und GCS hängen irgendwie alle zusammen, ich weiß es einfach

Windows Server zeigen aus bislang ungeklärten Gründen drastisch reduzierte Download- und Upload-Geschwindigkeiten im Vergleich zu den jeweils besten Befehlen auf einer Linux-Maschine. Die Performance-Einbrüche sind massiv – typischerweise 70–80 % langsamer als der jeweils beste Befehl unter Linux. Übertragungen großer Dateien sind dabei stärker betroffen als die Übertragung vieler kleiner Dateien.

Wenn Sie also TBs an Daten oder besonders große Dateien zeitkritisch von Windows nach GCS migrieren müssen, können Sie diese Performance-Probleme umgehen, indem Sie einen Disk-Snapshot erstellen, eine daraus erstellte Festplatte an eine Linux-Maschine anhängen und die Daten von dort hochladen.

Unabhängig von den Windows-Server-Problemen reicht das auf GCE-VMs verfügbare Standard-Datentransfer-Tool gsutil für Hochdurchsatz-Downloads auf keinem Betriebssystem aus. Mit s5cmd erreichen Sie hingegen eine mehrfach höhere Download-Geschwindigkeit.

Damit Sie sich im Dschungel der Tools und Argumente zurechtfinden, hier eine Zusammenfassung meiner Empfehlungen für maximalen Durchsatz – basierend auf den in diesem Artikel gezeigten Benchmarks:

Linux — Download

  • Einzelne große Datei: s5cmd --endpoint-url https://storage.googleapis.com cp s3://your_bucket/your_file .
  • Mehrere Dateien, klein oder groß: s5cmd --endpoint-url https://storage.googleapis.com cp s3://your_bucket/path* your_path/

Linux — Upload

  • Einzelne große Datei: gsutil -o GSUtil:parallel_composite_upload_threshold=150M cp your_file gs://your_bucket/
  • Mehrere Dateien, klein oder groß: gsutil -m -o GSUtil:parallel_composite_upload_threshold=150M cp -r your_path/ gs://your_bucket/

Windows Server — Download

  • Lokale SSD über NVMe statt SCSI anbinden
  • Einzelne große Datei: s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 s3://your_bucket/your_file .
  • Mehrere Dateien, klein oder groß: s5cmd --endpoint-url https://storage.googleapis.com cp s3://your_bucket/path* your_path/

Windows Server — Upload

  • Lokale SSD über NVMe statt SCSI anbinden
  • Einzelne große Datei: gsutil cp your_file gs://your_bucket/
  • Mehrere Dateien, klein oder groß: gsutil -m cp -r your_path/ gs://your_bucket/your_path/