Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Compute EngineとCloud Storage間のデータ転送を最適化

By Matthew PorterSep 21, 202119 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

GCE VMとCloud Storageバケット間の転送速度に悩んでいませんか?本記事では、LinuxとWindowsのVMでアップロード/ダウンロードのスループットを最大化する方法を紹介します。

概要

先日、DoiTのお客様から、Google Compute Engine(以下GCE)上のWindows Serverのローカル SSDにあるデータを、Cloud Storageバケットへアップロードする速度が想定よりはるかに遅い、というご相談をいただきました。当初は、GCPのドキュメントが推奨する gsutil の最適な引数の効果を示すための、ごく簡単なベンチマークで済むだろうと考えていました。ところが、初期検証の結果が予想外かつ非常に異例だったため、この「ちょっとした調査」は、GCEとGCS間のデータ転送パフォーマンスに関する本格的な調査へと発展しました。

LinuxまたはWindowsマシンでGCEとGCSの間でデータを移動する最適な方法だけを知りたい方は、本記事末尾の「転送ツール活用の結論」までスクロールしてください。

一方、よく使われるコマンドや引数で得られる、奇妙で直感に反することも多いスループットの数値について一緒に頭を悩ませてみたい方は、最終的に複雑な推奨事項に至るまでの詳細を、ぜひ最後までお読みください。

Linux VMでのgsutilパフォーマンス:大容量ファイル

お客様のご相談はWindows Server上でのデータ転送に関するものでしたが、まずは私が最も慣れている環境で基本的なベンチマークを実施しました。

具体的には、GCEのパブリックイメージ「Debian GNU/Linux 10 (buster)」を用いたLinuxです。

お客様はすでにローカルSSDからのファイル転送を試みており、ネットワークディスクが転送速度に影響する可能性を最小限に抑えたかったため、n2-standard-4とn2-standard-80の2種類のVMサイズを構成し、それぞれにベンチマーク用のローカルSSDを1つずつアタッチしました。

使用するGCSバケット、ならびに本記事に登場するすべてのVMは、us-central1リージョンのリソースとして作成しています。

お客様の大容量ファイルアップロード環境を再現するため、サイズ30 GBの空ファイルを作成しました:

fallocate -l 30G temp_30GB_file

ここから、よく推奨される2つのgsutilパラメータを検証しました:

  • -m:並列・マルチスレッドコピーを実行します。多数のファイルを並列に転送する際に有効で、単一ファイルのアップロードには向きません。
  • -o GSUtil:parallel_composite_upload_threshold=150M:指定したしきい値を超える大容量ファイルを分割し、各パートを並列にアップロードした後、すべての完了後に結合します。

両VMにおけるローカルSSDの推定最大パフォーマンスは以下のとおりです:

ローカルSSDのRead/Writeスループット上限

したがって、gsutil では最大で読み取り660 MB/s、書き込み350 MB/sのスループットが得られるはずです。アップロードベンチマークの結果を見てみましょう:

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

GCPのgsutilドキュメントの記載どおり、大容量ファイルのアップロードでは -o GSUtil を含めると効果を発揮します。ファイルパートの並列アップロードに使えるvCPU数が増えるほどアップロード時間は劇的に短縮され、n2-standard-80では一貫して600 MB/sのアップロード速度を実現し、SSDの最大スループット660 MB/sに迫る結果となりました。1ファイルのみの場合に -m を加えると、アップロード時間は数秒短縮されます。ここまでは特に異常な結果はありません。

続いて、ダウンロードのベンチマークを見てみましょう:

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

ちょっと待った

80 vCPUのVMでも、ダウンロード性能はローカルSSDの最大書き込みスループットのわずか23%しか達成していません。さらに、-m でマルチスレッドを有効にしても単一ファイルのダウンロードでは性能が改善せず、両マシンとも最大スループット(n2-standard-4で10 Gbps、n2-standard-80で32 Gbps)を大きく下回って動作しているにもかかわらず、同じファミリー内で上位のマシンを使うとダウンロード速度が約30%向上するという、奇妙な結果になっています。とはいえ、法外に高価なVMでローカルSSDの書き込みスループットの1/4しか出ないことに比べれば、まだましかもしれません。

一体何が起きているのでしょうか?

