GCSのバケットを効率的に移行する

こんにちは。AIチームの杉山です。今日はGCPのストレージサービスであるGCSに保存してあるファイルを効率的に別のバケットに移行する方法を紹介します。

はじめに

Webサービスの生ログの一次受けやサービス運用上発生する画像や音声ファイルなどの保管場所としてGCPのGoogle Cloud Storage(以降GCS)を利用するケースは多いと思います。
GCSは利用料も安価で、またデータの保存方式としてMulti-region, Dual-region, (Single-)Regionが選択できるなど可用性の面でも使いやすいサービスです。

GCSではデータを保管するストレージのフォルダのようなものとしてバケットという単位で管理しますが、昨今のセキュリティに対する意識の高まりから、既存のバケットを別のリージョンや別のセキュリティ方式のバケットに移行するようなケースがあると思います。
しかし、一度作成したバケットは後からそのリージョンなどを変更することができないため、望んだ設定で新規に作成したバケットに、既存バケットのファイル群を移行する必要があります。

なお、バケット名はWebリソース名として使用されるのでグローバルに一意でなければならないという制約があり、移行時に同じバケット名を使うことはできません。
そのため、移行後にも同じバケット名を使いたい場合には一度仮名のバケットにファイル群を退避し、古いバケットを削除、その後別のリージョンに元の名前でバケットを作成し、退避していたバケットからそちらにファイル群を移行する、という手順になります。

どちらにしても既存のファイル群はバケットからバケットに移行する必要がありますが、前述のような用途だと大量のファイルが存在するケースが多いと思います。
今回は、そのようなケースにおいて効率的にバケットを移行する方法と、その際に調べた内容について共有します。

並列コピー

GCSを操作する方法はREST APIを始めとしていくつか提供されていますが、今回はgsutilコマンドを用いることとします。
gsutilコマンドはコマンドラインからGCSにアクセスできるPythonアプリケーションで、gsutil以降にcpやmv、lsなどの馴染みのコマンドを与えることができ、UNIXコマンドに慣れた方なら簡単に使うことができます。

バケット移行の際にはmvで移動しても良いのですがもしものことを考えてcpでコピーし、作業完了後に不要となったバケットを削除することとします。
単純にバケット以下のフォルダを全てコピーするのであれば以下のような形式になります。(-rはcpコマンドのオプションでフォルダ下を再帰的にコピー)

gsutil cp -r gs://source_bucket_name gs://target_bucket_name

ただ、これだとファイルを一つずつ順にコピーすることになり大量のファイルだとかなりの時間がかかります。そこでトップレベルのコマンドラインオプションとして-m(並列実行)を与えると、対象のファイル群を並列にコピーすることができかなりの時間短縮になります。
このとき、トップレベルオプションはcpのオプションではなくgsutilに対してのオプションとなるのでオプションを与える位置に注意してください。

gsutil -m cp -r gs://source_bucket_name gs://target_bucket_name

実際にどれだけ早くコピーすることができるのか検証してみました。

検証

環境

今回の検証では移行元のバケットは以下の構成としました。

ロケーション タイプ: Region
ロケーション: asia-northeast1 (東京)
ストレージクラス: スタンダード

バケットの中身

移行対象のファイルとして、そこまで大(容)量ではないですが以下のようなファイル群を使用しました。一つ一つは画像や音声のようなデータの想定です。

ファイル数: 約13.3k個
1ファイルあたり: 数MiB
バケットの総サイズ: 約20GiB

計測結果

今回の検証は、手元のPCからの実行と、GCPコンソール上でコマンド実行ができるCloud Shellの2パターンで行いました。Cloud Shellはデフォルトでgsutilなどのツールが入っていたり、権限周りで特別な設定をせずに使用でき便利なのでパフォーマンス面で優れていればそちらを使うケースも多いのでは、との思いから試しています。
並列数はスレッドとプロセッサの数で決まり、それぞれboto構成ファイルの parallel_thread_countparallel_process_countで変更できますが最適な値は環境によって異なるため今回はデフォルト値を使用しています。

ローカル実行

まずは手元のMacでの実行結果です。今回は以下の5パターンで検証を行いました。

  • 検証1:同一リージョンへの直列コピー
  • 検証2:同一リージョンへの並列コピー
  • 検証3:別リージョン(us-east1)リージョンへ並列コピー
  • 検証4:Multi-Region(eastasia-1)へ並列コピー
  • 検証5:Multi-Region(eastasia-1)からMulti-Region(eastasia-1)へ並列コピー

結果は以下のようになりました。(試行回数1)

検証1検証2検証3検証4検証5
実行時間(sec)569392318854
平均転送速度(MiB/s)62914181498850

まず、直列コピーに比べて並列コピーは1/10以下の時間で終了しました。公式ドキュメントでは1/5程度に短縮された実験結果が載っていましたが、今回はそれ以上の結果となりました。また別リージョンへのコピーはネットワークのオーバーヘッドがあるため並列であってもそれなりの時間がかかっています。なお別リージョンへのコピーはネットワーク転送料も発生するため事前に試算をしておくのが良いでしょう。
Single-RegionからMulti-RegionへのコピーよりもMulti-RegionからMulti-Regionへのコピーの方が早かったのは意外でしたが、Multi-Regionではオブジェクトが複数になるため、その複製に時間がかかっているのではと想像します。

Cloud Shellでの実行

次にCloud Shellでの実行結果です。結論から言うとCloud Shellでの実行はローカル実行に比べて明らかに遅かったため、上記の検証1,2の2パターンで打ち切っています。

検証1検証2
実行時間(sec)1778158
平均転送速度(MiB/s)29419

Cloud Shellは時間制限はあるものの無料で使用できるため、割り当てられているリソースが限られているのかもしれません。このあたりは先述のbotoファイルを変更することで改善できる可能性があります。

終わりに

今回は、GCS上のファイルを効率的に別のバケットに移行する方法と、その速度の比較結果について紹介しました。
機械学習系のサービスを扱う上で、運用で得られるデータを学習や分析に使うためにその取得や移行といった作業が日々発生します。今回の記事がその際の一助になれば幸いです。ありがとうございました。

参考

https://cloud.google.com/blog/ja/products/gcp/optimizing-your-cloud-storage-performance-google-cloud-performance-atlas

https://cloud.google.com/storage/docs/apis