top pageへ.

Programming Room.

Google Cloud Tips.

GCS Tips.

 Google Cloud Storage(GCS)の利用に関するTipsです。


おしながき

無料のバケットを有効にする.2016/10/27

動機.

 GAEと同様にGCSにも無料枠があるような話を聞いたのに、公式ドキュメントを探してもGCP丸ごと60日間試用のリンクしか見つからない。GCEとかはまだ使う気がないので、これは取っておきたいのですよ。
 でもある日、コンソールから見つけてしまいました。GCSだけの、無料枠の設定手順の忘備録です。


概要.

 GCSの無料枠は、下記のようなものです。

手順.

 この無料のバケットは初期状態では無効になっています。有効化する手順は以下のとおりです。

  1. GCPコンソールのメニューから「App Engine」を選択。
  2. ウィンドウ左端のメニューから「設定」を選択。
  3. ウィンドウ右側ペインの「アプリケーションの設定」タブの中段「デフォルトの Cloud Storage バケット」下の「作成」をクリック。

 上記手順3.の後、「作成」ボタンがプロジェクト名のリンクに変わったら完了です。このリンクからはGCSのブラウザに遷移できます。
 デフォルトバケットの名称はGAEプロジェクトの名称と同じで固定のようです。GCSのバケット名には「ワールドワイドでユニーク」というルールがありますが、これならかぶらず安心ですね。

 もし手順3.で「作成」ボタンがなく、「GAEプロジェクト名.appspot.com」というリンクが表示されていたら、すでにデフォルトバケットはできています。このリンクでデフォルトバケットを表示できます。


補足.

 実際にデフォルトバケットを作ってみると、上記のデフォルトバケットの他に、「staging.」が先頭に追加されたバケットも生成されます。公式ドキュメントの注意書きでは、テスト用に使っていいそうです。容量も5GBとデフォルトバケットと同じ。
 ただし、毎週内容は消去されるそうです。だからテストなどテンポラリ用途に限定です。しかし、毎週といっても、週のいつでしょうね。説明がありません。またデフォルトバケットと違ってGCSのコンソールからアップロード/ダウンロードができないので、それなりにAPIを駆使したアプリを用意できてからでないと、使えません。まさにアプリのテスト専用のようです。

 なんで設定がGAEの中にあるんだろうと思ったのですが、理由を推測してみると以下のとおりです。
 過去GAEには整形されていないバイナリデータを保存し読み出せる「Blobstore」という機能がありました。このころはまだDatastoreもGAEの一部だったかもしれません。このバイナリデータの保存の機能を1つのサービスとして独立させたのがGCSです。GCSが出来てからバイナリの保存はこっちへという流れになりますが、DatastoreのBlobの機能をなくすと稼働中のアプリがお亡くなりになってしまいます。該当するアプリがそれなりに多かったので、APIだけ残して中身はGCSへという解決を行ったのではないかと思います。
 現在もBlobstore APIは機能しますが、公式ドキュメントには「BlobstoreよりGCSを使え」との注意書きがあります。Blobstore APIとは別に、GCSに直接アクセスするAPIも用意されています。バイナリの保存に関してはBlobstore APIを使うより、GCSを直接アクセスしたほうがよいでしょう。


オブジェクトのアップロード.2019/11/13

 「ブラウザからファイルをアップロードして、GCSにオブジェクトとして保存」を、Programmaticallyに行う方法の説明です。ブラウザ上で動作させるWebアプリを作るうえで、十分参考になると期待しています。

 GCPでは複数の方法が用意されていますので、実用性があると思われる方法を紹介しています。またいろいろとオプションがあるので、基本的なアップロードに限定して紹介しています。これだけでも準備や基礎知識がもろもろ必要になるので、読み応えあると思います。

 長いので、詳細は別ページにしました。


オブジェクトのダウンロード.2019/11/18

 前章のアップロードの逆で、「GCSに保存してあるオブジェクトを、ブラウザからダウンロード」を、Programmaticallyに行う方法の説明です。アップロードされたオブジェクトはそれを利用する手段が必要になります。最も想像しやすい利用ケースは、そのままダウンロードすることです。

 ダウンロードもアップロード同様に、GCPで複数の手段が用意されていますので、実用性のありそうな手段を紹介します。それぞれの手段には様々なオプションが用意されていますが、ここでは基本的なダウンロードに限定します。
 準備などはアップロードと共通になる部分もあります。

 これも長いので、詳細は別ページにしてあります。