この問題についてあちこち調べ回りましたが答えは見つからず、代わりに s5cmd というツールを発見しました。S3バケットへのアップロード/ダウンロードを劇的に高速化することを目的に設計されたツールで、コンパイル言語のGoで書かれているため、Pythonで書かれたAWS CLIの同等コマンド(例:aws s3 cp)と比べて12倍高速に動作するとされています。実は gsutil もPythonで書かれています。もしかすると、gsutilは言語の選択によって性能が大きく制限されているのか、あるいは単に最適化が不十分なのかもしれません。GCSバケットはS3 APIの相互運用性を有効化できるので、コンパイル言語で書かれたツールに切り替えるだけで、s5cmd によりアップロード/ダウンロードを高速化できるのでしょうか?

Linux VMでのs5cmdパフォーマンス:大容量ファイル

s5cmd を動作させるまでには少々苦労しました。主な理由は、GCSの相互運用性がS3のマルチパートアップロードAPIをサポートしていないことを身をもって学ぶ必要があったからです。このツールはAWSのみを念頭に書かれているため、GCPでは大容量ファイルのアップロードに失敗します。マルチパートアップロードを回避させる引数 -p=1000000 を指定する必要があります。詳しくは s5cmd のIssue #1 および #2 をご覧ください。

また、s5cmd には並列転送するパート/ファイル数を指定する -c パラメータもあり、デフォルト値は5です。

これら2つの引数を踏まえ、以下のLinuxアップロードベンチマークを実施しました:

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

予想どおり、マルチパートアップロード戦略が選択肢にないため、大容量ファイルのアップロードは gsutil と比べて大幅に遅くなりました。gsutil の200〜600 MB/sに対し、こちらは75〜85 MB/sです。並列度をデフォルトの5から1にしても、性能改善はわずかでした。つまり、s5cmd はAWSをファーストクラスとして扱い、GCPを考慮していないため、s5cmd でアップロードを高速化することはできません。

続いて、s5cmd のダウンロードベンチマークです:

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

劇的な改善です。ダウンロード時間にはばらつきがあるものの、-c-p を指定せずデフォルトのままにすると最適な速度が得られるようです。最大書き込みスループット350 MB/sには届きませんが、n2-standard-4で約289 MB/sという数値は、同じマシンでの gsutil の約64 MB/sと比べてはるかに近い値です。データ転送ツールを差し替えるだけで、ダウンロード速度が4.5倍向上するということです。

以上のLinuxにおける検証結果をまとめると:

  • GCS利用時に s5cmd ではマルチパートアップロードを有効化できないため、GCSへのアップロードでは -o GSUtil:parallel_composite_upload_threshold=150M を指定したうえで gsutil を使い続けるのが妥当です。
  • デフォルトパラメータの s5cmd は、ダウンロード性能で gsutil を圧倒します。コンパイル言語で書かれた転送ツールを使うだけで、劇的(4.5倍)な性能向上が得られます。

Windows VMでのgsutilパフォーマンス:大容量ファイル

ここまでで十分異常だと思われたかもしれませんが、Windowsの世界ではさらに深い闇が待っています。なにしろお客様のご相談はWindows Serverに関するものだったので、いよいよこのOSでベンチマークに着手します。問題はキーボードと椅子の間(つまりユーザー側)にはないだろう、という疑念が次第に強くなっていきました。

Linuxでは、適切な引数を与えれば gsutil がアップロードに優れ、デフォルトパラメータの s5cmd がダウンロードに優れることが確認できました。次はこれらのコマンドをWindowsで試す番です。ここでもまた、PowerShellの経験不足を痛感することになりました。

最終的に、GCE VMイメージ「Windows Server version 1809 Datacenter Core for Containers, built on 20200813」を実行する、ローカルSSDをアタッチしたn2-standard-4マシンでベンチマークを取得できました。Windows ServerにはvCPUあたりのライセンス費用がかかるため、本実験ではn2-standard-80でのメトリクス取得は見送りました。

メトリクスに進む前に、重要な補足があります:

ローカルSSDのアタッチに関するGCPドキュメントでは、「すべてのWindows Server」でローカルSSDをアタッチする際は、Linuxマシンで通常使用するNVMeドライバーではなく、SCSIドライバーを使うことを推奨しています。SCSIの方が最大スループット性能を引き出すよう最適化されているためです。私はNVMeアタッチとSCSIアタッチの2台のVMをローカルSSD付きでプロビジョニングし、これまで検証してきた各種ツールやパラメータと組み合わせて両者の性能を比較することにしました。

以下はアップロード速度のベンチマークです:

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

