Colaboratory環境でGoogle Cloud Storage(GCS)と連携する(gsutil,gcsfuse)
本記事では、gsutilとgcsfuseを利用したColaboratory環境とGoogle Cloud Storage(GCS)との連携について見ていきます。
【目次】
[1]概要
Google Cloud Storageは、公式ドキュメント(https://cloud.google.com/storage/docs?hl=ja)で以下のように説明されています。
- Cloud Storage は、Google Cloud でオブジェクトを保存するためのサービスです。 オブジェクトとは、任意の形式のファイルで構成される不変のデータのことです。オブジェクトをバケットと呼ばれるコンテナに保存します。すべてのバケットがプロジェクトに関連付けられ、組織のプロジェクトをグループ化できます。
これは技術的側面からの説明ですが、大雑把に言えば、Google Cloud Platform(GCP)が提供するデータ保管サービスの一つです。利用方法によっては、OSのファイルのように扱うこともできます。
本記事では、Google Cloud Storageについての説明は行いませんが、記事『Colaboratory+GoogleドライブでVision APIの実験環境を作る』の流れから、Colaboratory環境からGoogle Cloud Storageを利用する方法を見ていきます。また、利用例などはVision APIの利用に関するものを中心としています。
以下は、Google Cloud StorageをGCSと略して書きます。
(1)Vision APIに関連するGCSの利用シナリオ例
Vision APIはローカルにあるファイルを送信して処理することもできますが、GCS上の画像やファイルを直接処理することができます。
(PythonクライアントライブラリからGCS上の画像やファイルを指定する方法については、『Vision API Pythonクライアントライブラリを少し深堀りする(BatchAnnotateImages編)』や『Vision API Pythonクライアントライブラリを少し深堀りする(BatchAnnotateFiles編)』のリクエストデータの表現方法を参考にしてください。)
GCSを利用するシナリオとして以下のようなことが考えられます。
- クラウドアプリなど、そもそも元の画像やファイルがGCS上にある。
- 処理したい画像やファイルが大量にある場合、GCS上にアップロードして非同期API(AsyncBatchAnnotateImages、AsyncBatchAnnotateFiles)で一括処理したほうがよい場合がある。これらのAPIは、処理結果もGCS上に保存される。
- サイズ上限( JSONリクエストの場合は10 MB)を超える画像やファイルを処理したい場合は、GCSにアップロードして処理する必要がある。
また、Vision APIに限りませんが、Googleのサンプルは、GCS上で一般公開されているものが多いです。これらをローカルやGoogleドライブにダウンロードして内容を確認したい場合もあるかもしれません。
(2)GCSとの連携方法
ColaboratoryとGCSを連携する方法としては、ざっくり
- APIを利用する(クライアントライブラリなどを利用してプログラムから利用する)
- gsutil、gcsfuseなどのツールを利用する
があります。
本記事では、GCSのAPIを利用せず、Colaboratory環境からGoogleドライブを利用するのと同じような感覚で、gsutilとgcsfuseというツールを使って簡単にGCSを利用する方法を考えます。
gsutilとgcsfuseの概要は以下の通りです。
- gsutil
- コマンドラインツールです。
- Colaboratory環境には予めインストールされています。
- GCSに対する殆ど(全て?)のタスクを実行できます。
- GCS上にある一般公開データのダウンロードには特にお勧めのツールです。
- Cloud Storage FUSE(gcsfuse)
- Googleドライブをマウントして利用するのと同じように、GCSのバケットをマウントして、仮想マシン内のOSファイルとして扱う方法です。
- ファイル操作はOSのコマンドで行うため、新しいコマンドを覚える必要はありませんが、メタデータが無視されるなど、GCSの全ての機能を利用できるわけではありません。
- GCS上のファイルが通常のOSのファイルとして見えるため、プログラムから利用する場合は、GCS用の特別なコードを書く必要がありません。
一長一短があり、どちらが良いというものではなく、利用シナリオに応じて使い分けるものかと思います。
(3)利用料金について
ツール(gsutilとgcsfuse)は無料で利用できますが、GCSの利用には利用料金を考慮する必要があります。
料金の詳細についてはこちらを参照してください。
- Cloud Storage の料金(https://cloud.google.com/storage/pricing?hl=ja)
[2]gsutil
gsutilは、Google Cloud Storage(以下、GCS)にアクセスするコマンドラインツールです。
gsutilは、Google Cloud SDKに含まれているため、Cloud SDKをインストールすることで利用できます。(単独でインストールすることもできるようです。)
なお、Colaboratory環境では仮想マシンに予めインストールされているため、インストール作業は不要です。
gsutilは、とても多機能で、殆ど(全て?)のGCSの機能を利用することができます。しかも、コマンドの体系はLinux系OSのファイル操作に類似したコマンド構成となっているため、単純なファイル操作程度であれば、直観的に扱えます。
以下では、gsutilのls、cpに絞って利用例中心で説明します。
gsutilコマンドの詳細については、以下の公式ドキュメントを参照してください。
- クイックスタート: gsutil ツールの使用(https://cloud.google.com/storage/docs/quickstart-gsutil?hl=ja)
- gsutil ツール(https://cloud.google.com/storage/docs/gsutil?hl=ja)
なお、gsutilはPythonで実装されており、GitHubでソースコードも公開されています。
(1)Google Cloud Storage URI
gsutilを使う上で、まず理解する必要があるのは、GCS上のリソースの指定方法(Google Cloud Storage URI)です。
これは、接頭辞「gs://」に続いて、バケット名、オブジェクト名をスラッシュで区切るURIの形で指定します。
gs://[BUCKET_NAME]/[OBJECT_NAME]
例えば、Vision APIのドキュメント「画像内のテキストを検出する(https://cloud.google.com/vision/docs/ocr?hl=ja)」のサンプル画像は、「gs://cloud-samples-data/vision/ocr/sign.jpg」となっています。
この場合、[BUCKET_NAME]は「cloud-samples-data」、[OBJECT_NAME]は「vision/ocr/sign.jpg」です。
Windowsのパスに例えると、[BUCKET_NAME]はドライブ、[OBJECT_NAME]はディレクトリを含めたファイル名といった感じでしょうか。
GCSにはOSのようなディレクトリ(フォルダ)構造はありませんが、gsutilでは、スラッシュをディレクトリ区切りとして、OSのファイル操作と同じような感覚で処理してくれます。
また、ワイルドカードを利用した検索なども可能です。これらについては後述します。
(2)一般公開データのダウンロード例
一般公開されているGCS上のデータには認証情報が無くてもアクセスできますので、以下のgsutil cpコマンドを実行することで、簡単にダウンロードすることが出来ます。
例えば、「gs://bucket/samples/images/test.jpg」という公開データがある場合、これをCoraboratoryのカレントディレクトリにダウンロードするときは以下のコマンドを実行します。
!gsutil cp gs://bucket/samples/images/test.jpg .
もしGoogleドライブを「/content/drive/My Drive」にマウントしている場合、以下のコマンドを実行すると、Googleドライブのルートにダウンロードできます。
!gsutil cp gs://bucket/samples/images/test.jpg '/content/drive/My Drive'
単独のファイルだけでなく、ディレクトリレベルでの一括ダウンロードも可能です。
公開データ「gs://bucket/samples」以下の全てのディレクトリとファイルを、Coraboratoryのカレントディレクトリにダウンロードしたい場合は、以下のコマンドを実行します。
!gsutil -m cp -r gs://bucket/samples .
同様に、マウントしているGoogleドライブにダウンロードすることもできます。
!gsutil -m cp -r gs://bucket/samples '/content/drive/My Drive'
このように、コピーコマンドと同じ感覚でダウンロードすることが出来ます。(なお、-rは再帰的コピー、-mは並列化による高速化のオプションです。)
ここでは、簡単なダウンロードの例のみ書きましたが、もう少し詳しいgsutilの利用例については、以降の説明も参考にしてください。
(3)認証
一般公開されているGCS上のデータには認証情報が無くてもアクセスできますので、以下の認証処理は不要です。
しかし、そうでない場合は、GCS上のリソースにアクセス権をもつアカウントの認証が必要です。
ここでは、ユーザ認証とサービスアカウント認証の例を書きますが、利用用途に応じた方法で認証を行います。
①ユーザ認証
Colaboratory環境で認証を行う簡単な方法は、以下のコードを実行して、リソースへアクセス権を持つユーザの認証を行うことだと思います。
from google.colab import auth
auth.authenticate_user()
これを実行すると、まだ認証情報が設定されていない場合は、リンクが表示され、リンクをクリックすると、別タブにリクエストの許可を行う画面が表示されます。許可したら、コードが表示されますので、これをColaboratoryに戻って「Enter verification code」とあるテキストボックスに貼り付けて、エンターキーを押します。これで認証が完了します。
(参考)
上記のコードは、Colaboratoryの「コードスニペット」にある以下のコードと同じです。
- Open files from GCS with gsutil
- Saving data with gsutil
なお、上記のgoogle.colab.authモジュールは、GitHubでソースコードが公開されています。
コードをみると、認証処理には gcloud auth loginコマンドを使っていることが分かります。実際に、以下のコマンドを実行して認証するのと同じです。
!gcloud auth login --no-launch-browser
ただし、google.colab.authの場合は認証成功後に認証情報を消去してくれますが、gcloud auth loginを直接実行すると、入力したverification codeなどの表示が残ります。また、google.colab.authは一度認証が成功すると、認証情報が有効な間は何もせず終了します。
②サービスアカウント認証
もし、クライアントライブラリ等を使うために、GCSリソースにもアクセス権を持つサービスアカウントの秘密鍵ファイル(JSON)があれば、認証に秘密鍵ファイルを利用することができます。
!gcloud auth activate-service-account [ACCOUNT] --key-file=[KEY_FILE]
ここで、[KEY_FILE]は、サービスアカウントの秘密鍵ファイル(JSON)のパスを指定します。[ACCOUNT]は、サービスアカウントのE-mailアドレスです。(これは、JSONファイル内のclient_emailでも確認できます。)
(4)プロジェクトの設定(オプション)
必須ではありませんが、認証に続けて、以下のコードを実行してプロジェクトIDを設定しておくと、gsutilでプロジェクトIDが必要となる操作を行う際に入力が不要となります。
project_id = '[your Cloud Platform project ID]'
!gcloud config set project {project_id}
ここで、[your Cloud Platform project ID]には、GCPプロジェクトのIDを設定します。
(このコードはColaboratoryのスニペットにあるものです。Python変数として project_idを設定し、{project_id}とすることでシェルコマンドにPython変数の値を渡しています。)
プロジェクトIDを設定すると便利な場合の例を考えてみます。
例えば、以下のコマンドでプロジェクト内のバケットを一覧表示できます。
!gsutil ls -p [project ID] gs://
しかし、gcloud config set projectでデフォルトのプロジェクトIDを設定していると、-pオプションを指定しなくても、プロジェクト内のバケットを一覧表示できます。
!gsutil ls gs://
(5)URIとワイルドカード
gsutilでは、gs://で始まるGoogle Cloud Storage URIを利用してGCS上のリソースを指定しますが、このURIに、おなじみの「*」、「?」などのワイルドカードに加えて、再帰的なワイルドカード「**」も利用できます。
ワイルドカードの詳細については、以下の公式文書を参照してください。
簡単なGCSのバケット例を用いて説明します。
GCSのバケット名をbucketとして、bukect以下が以下のようなディレクトリとファイルで構成されているとします。
(+をディレクトリ、・をファイルとします。また、=>に続いて、Google Cloud Storage URIを書いています。)
bucket => gs://bucket
・ readme.txt => gs://bucket/readme.txt
+ images => gs://bucket/images
+ proj1 => gs://bucket/images/proj1
・ image1.jpg => gs://bucket/images/proj1/image1.jpg
・ image2.png => gs://bucket/images/proj1/image2.png
+ proj2 => gs://bucket/images/proj2
・ image3.jpg => gs://bucket/images/proj2/image3.jpg
・ image4.png => gs://bucket/images/proj2/image4.png
+ responses => gs://bucket/responses
・ response1.json => gs://bucket/responses/response1.json
・ response2.json => gs://bucket/responses/response2.json
ここで、おなじみの「*」を利用して、「gs://bucket/responses/*.json」とすると、resources以下のresponse1.jsonとresponse2.jsonがマッチします。
それでは、images以下にある全ての.jpgファイルにマッチさせたい場合はどうでしょうか。
*は、そのフォルダ(スラッシュで囲まれている範囲)でのマッチを試みるため、「gs://bucket/images/*.jpg」としても何もマッチしません。(imagesの下には、proj1とproj2しかないため。)
このような用途に「**」が利用できます。「gs://bucket/images/**.jpg」とすると、スラッシュを超えて、proj1/image1.jpgとproj2/image3.jpgにマッチします。
これらのワイルドカードは、gsutilのls、cpその他のコマンドで利用することができます。
(6)gsutil ls:バケットやオブジェクトの情報表示
公式リファレンスに正式な定義と、多くの利用例があります。
- ls - List providers, buckets, or objects(https://cloud.google.com/storage/docs/gsutil/commands/ls?hl=ja)
gsutil ls [オプション] GCSのURI
GCSのURIには、(5)のワイルドカードも利用できます。
オプションの例
- -l あるいは -lh
- 追加でサイズと日付が表示されます。
-lhとすると、サイズの単位がKiB、MiB、GiBで表示されます。 - -L
- Update time、Content-Type、ACLなどを含む詳細な情報が表示されます。
- -r または -R
- 再帰的にサブディレクトリに対してもリスト操作を実行します。
ただし、可能であれば、再帰的なワイルドカード(**)を利用した方が良いようです。
なお、-lや-Lで表示される日時はJSTではありませんので、ご注意ください。
以下に、(5)のバケット例をもとに、利用例を書いてみます。
①ファイルの存在確認を行う
!gsutil ls gs://bucket/images/proj1/image1.jpg
②ファイルの詳細な属性を表示する
!gsutil ls -L gs://bucket/images/proj1/image1.jpg
③ディレクトリ内のファイルを表示する(日付やサイズも表示)
!gsutil ls -lh gs://bucket/images/proj1
④imagesディレクトリ以下にある拡張子がjpgのファイルを探す
!gsutil ls -lh gs://bucket/images/**.jpg
(7)gsutil cp:ファイルやフォルダのコピー
公式リファレンスに正式な定義と、多くの利用例があります。
- cp - Copy files and objects(https://cloud.google.com/storage/docs/gsutil/commands/cp?hl=ja)
ネットワーク経由のファイルコピー(転送)はかなり奥が深い内容ですが、ここでは簡単な使い方に絞って書きます。
gsutil cp [オプション] コピー元 コピー先
コピー元がGCSのURI、コピー先がローカルファイルパスであれば、ダウンロード。
コピー元がローカルファイルパスで、コピー先がGCSのURIであればアップロードの動作となります。
GCSのURIには、(5)のワイルドカードも利用できます。
また、ローカルファイルパスに、マウントしたGoogleドライブのパスを指定することもできます。これを利用すれば、GoogleドライブとGCS間のアップロード、ダウンロードを行うことが出来ます。
オプションの例
- -r または -R
- 再帰的にサブディレクトリに対してコピー操作を実行します。
- -m
- 並列転送します。転送するファイル数が多い場合はパフォーマンスの向上が見込めます。(-m オプションはトップレベルオプションとなっているため、cpより前に書くことができます。)
以下に、(5)のバケット例をもとに、利用例を書いてみます。
①image1.jpgをカレントディレクトリにコピー(ダウンロード)する。
!gsutil cp gs://bucket/images/proj1/image1.jpg .
②imagesディレクトリの内容をカレントディレクトリにコピー(ダウンロード)する。
!gsutil -m cp -r gs://bucket/images .
※カレントディレクトリに、imagesフォルダが作成されます。
③imagesディレクトリ以下の.jpgファイルのみカレントディレクトリ以下にコピー(ダウンロード)する。
!gsutil -m cp gs://bucket/images/**.jpg .
※カレントディレクトリに、imagesフォルダは作成されず、マッチした.jpgファイルがフラットにカレントディレクトリにコピーされます。
④image5.jpgをgs://bucket/images/proj3の直下にコピー(アップロード)する。
!gsutil cp image5.jpg gs://bucket/images/proj3/
※proj3ディレクトリが作られて、その下にimage5.jpgがコピーされます。
⑤proj4ディレクトリ以下をgs://bucket/images/の直下にコピー(アップロード)する。
!gsutil -m cp -r proj4 gs://bucket/images/
[3]Cloud Storage FUSE(gcsfuse)
Cloud Storage FUSEを利用すると、Cloud Storageバケットをファイル システムとしてマウントできます。これにより、GCS上のオブジェクトをOSファイルのように扱えるようになります。
ファイル操作はOSのコマンドで行うため、gsutilのように新しいコマンドを覚える必要はありません。また、gsutilではGCS上のリソースを、gs://で始まるGoogle Cloud Storage URIで指定しますが、Cloud Storage FUSEでは、URIは利用せず、マウントしたOSのファイルパスを指定します。
プログラムから利用する場合も、通常のファイル操作で扱えるため、GCS用の特別なコードを書く必要はありません。
ただし、GCSのメタデータの扱い方やディレクトリの考え方など、GCSの全ての機能を利用できるわけではありませんし、完全にOSと同じということでもありませんので注意が必要です。
Cloud Storage FUSEの注意事項や詳細については、以下の公式ドキュメントを参照してください。
- Cloud Storage FUSE(https://cloud.google.com/storage/docs/gcs-fuse?hl=ja)
なお、gcsfuseはGoで実装されており、GitHubでソースコードも公開されています。
(1)インストール
gsutilと違って、Cloud Storage FUSEはColaboratory環境にインストールされいません。
利用するには、以下の手順でgcsfuseをインストールする必要があります。
!echo "deb http://packages.cloud.google.com/apt gcsfuse-bionic main" > /etc/apt/sources.list.d/gcsfuse.list
!curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
!apt update
!apt install gcsfuse
(2)認証
マウントしたいGCS上のリソースに対してアクセス権をもつアカウントの認証を行います。
ここでは、ユーザ認証とサービスアカウント認証の例を書きますが、利用用途にあう方法で認証を行います。
①ユーザ認証
gsutilと同様にgoogle.colab.authモジュールを利用するのが簡単だと思います。(gsutilの「①ユーザ認証」を参照してください。)
from google.colab import auth
auth.authenticate_user()
これを実行すると、まだ認証情報が設定されていない場合は、リンクが表示され、リンクをクリックすると、別タブにリクエストの許可を行う画面が表示されます。許可したら、コードが表示されますので、これをColaboratoryに戻って「Enter verification code」とあるテキストボックスに貼り付けて、エンターキーを押します。これで認証が完了します。
②サービスアカウント認証
もし、クライアントライブラリ等を使うために、GCSリソースにもアクセス権を持つサービスアカウントの秘密鍵ファイル(JSON)があれば、認証に秘密鍵ファイルを利用することができます。
gsutilの場合とは異なり、gcsfuseは、サービスアカウントの秘密鍵ファイル(JSON)を環境変数(GOOGLE_APPLICATION_CREDENTIALS)に設定していれば利用できます。
『Colaboratory+GoogleドライブでVision APIの実験環境を作る/Vision APIクライアントライブラリの準備』の例にあるような環境で、Pythonクライアントライブラリとgcsfuseを利用する場合は、以下のコードにより、どちらの認証にも利用できます。
import os
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = [KEY_FILE]'
(3)マウントして利用する
gcsfuseのインストールと認証の設定ができたら、後は任意のディレクトリにGCSのバケットをマウントするだけで、OSファイルとしてアクセスできるようになります。
マウントは以下のコマンドで行います。
gcsfuse [バケット名] [マウントしたいディレクトリのパス]
ここで、バケット名は、gs://で始まるGoogle Cloud Storage URIではなく、バケット名部分のみを指定します。
例えば、Colaboratory環境であれば、カレントディレクトリ(/content)の下に、gcsディレクトリを作成して、gs://bucketをマウントする場合は、以下のセルを実行します。
!gcsfuse bucket gcs
成功すれば、ls、cp、catなどのOSコマンドを利用してGCSのリソースにアクセスできるようになります。
例えば、マウントしたgcsディレクトリに対してlsコマンドを実行すると、マウントされたgs://bucket直下のファイルやディレクトリが一覧できるようになります。
!ls gcs
また、Googleドライブをマウントしている場合、cpコマンドを使って、GCSとGoogleドライブ間のファイルコピーも簡単にできます。
ただ、cpコマンドを使ってコピーすることは、内部的にGCSとファイルのダウンロードやアップロードが行われていることになります。ある意味では、gsutilより便利ですが、gsutilの-mオプションのような機能が使えないともいえます。gsutilのように、**を利用した検索もできません。さらに、ディレクトリの扱いの違いや、GCSのメタデータにアクセスすることができないなど、制限事項もあります。
これらが問題にならない場合、特に、プログラムから利用する場合は、GCS向けのプログラミングが不要なので、とても便利だと思います。
(4)マウントの解除
マウントを解除するにはfusermountコマンドを使います。
先にマウントした/content/gcsを解除するには以下のコマンドを実行します。
!fusermount -u /content/gcs
(5)他ツールとの共存:ディレクトリの扱いなどの問題
新規バケットをgcsfuseでマウントして利用するだけだと分かりにくい問題ですが、gsutilや、独自にGCSのクライアントライブラリを利用して作ったプログラムと共存する場合、微妙な問題があります。
例えば、gsutilでディレクトリのコピーなどを行うと、gcsfuseでマウントしたディレクトリから、コピーしたはずのディレクトリやファイルが見えない場合があります。
これはファイルが存在しないわけではなく(実際、GCPコンソールからは存在が確認できます)、ディレクトリの扱いからくる問題ですが、gcsfuseを使う立場からすると、ディレクトリやファイルにアクセスできず、結果としてコピーされていないことと同じになってしまいます。
この問題の詳細については、以下に詳しい記述があります。
このような、GCS上にはディレクトリやファイルがあるのに、gcsfuseでマウントしたディレクトリからは見えない場合の手っ取り早い解決策は、--implicit-dirsフラグを付けてマウントすることです。
!gcsfuse --implicit-dirs bucket /content/gcs
しかし、上記ドキュメントに書かれている通り、この方法にもパフォーマンスの問題などがありますので、完全な解決策ではありません。
これらを考えると、用途によってgcsfuseとgsutilを使い分ける、あるいは住み分ける必要があるものと思います。
コメント
コメントを投稿