Cloud Pub/Sub Notifications for Cloud Storageの使い方.2019/12/13

 アプリケーションのユーザがオブジェクトに何らかの変更を加えとき、例えばオブジェクトのアップロード(生成)/削除などですが、その変更内容を通知してくれる機能が用意されています。この通知を契機にアプリケーションに実装した機能を起動することができます。GCPでは下記の2種類が用意されています。


 「オブジェクト変更通知(Object Notification)」は、Cloud Pub/Subが登場する前からのGCSの古い機能ですが、今でもそのまま残されています。バケットに対して変更通知の登録を行い、バケット内のオブジェクトが変更されたら、登録した任意のURIに変更内容がPush(HttpでPOST)される機能です。GCSの設定のみで使用できます。

 Cloud Pub/Subが登場してから、それ経由で通知を行う「Cloud Pub/Sub Notifications for Cloud Storage」が実装されています。Cloud Pub/SubはPushだけでなくPullにも対応するなど、柔軟なメッセージング機構ですので、それを使っていこうじゃないかという仕組みです。Pushで使えば、「オブジェクト変更通知(Object Notification)」と同等の機能になります。Pullは、通知をCloud Pub/Subのキューに溜めておき、アプリケーション側のタイミングで読み出したい場合に使います。またランタイムのコストがオブジェクト変更通知より安価だそうです。

 ここでは新しい「Cloud Pub/Sub Notifications for Cloud Storage」を使用したオブジェクト変更の通知の実装方法について説明します。
 長いので、詳細は別ページです。


2021/05/01オブジェクトの保存でOutOfMemoryError発生とその回避.

動機.

 GAEでオブジェクトの保存を繰り返しているとOutOfMemoryErrorが発生するケースがありました。最初は1000回の保存に対して数回だけで、機能的にも必須のものではなかったので、放置していました。OutOfMemoryErrorが発生するときは非常に処理が重く(GCを繰り返している?)て、インスタンス死亡までも時間がかかります。インスタンスの死亡とともに、死亡したインスタンスが処理していた他のリクエストも巻き添えで停止します。


 発生していたOutOfMemoryErrorのログは、以下のようなものです。発生個所はCloud StorageのJavaライブラリの中ですが、OutOfMemoryErrorの性質上、それ以前に原因がある可能性も高い...

com.google.apphosting.runtime.jetty9.JettyLogger warn: Error for 発生したURI (JettyLogger.java:29)
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3236)
at com.google.cloud.BaseWriteChannel.write(BaseWriteChannel.java:135)
...

 しかし気付くといつの間にか、10回程度の書き込みで発生するくらいに頻発するように。パフォーマンス低下が無視できなくなっていました。


調査.

Jacksonの使い方.

 OutOfMemoryErrorでググってみると、Jacksonの使い方で発生することがあるらしいとのこと。


 GitHubのJacksonのドキュメントを見ても、確かに「ObjectMapperは使い回せ」と書かれていました。はじめてObjectMapperを使った時に使いまわしていいものかどうか悩んだ記憶がありますが、それ以降ろくに調べもせずに毎回必要な時にnewしてました。

 で、これを対処してみたものの、あまり変わらず。「ちょっと改善したかもしれないなー」程度です。


調査用プロジェクトでお試し.

 作成中のGAEアプリは巨大になってきたので、どこから影響を受けているのか、判りにくくなっています。このような時に効果があるのが、怪しい箇所だけを抜き出した小さいアプリを作って、原因を絞り込んでいく方法です。しかしやってみるとJacksonを使わなくても、繰り返し回数次第で発生することが判明。Jackson、お前を疑ってごめんよ。


解決.

 何かヒントはないかと、Cloud Storageのドキュメント「オブジェクトのアップロード」を見ると、見慣れないメソッドが。APIリファレンスを見るとバケットにオブジェクトを保存するには、以下のメソッドがあることがわかります。

クラス メソッド 概要
Storage create ダイレクトアップロードで新しいオブジェクトを作成します。同名のオブジェクトが既に存在する場合、上書きされます。
サイズの大きいオブジェクトの場合は下記のwriteメソッドが推奨されています。
writer オブジェクトを作成します。ストリーミングアップロードも可能です。
Resumable uploadも使用できます。

 最初オブジェクトの保存を実装した時は、オブジェクトのサイズによって使い分けるものと解釈し、サイズによらず使えそうなStorage.write()メソッドを使用していました。


 で、これをStorage.create()に替えてみると、見事に解決。10000回以上繰り返してもOutOfMemoryErrorは発生しません。


考察.

 Storage.write()メソッドはResumable uploadを使用したい時だけ使えばよいようです。予想ですが、Resumable uploadのためにメモリを消費する実装になっているのではないかと思われます。実際ログを仕込んで空きメモリを表示してみると、Storage.write()メソッドの使用前後でメモリの空きが大きく減ります。GAEのインスタンスは利用できるメモリが少ないので(基本F1使ってます)、他でのメモリ消費が増えたことが、メモリ消費の大きいStorage.write()メソッドに影響を与えたようです。

 GAEインスタンスのメモリは複数のリクエストで共有するので、Storage.write()メソッドの使用は注意が必要かもしれません。


▲page top.
Copyright 2005-2024, yosshie.