今の気持ちを言葉にできない

gsutil に引数を指定しない場合、アップロードスループットはLinuxマシンで達成された値の約60%です。引数をどう組み合わせても性能は低下します。Linuxではマルチパートアップロードを有効にすると42%の速度改善が得られたにもかかわらず、Windowsではアップロード速度が35%低下するのです。さらに、-m を指定せず gsutil が単一の大容量ファイルを最適にアップロードできるようにした場合、SCSIの方がWindows Server向けに最適化されたドライバーを持つとされているにもかかわらず、NVMeドライブからのアップロードがSCSIドライブよりも速く完了します。一体何が起きているのでしょうか?!

80〜85 MB/s前後の低いアップロード性能は、まさにDoiTのお客様が直面していた範囲そのものでしたので、少なくとも問題は再現できました。GCPが大容量ファイルアップロードに推奨している引数

-o GSUtil:parallel_composite_upload_threshold=150M を外せば、お客様は35%の性能ペナルティを取り除けるということになります。🤷

ダウンロードのベンチマークはさらに痛ましい結果となりました:

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

以下の問題のため、ダウンロードベンチマークでは安定した結果が得られませんでした:

  • 各ダウンロード操作が、開始までに最大2分間ハングする
  • ダウンロードが始まると約68〜70 MB/sで進行するが、その後…
  • 不確定な時間、再び一時停止することがある

ハングと再開が繰り返され、同じVMの同じディスクであってもダウンロード速度の平均が23 MB/sから58 MB/sの範囲で揺れました。このランダムで長時間のハングアップに振り回される中で、NVMeとSCSIのどちらがダウンロードに最適かを判断するのは至難の業でした。この点については後ほど詳しく述べます。

Windows VMでのs5cmdパフォーマンス:大容量ファイル

gsutil ダウンロードの常軌を逸した挙動にうんざりした私は、すぐに s5cmd へと移りました。これならハングアップを解消、または影響を軽減できるかもしれません。

まずは s5cmd のアップロードベンチマークから:

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

Linuxでの s5cmd アップロードと同様、マルチパートアップロードが利用できないことが足かせとなっています。並列度を1にした場合のアップロード性能はLinuxマシンでの同ツールの結果と同程度ですが、並列度をデフォルトの5にすると性能が劇的に低下し、振れ幅も大きくなります。並列度がここまで深刻な影響を及ぼすのは異例ですが、いずれにしても s5cmd のアップロード性能は gsutil より明らかに劣るため(両者ともマルチパートアップロード未使用なのに、というのは妙な話ですが)、アップロードに s5cmd を使う必要はありません。s5cmd のアップロード並列度の謎は、ここでは無視することにします。

続いて s5cmd のダウンロードベンチマークです:

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

gsutil と同様、ダウンロードにはハングアップとばらつきがあるものの、ここでも s5cmdgsutil より大幅に高性能でした。さらに、ハングアップの時間や頻度も低かったのです。とはいえ、奇妙なハングアップは依然として時折発生する問題として残ります。

Linux VMでは -c-p を省略することで最高性能が得られたのに対し、Windowsでは -c=1 -p=1000000 として両方を指定する方が最適なようです。ベンチマークを悩ませるランダムなハングアップがあるため、これが最適な構成だと断言するのは難しいですが、これらの引数で十分実用的に動作します。gsutil の場合と同じく、ハングアップのせいでNVMeとSCSIのどちらが最適かを判定するのも困難です。

NVMeとSCSIで最適な s5cmd 引数を使ったときのダウンロード速度をより正確に把握するため、20回のダウンロードを繰り返してその平均、最小、最大の実行時間を返す関数を書きました。瞬間的なハングアップを平均でならすのが狙いです:

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

同じダウンロード処理の所要時間にはばらつきが残るものの、Windows VMのローカルSSDではSCSIドライバーが理想とされているにもかかわらず、大容量ファイルのダウンロードにおいてSCSIがNVMeより優位とは言えないことが明らかになりました。

同じ平均化関数を使い、20回のアップロードを繰り返して、アップロードもNVMeの方が高性能かどうかを検証してみましょう:

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

先ほどの個別実行で示唆されたとおり、アップロードではNVMeがSCSIより高性能であることが裏付けられました。20回繰り返した本ケースでは、NVMeの方が大幅に高速です。

