Ce guide explique comment migrer d'App Engine Blobstore vers Cloud Storage.
Cloud Storage est semblable au service Blobstore d'App Engine, dans la mesure où vous pouvez l'utiliser pour diffuser des objets de données volumineux (blobs), tels que des fichiers vidéo ou image, et pour permettre à vos utilisateurs d'importer des fichiers de données volumineux. Bien qu'App Engine Blobstore soit accessible uniquement via les anciens services groupés App Engine, Cloud Storage est un produit Google Cloud autonome, accessible via les bibliothèques clientes Cloud. Cloud Storage propose une application de stockage d'objets plus moderne et vous donne la possibilité de migrer vers Cloud Run ou une autre plate-forme d'hébergement d'applications Google Cloud par la suite.
Pour les projets Google Cloud créés après novembre 2016, Blobstore utilise des buckets Cloud Storage en arrière-plan. Cela signifie que lorsque vous migrez votre application vers Cloud Storage, tous vos objets et autorisations existants dans ces buckets Cloud Storage existants restent inchangés. Vous pouvez également commencer à accéder à ces buckets existants à l'aide des bibliothèques clientes Cloud pour Cloud Storage.
Principales différences et similitudes
Cloud Storage exclut les dépendances et limitations suivantes du service Blobstore :
- L'API Blobstore pour Python 2 dépend d'une application Web.
- L'API Blobstore pour Python 3 utilise des classes utilitaires pour faire appel à des gestionnaires Blobstore.
- Pour Blobstore, le nombre maximal de fichiers pouvant être importés dans le Blobstore est de 500. Vous pouvez créer autant d'objets que vous le souhaitez dans un bucket Cloud Storage.
Cloud Storage n'est pas compatible avec :
- Classes du gestionnaire Blobstore
- Objets Blobstore
Similarités de Cloud Storage et Blobstore d'App Engine :
- Peut lire et écrire de grands objets de données dans un environnement d'exécution, ainsi que stocker et diffuser de grands objets de données statiques, tels que des films, des images ou d'autres contenus statiques. La taille maximale de l'objet pour Cloud Storage est de 5 Tio.
- Vous permet de stocker des objets dans un bucket Cloud Storage.
- Dispose d'une version gratuite.
Avant de commencer
- Vous devez examiner et comprendre les tarifs et les quotas de Cloud Storage :
- Cloud Storage est un service payant. Il propose ses propres tarifs pour le stockage de données, en fonction de la classe de stockage des données et de l'emplacement de vos buckets.
- Les quotas Cloud Storage présentent certaines différences avec les quotas et limites Blobstore d'App Engine, ce qui peut avoir un impact sur vos quotas de requêtes App Engine.
- Vérifiez que vous disposez d'une application App Engine Python 2 ou Python 3 utilisant Blobstore.
- Les exemples de ce guide illustrent une application qui migre vers Cloud Storage à l'aide du framework Flask. Notez que vous pouvez utiliser n'importe quel framework Web, y compris en restant sur
webapp2
, lors de la migration vers Cloud Storage.
Présentation
De manière générale, le processus de migration vers Cloud Storage à partir de Blobstore App Engine comprend les étapes suivantes :
- Mettre à jour les fichiers de configuration
- Mettre à jour votre application Python :
- Mettre à jour votre framework Web
- Importer et initialiser Cloud Storage
- Mettre à jour les gestionnaires Blobstore
- Facultatif : Mettre à jour votre modèle de données si vous utilisez Cloud NDB ou App Engine NDB
- Tester et déployer votre application
Mettre à jour les fichiers de configuration
Avant de modifier le code de votre application pour passer de Blobstore à Cloud Storage, mettez à jour vos fichiers de configuration pour utiliser la bibliothèque Cloud Storage.
Mettez à jour le fichier
app.yaml
. Suivez les instructions correspondant à votre version de Python :Python 2
Pour les applications Python 2 :
- Supprimez la section
handlers
et toutes les dépendances d'applications Web inutiles dans la sectionlibraries
. - Si vous utilisez les bibliothèques clientes Cloud, ajoutez les dernières versions des bibliothèques
grpcio
etsetuptools
. - Ajoutez la bibliothèque
ssl
, car elle est requise par Cloud Storage.
Voici un exemple de fichier
app.yaml
dans lequel les modifications ont été apportées :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
Pour les applications Python 3, supprimez toutes les lignes, à l'exception de l'élément
runtime
. Exemple :runtime: python310 # or another support version
L'environnement d'exécution Python 3 installe automatiquement les bibliothèques. Vous n'avez donc pas besoin de spécifier des bibliothèques intégrées à partir de l'environnement d'exécution Python 2 précédent. Si votre application Python 3 utilise d'autres anciens services groupés lors de la migration vers Cloud Storage, laissez le fichier
app.yaml
tel quel.- Supprimez la section
Mettez à jour le fichier
requirements.txt
. Suivez les instructions correspondant à votre version de Python :Python 2
Ajoutez les bibliothèques clientes cloud pour Cloud Storage à votre liste de dépendances dans le fichier
requirements.txt
.google-cloud-storage
Exécutez ensuite
pip install -t lib -r requirements.txt
pour mettre à jour la liste des bibliothèques disponibles pour votre application.Python 3
Ajoutez les bibliothèques clientes cloud pour Cloud Storage à votre liste de dépendances dans le fichier
requirements.txt
.google-cloud-storage
App Engine installe automatiquement ces dépendances dans l'environnement d'exécution Python 3 lors du déploiement de l'application. Par conséquent, supprimez le dossier
lib
s'il existe.Pour les applications Python 2, si votre application utilise des bibliothèques intégrées ou copiées, vous devez spécifier ces chemins d'accès dans le fichier
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)
Mettre à jour votre application Python
Après avoir modifié vos fichiers de configuration, mettez à jour votre application Python.
Mettre à jour votre framework Web Python 2
Pour les applications Python 2 qui utilisent le framework webapp2
, il est recommandé de migrer le framework obsolète webapp2
. Pour connaître la date de fin de l'assistance Python 2, consultez le calendrier de compatibilité des environnements d'exécution.
Vous pouvez migrer vers un autre framework Web tel que Flask, Django ou WSGI. Étant donné que Cloud Storage exclut les dépendances sur webapp2
et que les gestionnaires Blobstore ne sont pas compatibles, vous pouvez supprimer ou remplacer d'autres bibliothèques liées à l'appplication Web.
Si vous choisissez de continuer à utiliser webapp2
, notez que les exemples de ce guide utilisent Cloud Storage avec Flask.
Si vous prévoyez d'utiliser les services Google Cloud en plus de Cloud Storage ou pour accéder aux dernières versions d'exécution, vous devez envisager de mettre à niveau votre application vers l'environnement d'exécution Python 3. Pour en savoir plus, consultez la Présentation de la migration de Python 2 vers Python 3.
Importer et initialiser Cloud Storage
Modifiez les fichiers de votre application en mettant à jour les lignes d'importation et d'initialisation :
Supprimez les instructions d'importation Blobstore, comme suit :
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
Ajoutez les instructions d'importation pour Cloud Storage et les bibliothèques d'authentification Google, comme suit :
import io from flask import (Flask, abort, redirect, render_template, request, send_file, url_for) from google.cloud import storage import google.auth
La bibliothèque d'authentification Google est nécessaire pour obtenir le même ID de projet que celui utilisé dans Blobstore pour Cloud Storage. Importez d'autres bibliothèques telles que Cloud NBD, le cas échéant.
Créez un client pour Cloud Storage et spécifiez le bucket utilisé dans Blobstore. Exemple :
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
Pour les projets Google Cloud après novembre 2016, Blobstore écrit dans un bucket Cloud Storage nommé d'après l'URL de votre application et suit le format
PROJECT_ID.appspot.com
. Vous utilisez l'authentification Google pour obtenir l'ID du projet afin de spécifier le bucket Cloud Storage utilisé pour stocker des blobs dans Blobstore.
Mettre à jour les gestionnaires Blobstore
Comme Cloud Storage n'est pas compatible avec les gestionnaires d'importation et de téléchargement de Blobstore, vous devez utiliser une combinaison de fonctionnalités Cloud Storage, de module de bibliothèque standard io
, de framework Web et d'utilitaires Python pour importer et télécharger des objets (blobs) dans Cloud Storage.
Voici comment mettre à jour les gestionnaires Blobstore en utilisant Flask comme exemple de framework Web :
Remplacez vos classes de gestionnaire d'importation Blobstore par une fonction d'importation dans Flask. Suivez les instructions correspondant à votre version de Python :
Python 2
Les gestionnaires Blobstore dans Python 2 sont des classes
webapp2
, comme illustré dans l'exemple Blobstore suivant :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)
Pour utiliser Cloud Storage :
- Remplacez la classe d'importation de l'application Web par une fonction d'importation Flask.
- Remplacez le gestionnaire d'importation et le routage par une méthode Flask
POST
décorée avec du routage.
Exemple de code mis à jour :
@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)
Dans l'exemple de code Cloud Storage mis à jour, l'application identifie désormais les artefacts d'objet par son nom d'objet (
fname
) au lieu deblob_id
. Le routage s'effectue également au bas du fichier d'application.Pour obtenir l'objet importé, la méthode
get_uploads()
de Blobstore est remplacée par la méthoderequest.files.get()
de Flask. Dans Flask, vous pouvez utiliser la méthodesecure_filename()
pour obtenir un nom sans caractère de chemin d'accès, tel que/
pour le fichier, et identifier l'objet à l'aide degcs_client.bucket(BUCKET).blob(fname)
pour spécifier le nom du bucket et le nom de l'objet.L'appel
upload_from_file()
de Cloud Storage effectue l'importation, comme indiqué dans l'exemple mis à jour.Python 3
La classe de gestionnaire d'importation de Blobstore pour Python 3 est une classe utilitaire et nécessite d'utiliser le dictionnaire WSGI
environ
comme paramètre d'entrée, comme indiqué dans l'exemple suivant de Blobstore :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()
Pour utiliser Cloud Storage, remplacez la méthode
get_uploads(request.environ)
de Blobstore par la méthoderequest.files.get()
de Flask.Exemple de code mis à jour :
@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)
Dans l'exemple de code Cloud Storage mis à jour, l'application identifie désormais les artefacts d'objet par son nom d'objet (
fname
) au lieu deblob_id
. Le routage s'effectue également au bas du fichier d'application.Pour obtenir l'objet importé, la méthode
get_uploads()
de Blobstore est remplacée par la méthoderequest.files.get()
de Flask. Dans Flask, vous pouvez utiliser la méthodesecure_filename()
pour obtenir un nom sans caractère de chemin d'accès, tel que/
pour le fichier, et identifier l'objet à l'aide degcs_client.bucket(BUCKET).blob(fname)
pour spécifier le nom du bucket et le nom de l'objet.La méthode Cloud Storage
upload_from_file()
effectue l'importation, comme illustré dans l'exemple mis à jour.Remplacez vos classes du gestionnaire de téléchargement Blobstore par une fonction de téléchargement dans Flask. Suivez les instructions correspondant à votre version de Python :
Python 2
L'exemple suivant du gestionnaire de téléchargement illustre l'utilisation de la classe
BlobstoreDownloadHandler
, qui utilise webapp2 :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)
Pour utiliser Cloud Storage :
- Mettez à jour la méthode
send_blob()
de Blobstore pour utiliser la méthodedownload_as_bytes()
de Cloud Storage. - Remplacez le routage de webapp2 par Flask.
Exemple de code mis à jour :
@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)
Dans l'exemple de code Cloud Storage mis à jour, Flask décore l'itinéraire dans la fonction Flask et identifie l'objet à l'aide de
'/view/<path:fname>'
. Cloud Storage identifie l'objetblob
par son nom et le nom du bucket, et utilise la méthodedownload_as_bytes()
pour télécharger l'objet en tant qu'octets, plutôt que d'utiliser la méthodesend_blob
de Blobstore. Si l'artefact n'est pas trouvé, l'application renvoie une erreur HTTP404
.Python 3
À l'instar du gestionnaire d'importation, la classe du gestionnaire de téléchargement de Blobstore pour Python 3 est une classe utilitaire et nécessite d'utiliser le dictionnaire WSGI
environ
comme paramètre d'entrée, comme illustré dans l'exemple Blobstore suivant :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)
Pour utiliser Cloud Storage, remplacez la méthode
send_blob(request.environ, blob_key)
de Blobstore par la méthodeblob.download_as_bytes()
de Cloud Storage.Exemple de code mis à jour :
@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)
Dans l'exemple de code Cloud Storage mis à jour,
blob_key
est remplacé parfname
, et Flask identifie l'objet à l'aide de l'URL'/view/<path:fname>'
. La méthodegcs_client.bucket(BUCKET).blob(fname)
permet de localiser le nom du fichier et le nom du bucket. La méthodedownload_as_bytes()
de Cloud Storage télécharge l'objet en tant qu'octets, au lieu d'utiliser la méthodesend_blob()
de Blobstore.- Mettez à jour la méthode
Si votre application utilise un gestionnaire principal, remplacez la classe
MainHandler
par la fonctionroot()
dans Flask. Suivez les instructions correspondant à votre version de Python :Python 2
Voici un exemple d'utilisation de la classe
MainHandler
de Blobstore :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)
Pour utiliser Cloud Storage :
- Supprimez la classe
MainHandler(BaseHandler)
, car Flask gère le routage à votre place. - Simplifiez le code Blobstore avec Flask.
- Supprimez le routage de la webapp à la fin.
Exemple de code mis à jour :
@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
Si vous avez utilisé Flask, vous n'aurez pas de classe
MainHandler
, mais votre fonction racine Flask doit être mise à jour si blobstore est utilisé. L'exemple suivant utilise la fonctionblobstore.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)
Pour utiliser Cloud Storage, remplacez la fonction
blobstore.create_upload_url('/upload')
par la méthodeurl_for()
de Flask afin d'obtenir l'URL de la fonctionupload()
.Exemple de code mis à jour :
@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)
- Supprimez la classe
Tester et déployer votre application
Le serveur de développement local vous permet de vérifier que votre application s'exécute, mais vous ne pouvez pas tester Cloud Storage tant que vous n'avez pas déployé une nouvelle version, car toutes les requêtes Cloud Storage doivent être envoyées par Internet à un bucket Cloud Storage. Pour savoir comment exécuter votre application en local, consultez la page Tester et déployer votre application. Déployez ensuite une nouvelle version pour vérifier que l'application est identique à celle d'avant.
Applications utilisant App Engine NDB ou Cloud NDB
Vous devez mettre à jour votre modèle de données Datastore si votre application utilise App Engine NDB ou Cloud NDB pour inclure des propriétés liées au Blobstore.
Mettre à jour votre modèle de données
Comme les propriétés BlobKey
de NDB ne sont pas acceptées par Cloud Storage, vous devez modifier les lignes liées au Blobstore pour utiliser des équivalents intégrés à partir de NDB, des frameworks Web ou d'ailleurs.
Pour mettre à jour votre modèle de données, procédez comme suit :
Recherchez les lignes qui utilisent
BlobKey
dans le modèle de données, comme suit :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()
Remplacez
ndb.BlobKeyProperty()
parndb.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()
Si vous effectuez également une mise à niveau d'App Engine NDB vers Cloud NDB pendant la migration, consultez le guide de migration de Cloud NDB pour savoir comment refactoriser le code NDB afin d'utiliser les gestionnaires de contexte Python.
Rétrocompatibilité du modèle de données Datastore
Dans la section précédente, le remplacement de ndb.BlobKeyProperty
par ndb.StringProperty
a rendu l'application rétrocompatible, ce qui signifie qu'elle ne pourra pas traiter les entrées plus anciennes créées par Blobstore. Si vous devez conserver d'anciennes données, créez un champ supplémentaire pour les nouvelles entrées Cloud Storage au lieu de mettre à jour le champ ndb.BlobKeyProperty
, puis créez une fonction de normalisation des données.
Dans les exemples des sections précédentes, apportez les modifications suivantes :
Créez deux champs de propriété distincts lors de la définition de votre modèle de données. Utilisez la propriété
file_blob
pour identifier les objets créés par Blobstore et la propriétéfile_gcs
pour identifier les objets créés par Cloud Storage :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()
Recherchez les lignes faisant référence à de nouvelles visites, comme suit :
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()
Modifiez votre code afin que
file_gcs
soit utilisé pour les entrées récentes. Exemple :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
Créez une fonction pour normaliser les données. L'exemple suivant illustre l'utilisation de l'ETL (extract, transform, load) pour parcourir toutes les visites, et prend les données de visiteur et d'horodatage pour vérifier si
file_gcs
oufile_gcs
existe :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]
Recherchez la ligne faisant référence à la fonction
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)
Encapsulez
fetch_visits()
dans la fonctionetl_visits()
, par exemple :@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)
Exemples
- Pour consulter un exemple de migration d'une application Python 2 vers Cloud Storage, comparez l'exemple de code Blobstore pour Python 2 et l'exemple de code Cloud Storage dans GitHub.
- Pour consulter un exemple de migration d'une application Python 3 vers Cloud Storage, comparez l'exemple de code Blobstore pour Python 3 et l'exemple de code Cloud Storage dans GitHub.
Étapes suivantes
- Pour accéder à un tutoriel pratique, consultez l'atelier de programmation Migrer d'App Engine Blobstore vers l'atelier de programmation Cloud Storage pour Python.
- Découvrez comment stocker et traiter des fichiers statiques à partir de Cloud Storage.
- Pour en savoir plus, consultez la documentation Cloud Storage.