En esta guía se explica cómo migrar de App Engine Blobstore a Cloud Storage.
Cloud Storage es similar a Blobstore de App Engine en el sentido de que puedes usar Cloud Storage para servir objetos de datos grandes (blobs), como archivos de vídeo o de imagen, y permitir que tus usuarios suban archivos de datos grandes. Aunque solo se puede acceder a Blobstore de App Engine a través de los servicios empaquetados antiguos de App Engine, Cloud Storage es un producto independiente al que se accede a través de las bibliotecas de cliente de Cloud. Google CloudCloud Storage ofrece a tu aplicación una solución de almacenamiento de objetos más moderna y te da la flexibilidad de migrar a Cloud Run u otra plataforma de alojamiento de aplicaciones más adelante. Google Cloud
En los Google Cloud proyectos creados después de noviembre del 2016, Blobstore usa segmentos de Cloud Storage en segundo plano. Esto significa que, cuando migres tu aplicación a Cloud Storage, todos los objetos y permisos que tengas en los segmentos de Cloud Storage no cambiarán. También puedes empezar a acceder a esos segmentos con las bibliotecas de cliente de Cloud para Cloud Storage.
Diferencias y similitudes clave
Cloud Storage excluye las siguientes dependencias y limitaciones de Blobstore:
- La API Blobstore para Python 2 depende de webapp.
- La API Blobstore para Python 3 usa clases de utilidad para usar controladores de Blobstore.
- En Blobstore, el número máximo de archivos que se pueden subir es 500. No hay límite en el número de objetos que puedes crear en un segmento de Cloud Storage.
Cloud Storage no admite lo siguiente:
- Clases de controlador de Blobstore
- Objetos de Blobstore
Similitudes entre Cloud Storage y Blobstore de App Engine:
- Puede leer y escribir objetos de datos de gran tamaño en un entorno de tiempo de ejecución, así como almacenar y servir objetos de datos estáticos de gran tamaño, como películas, imágenes u otro contenido estático. El límite de tamaño de los objetos de Cloud Storage es de 5 TiB.
- Permite almacenar objetos en un segmento de Cloud Storage.
- Tener un nivel gratuito.
Antes de empezar
- Debes revisar y comprender los precios y las cuotas de Cloud Storage:
- Cloud Storage es un servicio de pago que tiene sus propios precios para el almacenamiento de datos en función de la clase de almacenamiento de tus datos y la ubicación de tus segmentos.
- Las cuotas de Cloud Storage tienen algunas diferencias con las cuotas y los límites de Blobstore de App Engine, lo que puede afectar a las cuotas de solicitudes de App Engine.
- Tener una aplicación de App Engine de Python 2 o Python 3 que use Blobstore.
- En los ejemplos de esta guía se muestra una aplicación que migra a Cloud Storage con el framework Flask. Ten en cuenta que puedes usar cualquier framework web, incluido
webapp2
, al migrar a Cloud Storage.
Información general
A grandes rasgos, el proceso para migrar a Cloud Storage desde App Engine Blobstore consta de los siguientes pasos:
- Actualizar archivos de configuración
- Actualiza tu aplicación Python:
- Actualizar el framework web
- Importar e inicializar Cloud Storage
- Actualizar controladores de Blobstore
- Opcional: Actualiza tu modelo de datos si usas Cloud NDB o App Engine NDB
- Probar y desplegar una aplicación
Actualizar los archivos de configuración
Antes de modificar el código de tu aplicación para pasar de Blobstore a Cloud Storage, actualiza los archivos de configuración para usar la biblioteca de Cloud Storage.
Actualiza el archivo
app.yaml
. Sigue las instrucciones correspondientes a tu versión de Python:Python 2
En el caso de las aplicaciones Python 2:
- Elimina la sección
handlers
y las dependencias de aplicaciones web innecesarias de la secciónlibraries
. - Si usas bibliotecas de cliente de Cloud, añade las versiones más recientes de las bibliotecas
grpcio
ysetuptools
. - Añade la biblioteca
ssl
, ya que Cloud Storage la necesita.
A continuación, se muestra un ejemplo de archivo
app.yaml
con los cambios realizados: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
En las aplicaciones de Python 3, elimina todas las líneas excepto el elemento
runtime
. Por ejemplo:runtime: python310 # or another support version
El tiempo de ejecución de Python 3 instala las bibliotecas automáticamente, por lo que no es necesario especificar las bibliotecas integradas del tiempo de ejecución de Python 2 anterior. Si tu aplicación Python 3 usa otros servicios empaquetados antiguos al migrar a Cloud Storage, deja el archivo
app.yaml
tal cual.- Elimina la sección
Actualiza el archivo
requirements.txt
. Sigue las instrucciones correspondientes a tu versión de Python:Python 2
Añade las bibliotecas de cliente de Cloud para Cloud Storage a tu lista de dependencias en el archivo
requirements.txt
.google-cloud-storage
A continuación, ejecuta
pip install -t lib -r requirements.txt
para actualizar la lista de bibliotecas disponibles para tu aplicación.Python 3
Añade las bibliotecas de cliente de Cloud para Cloud Storage a tu lista de dependencias en el archivo
requirements.txt
.google-cloud-storage
App Engine instala automáticamente estas dependencias durante la implementación de la aplicación en el tiempo de ejecución de Python 3, así que elimina la carpeta
lib
si existe.En el caso de las aplicaciones de Python 2, si tu aplicación usa bibliotecas integradas o copiadas, debes especificar esas rutas en el archivo
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)
Actualizar una aplicación Python
Después de modificar los archivos de configuración, actualiza tu aplicación Python.
Actualizar tu framework web de Python 2
En el caso de las aplicaciones de Python 2 que usan el framework webapp2
, se recomienda migrar del framework webapp2
obsoleto. Consulta el
calendario de asistencia del entorno de ejecución
para ver la fecha de finalización de la asistencia de Python 2.
Puedes migrar a otro framework web, como Flask, Django o WSGI. Como Cloud Storage excluye las dependencias de webapp2
y los controladores de Blobstore no son compatibles, puedes eliminar o sustituir otras bibliotecas relacionadas con aplicaciones web.
Si decides seguir usando webapp2
, ten en cuenta que los ejemplos de esta guía usan Cloud Storage con Flask.
Si tienes previsto usar los servicios de Google Cloud además de Cloud Storage o acceder a las versiones de tiempo de ejecución más recientes, te recomendamos que actualices tu aplicación al tiempo de ejecución de Python 3. Para obtener más información, consulta la información general sobre la migración de Python 2 a Python 3.
Importar e inicializar Cloud Storage
Modifica los archivos de tu aplicación actualizando las líneas de importación e inicialización:
Elimina las instrucciones de importación de Blobstore, como las siguientes:
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
Añade las instrucciones de importación de Cloud Storage y las bibliotecas de autenticación de Google, como las siguientes:
import io from flask import (Flask, abort, redirect, render_template, request, send_file, url_for) from google.cloud import storage import google.auth
Se necesita la biblioteca de autenticación de Google para obtener el mismo ID de proyecto que se usó en Blobstore para Cloud Storage. Importa otras bibliotecas, como Cloud NBD, si procede en tu aplicación.
Crea un cliente para Cloud Storage y especifica el segmento que se usa en Blobstore. Por ejemplo:
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
En los proyectos posteriores a noviembre del 2016, Blobstore escribe en un segmento de Cloud Storage con el nombre de la URL de tu aplicación y sigue el formato
PROJECT_ID.appspot.com
. Google Cloud Usas la autenticación de Google para obtener el ID de proyecto y especificar el segmento de Cloud Storage que se usa para almacenar blobs en Blobstore.
Actualizar controladores de Blobstore
Como Cloud Storage no admite los controladores de subida y descarga de Blobstore, debes usar una combinación de funciones de Cloud Storage, el módulo de biblioteca estándar io
, tu framework web y las utilidades de Python para subir y descargar objetos (blobs) en Cloud Storage.
A continuación, se muestra cómo actualizar los controladores de Blobstore usando Flask como framework web de ejemplo:
Sustituye las clases de controlador de subida de Blobstore por una función de subida en Flask. Sigue las instrucciones correspondientes a tu versión de Python:
Python 2
Los controladores de Blobstore en Python 2 son clases
webapp2
, como se muestra en el siguiente ejemplo de Blobstore: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)
Para usar Cloud Storage, sigue estos pasos:
- Sustituye la clase de subida de la aplicación web por una función de subida de Flask.
- Sustituye el controlador de subidas y el enrutamiento por un método
POST
de Flask decorado con el enrutamiento.
Ejemplo de código actualizado:
@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)
En el ejemplo de código de Cloud Storage actualizado, la aplicación ahora identifica los artefactos de objeto por su nombre de objeto (
fname
) en lugar deblob_id
. El enrutamiento también se produce en la parte inferior del archivo de la aplicación.Para obtener el objeto subido, el método
get_uploads()
de Blobstore se sustituye por el métodorequest.files.get()
de Flask. En Flask, puedes usar el métodosecure_filename()
para obtener el nombre del archivo sin caracteres de ruta, como/
, e identificar el objeto mediantegcs_client.bucket(BUCKET).blob(fname)
para especificar el nombre del contenedor y el nombre del objeto.La llamada a Cloud Storage
upload_from_file()
realiza la subida como se muestra en el ejemplo actualizado.Python 3
La clase de controlador de subida de Blobstore para Python 3 es una clase de utilidad y requiere el uso del diccionario WSGI
environ
como parámetro de entrada, tal como se muestra en el siguiente ejemplo 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()
Para usar Cloud Storage, sustituye el método
get_uploads(request.environ)
de Blobstore por el métodorequest.files.get()
de Flask.Ejemplo de código actualizado:
@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)
En el ejemplo de código de Cloud Storage actualizado, la aplicación ahora identifica los artefactos de objeto por su nombre de objeto (
fname
) en lugar deblob_id
. El enrutamiento también se produce en la parte inferior del archivo de la aplicación.Para obtener el objeto subido, el método
get_uploads()
de Blobstore se sustituye por el métodorequest.files.get()
de Flask. En Flask, puedes usar el métodosecure_filename()
para obtener el nombre del archivo sin caracteres de ruta, como/
, e identificar el objeto mediantegcs_client.bucket(BUCKET).blob(fname)
para especificar el nombre del contenedor y el nombre del objeto.El método de Cloud Storage
upload_from_file()
realiza la subida tal como se muestra en el ejemplo actualizado.Sustituye las clases de controlador de descarga de Blobstore por una función de descarga en Flask. Sigue las instrucciones correspondientes a tu versión de Python:
Python 2
En el siguiente ejemplo de controlador de descargas se muestra el uso de la clase
BlobstoreDownloadHandler
, que usa 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)
Para usar Cloud Storage, sigue estos pasos:
- Actualiza el método
send_blob()
de Blobstore para usar el métododownload_as_bytes()
de Cloud Storage. - Cambia el enrutamiento de webapp2 a Flask.
Ejemplo de código actualizado:
@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)
En el ejemplo de código de Cloud Storage actualizado, Flask decora la ruta en la función de Flask e identifica el objeto mediante
'/view/<path:fname>'
. Cloud Storage identifica elblob
objeto por su nombre y el nombre del segmento, y usa el métododownload_as_bytes()
para descargar el objeto como bytes, en lugar de usar el métodosend_blob
de Blobstore. Si no se encuentra el artefacto, la aplicación devuelve un error HTTP404
.Python 3
Al igual que el controlador de subida, la clase de controlador de descarga de Blobstore para Python 3 es una clase de utilidad y requiere el uso del diccionario WSGI
environ
como parámetro de entrada, tal como se muestra en el siguiente ejemplo de Blobstore: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)
Para usar Cloud Storage, sustituye
send_blob(request.environ, blob_key)
de Blobstore por el métodoblob.download_as_bytes()
de Cloud Storage.Ejemplo de código actualizado:
@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)
En el ejemplo de código de Cloud Storage actualizado,
blob_key
se sustituye porfname
y Flask identifica el objeto mediante la URL'/view/<path:fname>'
. El métodogcs_client.bucket(BUCKET).blob(fname)
se usa para localizar el nombre del archivo y el nombre del contenedor. El métododownload_as_bytes()
de Cloud Storage descarga el objeto como bytes, en lugar de usar el métodosend_blob()
de Blobstore.- Actualiza el método
Si tu aplicación usa un controlador principal, sustituye la clase
MainHandler
por la funciónroot()
en Flask. Sigue las instrucciones correspondientes a tu versión de Python:Python 2
A continuación, se muestra un ejemplo de uso de la clase
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)
Para usar Cloud Storage, sigue estos pasos:
- Elimina la clase
MainHandler(BaseHandler)
, ya que Flask se encarga de las rutas. - Simplifica el código de Blobstore con Flask.
- Elimina el enrutamiento de la aplicación web al final.
Ejemplo de código actualizado:
@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 has usado Flask, no tendrás una clase
MainHandler
, pero tendrás que actualizar tu función raíz de Flask si usas Blobstore. En el siguiente ejemplo se usa la funciónblobstore.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)
Para usar Cloud Storage, sustituye la función
blobstore.create_upload_url('/upload')
por el métodourl_for()
de Flask para obtener la URL de la funciónupload()
.Ejemplo de código actualizado:
@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)
- Elimina la clase
Probar y desplegar una aplicación
El servidor de desarrollo local te permite comprobar que tu aplicación se ejecuta, pero no podrás probar Cloud Storage hasta que implementes una nueva versión, ya que todas las solicitudes de Cloud Storage deben enviarse a través de Internet a un segmento de Cloud Storage real. Consulta el artículo Probar y desplegar una aplicación para saber cómo ejecutar tu aplicación de forma local. A continuación, despliega una nueva versión para confirmar que la aplicación se muestra igual que antes.
Aplicaciones que usan App Engine NDB o Cloud NDB
Si tu aplicación usa App Engine NDB o Cloud NDB, debes actualizar el modelo de datos de Datastore para incluir propiedades relacionadas con Blobstore.
Actualizar el modelo de datos
Como Cloud Storage no admite las propiedades BlobKey
de NDB, debes modificar las líneas relacionadas con Blobstore para usar los equivalentes integrados de NDB, los frameworks web u otros.
Para actualizar tu modelo de datos, sigue estos pasos:
Busca las líneas que usan
BlobKey
en el modelo de datos, como las siguientes: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()
Reemplaza
ndb.BlobKeyProperty()
porndb.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 también vas a actualizar de App Engine NDB a Cloud NDB durante la migración, consulta la guía de migración de Cloud NDB para obtener información sobre cómo refactorizar el código NDB para usar gestores de contexto de Python.
Retrocompatibilidad del modelo de datos de Datastore
En la sección anterior, al sustituir ndb.BlobKeyProperty
por ndb.StringProperty
, la aplicación se hizo incompatible con versiones anteriores, lo que significa que no podrá procesar las entradas antiguas creadas por Blobstore. Si necesitas conservar datos antiguos, crea un campo adicional para las nuevas entradas de Cloud Storage en lugar de actualizar el campo ndb.BlobKeyProperty
y crea una función para normalizar los datos.
A partir de los ejemplos de las secciones anteriores, haz los siguientes cambios:
Crea dos campos de propiedad independientes al definir tu modelo de datos. Utilice la propiedad
file_blob
para identificar los objetos creados en Blobstore y la propiedadfile_gcs
para identificar los objetos creados en 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()
Busca las líneas que hagan referencia a las nuevas visitas, como las siguientes:
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()
Cambia tu código para que se use
file_gcs
en las entradas recientes. Por ejemplo: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
Crea una función para normalizar los datos. En el siguiente ejemplo se muestra el uso de la extracción, la transformación y la carga (ETL) para recorrer todas las visitas y se toman los datos del visitante y de la marca de tiempo para comprobar si existen
file_gcs
ofile_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]
Busca la línea que hace referencia a la función
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)
Encierra la función
fetch_visits()
entre la funciónetl_visits()
, por ejemplo:@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)
Ejemplos
- Para ver un ejemplo de cómo migrar una aplicación de Python 2 a Cloud Storage, compara el código de muestra de Blobstore para Python 2 y el código de muestra de Cloud Storage en GitHub.
- Para ver un ejemplo de cómo migrar una aplicación de Python 3 a Cloud Storage, compara el ejemplo de código de Blobstore para Python 3 y el ejemplo de código de Cloud Storage en GitHub.
Siguientes pasos
- Para ver un tutorial práctico, consulta el codelab de migración de App Engine Blobstore a Cloud Storage para Python.
- Consulta cómo almacenar y servir archivos estáticos desde Cloud Storage.
- Consulta más información en la documentación de Cloud Storage.