つまりWindows VMでは、GCPドキュメントとは逆に、GCSへのアップロード時には gsutil-o GSUtil:parallel_composite_upload_threshold=150M を使うべきではなく、ローカルSSDのドライバーもSCSIではなくNVMeを選ぶべきだということです。アップロード、そしておそらくダウンロードも改善されます。また、ダウンロード/アップロードのいずれにおいても、1〜2分から最大10〜30分にも及ぶ予測不能な一時停止が頻繁に発生することも確認されました。

お客様には何と伝えたか…

この時点で、お客様にはWindows VMの利用にはデータ転送上の本質的な制約があるものの、以下の方法で部分的に緩和できることをお伝えしました:

  • GCPドキュメントの推奨とは逆に、gsutil cp による大容量ファイルアップロードではオプション引数をデフォルトのままにする
  • ダウンロードでは gsutil ではなく s5cmd -c=1 -p=1000000 を使う
  • GCPドキュメントの推奨とは逆に、ローカルSSDのドライバーにはSCSIではなくNVMeを使い、アップロード(およびおそらくダウンロード)速度を改善する

あわせて、Windows由来のハングアップを丸ごと回避すれば、アップロードもダウンロードも劇的に改善することもお伝えしました。具体的には、ディスクスナップショット経由でデータをLinuxマシンに移し、Linuxにアタッチしたディスクから GCSへデータ同期処理を実行する方法です。これが最終的に、GCE VMとGCS間で期待どおりのスループットを得るための最も迅速な解決策となりました。お客様にはご満足いただけたものの、Windows Serverでの不可解な性能問題には依然として歯がゆさが残ったようです。

この経験から私が得た教訓は次のとおりです。gsutil はWindows Server上の操作にまったく最適化されていないだけでなく、GCSがWindowsとデータをやり取りする能力にも根本的な問題があるように見えます。gsutils5cmd のいずれにおいても、ダウンロード/アップロードの両方で遅延やハングアップが発生するからです。

お客様の課題は解決しました…が、私の好奇心はまだ満たされていませんでした。少数の大容量ファイルではなく、多数の小容量ファイルを転送する場合には、どんな帯域の罠が待っているのでしょうか?

Linux VMでのgsutilパフォーマンス:小容量ファイル

Linuxに戻り、30 GBの大容量ファイルを5万個(正確には50,001個)に分割しました:

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

そして gsutil でアップロード性能を測定しました:

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

予想どおり、-m を指定してマルチスレッド・並列アップロードを有効にすると、アップロード速度は劇的に改善します。大量のファイルが入ったフォルダーを -m なしでアップロードしようとしてはいけません。マシンのvCPU数が多いほど、同時に実行できるファイルアップロード数も増えます。

以下は 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

こちらでも -m は必須です。大量のファイルが入ったフォルダーを -m なしでダウンロードしようとしてはいけません。アップロードと同様、-m による並列ファイル転送と豊富なvCPUによってgsutilの性能は向上します。

Linux上で gsutil を使った大量小容量ファイルのアップロード/ダウンロードに関しては、特に異常な点は見られませんでした。

Linux VMでのs5cmdパフォーマンス:小容量ファイル

すでに s5cmd をGCSへのファイルアップロードに使うべきではないと結論づけたので、ここではLinuxのダウンロードベンチマークだけを報告します:

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

n2-standard-4マシンでは、gsutilと比べて大量小容量ファイルのダウンロード速度が6.9倍に高速化しました。多数の小容量ファイルのダウンロードにも、大容量ファイルのダウンロードにも s5cmd を使うのが妥当だと言えます。

Linux上で s5cmd による大量小容量ファイルのダウンロードに関しても、特に異常な点は見られませんでした。

Windows VMでのs5cmdパフォーマンス:小容量ファイル(および追加の大容量ファイルテスト)

OSを問わず s5cmdgsutil より大幅にダウンロードが高速なので、Windowsでの小容量ファイルのダウンロードベンチマークも 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

Windows VMでは、5万個の小容量ファイルのダウンロードの方が、大容量ファイルのダウンロードよりも性能が高く、より予測しやすいことがわかります。NVMeはSCSIをわずかに上回る程度です。

このユースケースでは、先ほど見た個別の大容量ファイルコピーコマンドと比べ、データ同期に関して奇妙なほど一貫性があり、長時間のハングアップもありません。ハングアップ傾向が大容量ファイルでより発生しやすいことを確実に検証するため、平均化関数を使って30 GBファイルのダウンロードを20回繰り返してみました:

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

