このガイドでは、App Engine Blobstore から Cloud Storage に移行する方法を説明します。
Cloud Storage では、Cloud Storage を使用して動画や画像ファイルなどのサイズの大きいデータ オブジェクト(blob)を処理でき、ユーザーがサイズの大きいデータファイルをアップロードできます。App Engine Blobstore には App Engine のレガシー バンドル サービスからのみアクセスできますが、Cloud Storage は Cloud クライアント ライブラリを介してアクセスするスタンドアロンの Google Cloud プロダクトです。Cloud Storage は、最新のオブジェクト ストレージ ソリューションを提供し、後で Cloud Run またはプラットフォームをホストしている他の Google Cloud アプリに柔軟に移行できます。
2016 年 11 月以降に作成された Google Cloud プロジェクトの場合、Blobstore はバックグラウンドで Cloud Storage バケットを使用します。つまり、アプリを Cloud Storage に移行しても、既存のオブジェクトと既存の Cloud Storage バケットの権限はすべて変更されません。Cloud Storage 用の Cloud クライアント ライブラリを使用して、既存のバケットにアクセスすることもできます。
主な違いと類似点
Cloud Storage では、次の Blobstore の依存関係と制限が除外されます。
- Python 2 用 Blobstore API はウェブアプリに依存します。
- Python 3 用 Blobstore API では、ユーティリティ クラスを使用して Blobstore ハンドラを使用します。
- Blobstore の場合、Blobstore にアップロードできるファイルの最大数は 500 です。Cloud Storage バケットに作成できるオブジェクトの数に上限はありません。
Cloud Storage は、以下をサポートしていません。
- Blobstore ハンドラクラス
- Blobstore オブジェクト
Cloud Storage と App Engine Blobstore の類似点:
- ランタイム環境で大規模なデータ オブジェクトを読み書きでき、静的で大規模なデータ オブジェクト(映画、画像、その他の静的コンテンツなど)を保存し提供することができます。Cloud Storage のオブジェクト サイズの上限は 5 TiB です。
- Cloud Storage バケットにオブジェクトを格納できます。
- 無料枠を用意します。
始める前に
- Cloud Storage の料金と割り当てを確認、把握する必要があります。
- Cloud Storage は従量制サービスであり、データのストレージ クラスとバケットのロケーションに基づいてデータ ストレージに独自の料金が設定されています。
- Cloud Storage の割り当てには App Engine Blobstore の割り当てと上限とのいくつかの違いがあります。これにより、App Engine リクエストの割り当てに影響する場合があります。
- Blobstore を使用している Python 2 または Python 3 の App Engine アプリがすでにあります。
- このガイドの例では、Flask フレームワークを使用して Cloud Storage に移行するアプリを示しています。Cloud Storage に移行する際は、
webapp2
のままにするなど、任意のウェブ フレームワークを使用できます。
概要
App Engine Blobstore から Cloud Storage への移行プロセスは、大きく分けて次の手順が含まれます。
- 設定ファイルを更新する
- Python アプリを更新します。
- ウェブ フレームワークを更新する
- Cloud Storage をインポートして初期化する
- Blobstore ハンドラを更新する
- 省略可: Cloud NDB または App Engine NDB を使用する場合はデータモデルを更新する
- アプリのテストとデプロイ
設定ファイルを更新する
Blobstore から Cloud Storage に移行するようにアプリケーション コードを変更する前に、Cloud Storage ライブラリを使用するように構成ファイルを更新します。
app.yaml
ファイルを更新します。 Python のバージョンに応じた手順に沿って操作します。Python 2
Python 2 アプリの場合:
handlers
セクションとlibraries
セクションの不要なウェブアプリ依存関係を削除します。- Cloud クライアント ライブラリを使用する場合は、
grpcio
ライブラリとsetuptools
ライブラリの最新バージョンを追加します。 ssl
ライブラリは Cloud Storage で必要なため、追加します。
変更を含む
app.yaml
ファイルの例を次に示します。runtime: python27 threadsafe: yes api_version: 1 handlers: - url: /.* script: main.app libraries: - name: grpcio version: latest - name: setuptools version: latest - name: ssl version: latest
Python 3
Python 3 アプリの場合は、
runtime
要素以外のすべての行を削除します。例:runtime: python310 # or another support version
Python 3 ランタイムでは、ライブラリが自動的にインストールされるため、以前の Python 2 ランタイムの組み込みライブラリを指定する必要はありません。Cloud Storage への移行時に Python 3 アプリが他の以前のバンドル サービスを使用している場合は、
app.yaml
ファイルをそのままにします。requirements.txt
ファイルを更新します。 Python のバージョンに応じた手順に沿って操作します。Python 2
Cloud Storage 用の Cloud クライアント ライブラリを
requirements.txt
ファイルの依存関係のリストに追加します。google-cloud-storage
次に
pip install -t lib -r requirements.txt
を実行して、アプリで使用可能なライブラリのリストを更新します。Python 3
Cloud Storage 用の Cloud クライアント ライブラリを
requirements.txt
ファイルの依存関係のリストに追加します。google-cloud-storage
Python 3 ランタイムにアプリをデプロイするときに、これらの依存関係が自動的にインストールされるため、
lib
フォルダが存在する場合は削除します。Python 2 アプリで組み込みライブラリまたはコピーされたライブラリを使用している場合、そのパスを
appengine_config.py
ファイル内に指定する必要があります。import pkg_resources from google.appengine.ext import vendor # Set PATH to your libraries folder. PATH = 'lib' # Add libraries installed in the PATH folder. vendor.add(PATH) # Add libraries to pkg_resources working set to find the distribution. pkg_resources.working_set.add_entry(PATH)
Python アプリを更新する
構成ファイルを変更したら、Python アプリを更新します。
Python 2 ウェブ フレームワークを更新する
webapp2
フレームワークを使用する Python 2 アプリの場合、古い webapp2
フレームワークから移行することをおすすめします。Python 2 のサポート終了日については、ランタイム サポートのスケジュールをご覧ください。
Flask、Django、WSGI などの別のウェブ フレームワークに移行できます。Cloud Storage では webapp2
への依存関係が除外され、Blobstore ハンドラはサポートされていないため、他のウェブアプリ関連のライブラリを削除または置換できます。
webapp2
を引き続き使用する場合は、このガイド全体を通して例で Cloud Storage と Flask を使用します。
Cloud Storage に加えて Google Cloud サービスを使用する場合や、最新のランタイム バージョンへのアクセス権を取得する場合は、アプリを Python 3 ランタイムにアップグレードすることを検討してください。詳細については、Python 2 から Python 3 への移行の概要をご覧ください。
Cloud Storage をインポートして初期化する
インポートの行と初期化の行を更新してアプリケーション ファイルを変更します。
次のような Blobstore インポート ステートメントを削除します。
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
次のように、Cloud Storage と Google Authentication ライブラリのインポート ステートメントを追加します。
import io from flask import (Flask, abort, redirect, render_template, request, send_file, url_for) from google.cloud import storage import google.auth
Google 認証ライブラリは、Cloud Storage 用 Blobstore で使用したのと同じプロジェクト ID を取得するために必要です。アプリに適用できる場合は、Cloud NBD などの他のライブラリをインポートします。
Cloud Storage の新しいクライアントを作成し、Blobstore で使用するバケットを指定します。例:
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
2016 年 11 月以降の Google Cloud プロジェクトの場合、Blobstore はアプリの URL に合わせて名前を付けた Cloud Storage バケットに書き込み、
PROJECT_ID.appspot.com
の形式を使用します。Google 認証を使用してプロジェクト ID を取得し、Blobstore への blob の保存に使用する Cloud Storage バケットを指定します。
Blobstore ハンドラを更新する
Cloud Storage では Blobstore のアップロードとダウンロードのハンドラがサポートされていないため、io
標準ライブラリ モジュール、ウェブ フレームワーク、Python ユーティリティを使用して Cloud Storage へのオブジェクト(blob)のアップロードやダウンロードを行う必要があります。
以下では、ウェブ フレームワークの例として Flask を使用して Blobstore ハンドラを更新する方法を示します。
Blobstore のアップロード ハンドラのクラスを Flask のアップロード関数に置き換えます。Python のバージョンに応じた手順に従います。
Python 2
Python 2 の Blobstore ハンドラは、次の Blobstore の例に示すように
webapp2
クラスです。class UploadHandler(blobstore_handlers.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads() blob_id = uploads[0].key() if uploads else None store_visit(self.request.remote_addr, self.request.user_agent, blob_id) self.redirect('/', code=307) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Cloud Storage を使用するには:
- ウェブアプリのアップロードクラスを Flask のアップロード関数に置き換えます。
- アップロード ハンドラとルーティングを、ルーティングで装飾された Flask
POST
メソッドに置き換えます。
更新されたコードサンプル:
@app.route('/upload', methods=['POST']) def upload(): 'Upload blob (POST) handler' fname = None upload = request.files.get('file', None) if upload: fname = secure_filename(upload.filename) blob = gcs_client.bucket(BUCKET).blob(fname) blob.upload_from_file(upload, content_type=upload.content_type) store_visit(request.remote_addr, request.user_agent, fname) return redirect(url_for('root'), code=307)
更新された Cloud Storage コードサンプルでは、オブジェクト アーティファクトが
blob_id
ではなく、オブジェクト名(fname
)で識別されるようになりました。ルーティングはアプリケーション ファイルの下部でも行われます。アップロードされたオブジェクトを取得するために、Blobstore の
get_uploads()
メソッドは Flask のrequest.files.get()
メソッドに置き換えられます。Flask では、secure_filename()
メソッドを使用して、ファイルのパス文字(/
など)を含まない名前を取得し、gcs_client.bucket(BUCKET).blob(fname)
を使用してバケット名とオブジェクト名を指定します。更新された例に示すように、Cloud Storage の
upload_from_file()
呼び出しでアップロードを行います。Python 3
Python 3 用 Blobstore のアップロード ハンドラクラスはユーティリティ クラスです。次の Blobstore の例に示すように、入力パラメータとして WSGI
environ
辞書を使用する必要があります。class UploadHandler(blobstore.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads(request.environ) if uploads: blob_id = uploads[0].key() store_visit(request.remote_addr, request.user_agent, blob_id) return redirect('/', code=307) ... @app.route('/upload', methods=['POST']) def upload(): """Upload handler called by blobstore when a blob is uploaded in the test.""" return UploadHandler().post()
Cloud Storage を使用するには、Blobstore の
get_uploads(request.environ)
メソッドを Flask のrequest.files.get()
メソッドに置き換えます。更新されたコードサンプル:
@app.route('/upload', methods=['POST']) def upload(): 'Upload blob (POST) handler' fname = None upload = request.files.get('file', None) if upload: fname = secure_filename(upload.filename) blob = gcs_client.bucket(BUCKET).blob(fname) blob.upload_from_file(upload, content_type=upload.content_type) store_visit(request.remote_addr, request.user_agent, fname) return redirect(url_for('root'), code=307)
更新された Cloud Storage コードサンプルでは、オブジェクト アーティファクトが
blob_id
ではなく、オブジェクト名(fname
)で識別されるようになりました。ルーティングはアプリケーション ファイルの下部でも行われます。アップロードされたオブジェクトを取得するために、Blobstore の
get_uploads()
メソッドは Flask のrequest.files.get()
メソッドに置き換えられます。Flask では、secure_filename()
メソッドを使用して、ファイルのパス文字(/
など)を含まない名前を取得し、gcs_client.bucket(BUCKET).blob(fname)
を使用してバケット名とオブジェクト名を指定します。更新された例に示すように、Cloud Storage の
upload_from_file()
メソッドでアップロードを行います。Blobstore のダウンロード ハンドラクラスを Flask のダウンロード関数に置き換えます。Python のバージョンに応じた手順に従います。
Python 2
次のダウンロード ハンドラの例では、webapp2 を使用する
BlobstoreDownloadHandler
クラスを使用しています。class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Cloud Storage を使用するには:
- Cloud Storage の
download_as_bytes()
メソッドを使用するように Blobstore のsend_blob()
メソッドを更新します。 - ルーティングを webapp2 から Flask に変更します。
更新されたコードサンプル:
@app.route('/view/<path:fname>') def view(fname): 'view uploaded blob (GET) handler' blob = gcs_client.bucket(BUCKET).blob(fname) try: media = blob.download_as_bytes() except exceptions.NotFound: abort(404) return send_file(io.BytesIO(media), mimetype=blob.content_type)
更新された Cloud Storage コードサンプルでは、Flask は Flask 関数でルートを修飾し、
'/view/<path:fname>'
を使用してオブジェクトを識別します。Cloud Storage では、オブジェクト名とバケット名によってblob
オブジェクトを識別し、Blobstore からsend_blob
メソッドを使用する代わりに、download_as_bytes()
メソッドを使用してオブジェクトをバイトとしてダウンロードします。アーティファクトが見つからない場合は、HTTP404
エラーが返されます。Python 3
アップロード ハンドラと同様に、Python 3 用 Blobstore のダウンロード ハンドラクラスはユーティリティ クラスであり、次の Blobstore の例に示すように WSGI
environ
辞書を入力パラメータとして使用する必要があります。class ViewBlobHandler(blobstore.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): if not blobstore.get(blob_key): return "Photo key not found", 404 else: headers = self.send_blob(request.environ, blob_key) # Prevent Flask from setting a default content-type. # GAE sets it to a guessed type if the header is not set. headers['Content-Type'] = None return '', headers ... @app.route('/view/<blob_key>') def view_photo(blob_key): """View photo given a key.""" return ViewBlobHandler().get(blob_key)
Cloud Storage を使用するには、Blobstore の
send_blob(request.environ, blob_key)
を Cloud Storage のblob.download_as_bytes()
メソッドに置き換えます。更新されたコードサンプル:
@app.route('/view/<path:fname>') def view(fname): 'view uploaded blob (GET) handler' blob = gcs_client.bucket(BUCKET).blob(fname) try: media = blob.download_as_bytes() except exceptions.NotFound: abort(404) return send_file(io.BytesIO(media), mimetype=blob.content_type)
更新された Cloud Storage コードサンプルでは、
blob_key
がfname
に置き換えられ、Flask は'/view/<path:fname>'
URL を使用してオブジェクトを識別します。gcs_client.bucket(BUCKET).blob(fname)
メソッドを使用して、ファイル名とバケット名を検索します。Cloud Storage のdownload_as_bytes()
メソッドは、Blobstore からsend_blob()
メソッドを使用する代わりに、オブジェクトをバイトとしてダウンロードします。- Cloud Storage の
アプリでメインハンドラを使用する場合は、
MainHandler
クラスを Flask のroot()
関数に置き換えます。Python のバージョンに応じた手順に沿って操作します。Python 2
Blobstore の
MainHandler
クラスを使用した例を次に示します。class MainHandler(BaseHandler): 'main application (GET/POST) handler' def get(self): self.render_response('index.html', upload_url=blobstore.create_upload_url('/upload')) def post(self): visits = fetch_visits(10) self.render_response('index.html', visits=visits) app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Cloud Storage を使用するには:
- Flask によってルーティングが処理されるため、
MainHandler(BaseHandler)
クラスを削除します。 - Flask を使用して Blobstore コードを簡素化します。
- ウェブアプリ ルーティングを最後に削除します。
更新されたコードサンプル:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
Python 3
Flask を使用した場合、
MainHandler
クラスはありませんが、Blobstore を使用する場合は Flask ルート関数を更新する必要があります。次の例では、blobstore.create_upload_url('/upload')
関数を使用しています。@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = blobstore.create_upload_url('/upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
Cloud Storage を使用するには、
blobstore.create_upload_url('/upload')
関数を Flask のurl_for()
メソッドに置き換えて、upload()
関数の URL を取得します。更新されたコードサンプル:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') # Updated to use url_for else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
- Flask によってルーティングが処理されるため、
アプリのテストとデプロイ
ローカル開発用サーバーでは、アプリの実行をテストできますが、新しいバージョンをデプロイするまで Cloud Storage をテストできません。これは、すべての Cloud Storage リクエストをインターネット経由で実際の Cloud Storage バケットに送信する必要があるためです。アプリケーションをローカルで実行する方法については、アプリケーションのテストとデプロイをご覧ください。次に、新しいバージョンをデプロイして、アプリが以前と同じように表示されることを確認します。
App Engine NDB または Cloud NDB を使用するアプリ
アプリで App Engine NDB または Cloud NDB を使用して Blobstore 関連のプロパティを含める場合は、Datastore データモデルを更新する必要があります。
データモデルを更新する
NDB の BlobKey
プロパティは Cloud Storage ではサポートされていないため、NDB やウェブ フレームワークなどの組み込みサービスを使用するように Blobstore 関連の行を変更する必要があります。
データモデルを更新するには:
次のように、データモデルで
BlobKey
を使用する行を見つけます。class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.BlobKeyProperty()
ndb.BlobKeyProperty()
をndb.StringProperty()
に置き換えます。class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.StringProperty() # Modified from ndb.BlobKeyProperty()
移行中に App Engine NDB から Cloud NDB にアップグレードする場合も、Python コンテキスト マネージャーを使用するように NDB コードをリファクタリングする方法については、Cloud NDB 移行ガイドをご覧ください。
Datastore データモデルの下位互換性
前のセクションの ndb.BlobKeyProperty
を ndb.StringProperty
に置き換えると、アプリの下位互換性がなくなります。つまり、Blobstore によって作成された古いエントリを処理できなくなります。古いデータを保持する必要がある場合は、ndb.BlobKeyProperty
フィールドを更新するのではなく、新しい Cloud Storage エントリ用に追加のフィールドを作成し、データを正規化する関数を作成します。
前のセクションの例から、次の変更を行います。
データモデルを定義するときに、2 つの個別のプロパティ フィールドを作成します。Blobstore によって作成されたオブジェクトを識別するには
file_blob
プロパティを使用し、Cloud Storage によって作成されたオブジェクトを識別するにはfile_gcs
プロパティを使用します。class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.BlobKeyProperty() # backwards-compatibility file_gcs = ndb.StringProperty()
次のように、新しいアクセスを参照している行を見つけます。
def store_visit(remote_addr, user_agent, upload_key): 'create new Visit entity in Datastore' with ds_client.context(): Visit(visitor='{}: {}'.format(remote_addr, user_agent), file_blob=upload_key).put()
最近のエントリで
file_gcs
が使用されるようにコードを変更します。例:def store_visit(remote_addr, user_agent, upload_key): 'create new Visit entity in Datastore' with ds_client.context(): Visit(visitor='{}: {}'.format(remote_addr, user_agent), file_gcs=upload_key).put() # change file_blob to file_gcs for new requests
データを正規化する新しい関数を作成します。次の例では、抽出、変換、低(ETL)を使用してすべてのアクセスをループし、訪問者とタイムスタンプのデータを取得して、
file_gcs
またはfile_gcs
が存在しているかどうかを確認します。def etl_visits(visits): return [{ 'visitor': v.visitor, 'timestamp': v.timestamp, 'file_blob': v.file_gcs if hasattr(v, 'file_gcs') \ and v.file_gcs else v.file_blob } for v in visits]
fetch_visits()
関数を参照する行を探します。@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
次のように、
fetch_visits()
をetl_visits()
関数内でラップします。@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = etl_visits(fetch_visits(10)) # etl_visits wraps around fetch_visits return render_template('index.html', **context)
例
- Python 2 アプリを Cloud Storage に移行する方法の例を確認するには、GitHub で Python 2 用 Blobstore のコードサンプルと Cloud Storage のコードサンプルを比較してください。
- Python 3 アプリを Cloud Storage に移行する方法の例を確認するには、GitHub で Python 3 用 Blobstore のコードサンプルと Cloud Storage のコードサンプルを比較してください。
次のステップ
- ハンズオン チュートリアルについては、App Engine Blobstore から Python 用 Cloud Storage Codelab に移行するをご覧ください。
- Cloud Storage から静的ファイルを保存して処理する方法を学習する。
- 詳細については、Cloud Storage のドキュメントをご覧ください。