NVMeでのWindowsダウンロード時間は1分37秒から10分35秒の範囲で振れる一方、同じOSで5万個の小容量ファイルをダウンロードした場合は2分35秒から2分44秒の範囲に収まっています。したがって、Windows VMでの大容量ファイル転送には、Windows側またはGCS側に固有の問題があるようです。

また、NVMeでのダウンロード時間の平均は、Linuxで s5cmd を実行した場合(3分3秒対1分46秒)よりも約73%長くなっています。

上記の結果からは、SCSIの方が大量小容量ファイルのダウンロードでNVMeより有利と思いたくなりますが、ランダムなハングアップが平均値を歪めていることを踏まえると、私はNVMeを推奨ドライバーとして堅持します。後述のとおり、大量小容量ファイルのアップロードでの優位性が確認されており、大容量ファイルのダウンロードでも先ほど見たとおり同等の性能を発揮しているからです。

Windows VMでのgsutilパフォーマンス:小容量ファイル

以下は、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がSCSIを上回り、Linuxマシンよりも速度が大幅に遅いことが引き続き確認できます。Linuxでは大量小容量ファイルのアップロードに約9分7秒しかかからなかったのに対し、WindowsのNVMeでの平均アップロード時間16分36秒は、Linuxより約82%遅い結果となっています。

ベンチマークの再現性

ご自身でベンチマークを再現してみたい方のために、私が使用したシェルスクリプトおよびPowerShellスクリプトと、観測したスループットをまとめたコメントを以下に掲載します:

Linux ベンチマークスクリプト

Windows ベンチマークスクリプト

転送ツール活用の結論

総じて、ローカルSSDにデータが置かれたGCE VMとGCSの間でのデータ転送に最適な方法を判断するには、本来あるべき以上に複雑な事情が絡んでいます。

GCEのさまざまなOSとGCS間の性能差は、何らかの形ですべて関連しているはずです。私はそう確信しています

Windows Serverでは、Linuxマシンでの最適な同等コマンドと比べて、ダウンロードもアップロードも劇的に速度が低下します。理由は今のところ不明です。性能低下は深刻で、Linuxで実行する最適なコマンドより通常70〜80%遅くなります。大容量ファイルの転送は、多数の小容量ファイルの転送よりも大きく影響を受けます。

したがって、TB級のデータや特に大容量のファイルを、時間的制約のある中でWindowsからGCSへ移行する必要がある場合は、ディスクスナップショットを取得し、そのスナップショットから作成したディスクをLinuxマシンにアタッチして、当該OS経由でデータをアップロードすることで、これらの性能問題を回避することをおすすめします。

Windows Server固有の問題とは別に、GCE VM上で標準提供されるデータ転送ツール gsutil は、どのOSにおいても高スループットなダウンロードには力不足です。s5cmd を使えば、ダウンロード速度を数倍に高めることができます。

無数のツールと引数の選択肢の中から最適解を見つけやすくするために、本記事のベンチマークに基づくスループット最大化の推奨事項を以下にまとめます:

Linux — ダウンロード

  • 単一の大容量ファイル:s5cmd --endpoint-url https://storage.googleapis.com cp s3://your_bucket/your_file .
  • 複数ファイル(小・大いずれも):s5cmd --endpoint-url https://storage.googleapis.com cp s3://your_bucket/path* your_path/

Linux — アップロード

  • 単一の大容量ファイル:gsutil -o GSUtil:parallel_composite_upload_threshold=150M cp your_file gs://your_bucket/
  • 複数ファイル(小・大いずれも):gsutil -m -o GSUtil:parallel_composite_upload_threshold=150M cp -r your_path/ gs://your_bucket/

Windows Server — ダウンロード

  • ローカルSSDの接続にはSCSIではなくNVMeを使用する
  • 単一の大容量ファイル:s5cmd --endpoint-url https://storage.googleapis.com cp -c=1 -p=1000000 s3://your_bucket/your_file .
  • 複数ファイル(小・大いずれも):s5cmd --endpoint-url https://storage.googleapis.com cp s3://your_bucket/path* your_path/

Windows Server — アップロード

  • ローカルSSDの接続にはSCSIではなくNVMeを使用する
  • 単一の大容量ファイル:gsutil cp your_file gs://your_bucket/
  • 複数ファイル(小・大いずれも):gsutil -m cp -r your_path/ gs://your_bucket/your_path/