Migra de App Engine Blobstore a Cloud Storage (módulo 16)

1. Descripción general

La serie de codelabs de Serverless Migration Station (instructivos prácticos de autoaprendizaje) y los videos relacionados tienen como objetivo ayudar a los desarrolladores de Google Cloud sin servidores a modernizar sus aplicaciones guiándolos a través de una o más migraciones, principalmente para dejar de usar servicios heredados. De esta manera, tus apps serán más portátiles y tendrás más opciones y flexibilidad, lo que te permitirá integrarte a una gama más amplia de productos de Cloud y acceder a ellos, además de actualizar con mayor facilidad a versiones de lenguaje más recientes. Si bien inicialmente se enfoca en los primeros usuarios de Cloud, principalmente desarrolladores de App Engine (entorno estándar), esta serie es lo suficientemente amplia como para incluir otras plataformas sin servidores, como Cloud Functions y Cloud Run, o en otros lugares, si corresponde.

En este codelab, aprenderás a migrar de Blobstore de App Engine a Cloud Storage. También hay migraciones implícitas de los siguientes tipos:

Consulta los módulos de migración relacionados para obtener más información paso a paso.

En un próximo lab,

  • Agrega el uso de la API o biblioteca de Blobstore de App Engine
  • Almacena los archivos que suben los usuarios al servicio de Blobstore
  • Prepárate para el siguiente paso de la migración a Cloud Storage

Requisitos

Encuesta

¿Cómo usarás este instructivo?

Leer Leer y completar los ejercicios

¿Cómo calificarías tu experiencia en Python?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud?

Principiante Intermedio Avanzado

2. Fondo

Este codelab comienza con la app de ejemplo del módulo 15 y muestra cómo migrar de Blobstore (y NDB) a Cloud Storage (y Cloud NDB). El proceso de migración implica reemplazar las dependencias de los servicios agrupados heredados de App Engine, lo que te permite mover tus apps a otra plataforma sin servidores de Cloud o a otra plataforma de hosting si lo deseas.

Esta migración requiere un poco más de esfuerzo en comparación con las otras migraciones de esta serie. Blobstore tiene dependencias en el marco de trabajo de aplicaciones web original, y es por eso que la app de ejemplo usa el framework webapp2 en lugar de Flask. En este instructivo, se presentan migraciones a Cloud Storage, Cloud NDB, Flask y Python 3.

La app aún registra las "visitas" de los usuarios finales y muestra las diez más recientes, pero el codelab anterior (módulo 15) agregó una nueva funcionalidad para adaptarse al uso de Blobstore: la app solicita a los usuarios finales que suban un artefacto (un archivo) correspondiente a su "visita". Los usuarios pueden hacerlo o seleccionar "omitir" para no participar. Independientemente de la decisión del usuario, la página siguiente renderiza el mismo resultado que las versiones anteriores de esta app, en el que se muestran las visitas más recientes. Un giro adicional es que las visitas con artefactos correspondientes incluyen un vínculo de "vista" para mostrar el artefacto de una visita. En este codelab, se implementan las migraciones mencionadas antes, a la vez que se conserva la funcionalidad descrita.

3. Configurar/trabajo previo

Antes de pasar a la parte principal del instructivo, configuremos nuestro proyecto, obtengamos el código y, luego, implementemos la app de referencia para asegurarnos de que comenzamos con un código que funciona.

1. Configura el proyecto

Si ya implementaste la app del módulo 15, te recomendamos que vuelvas a usar el mismo proyecto (y código). Como alternativa, puedes crear un proyecto nuevo o reutilizar otro existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y de que App Engine esté habilitado.

2. Obtén app de ejemplo del modelo de referencia

Uno de los requisitos previos de este codelab es tener una app de ejemplo del módulo 15 que funcione. Si no la tienes, puedes obtenerla de la carpeta "START" del módulo 15 (vínculo a continuación). En este codelab, se te guiará por cada paso y se concluirá con un código similar al que se encuentra en la carpeta "FINISH" del módulo 16.

El directorio de archivos START del módulo 15 debería verse de la siguiente manera:

$ ls
README.md       app.yaml        main-gcs.py     main.py         templates

El archivo main-gcs.py es una versión alternativa de main.py del módulo 15 que permite seleccionar un bucket de Cloud Storage diferente de la URL predeterminada asignada a una app según el ID del proyecto: PROJECT_ID.appspot.com. Este archivo no tiene ninguna función en este codelab (módulo 16), excepto que se pueden aplicar técnicas de migración similares a ese archivo si se desea.

3. (vuelve a) implementa la aplicación de modelo de referencia

Los pasos del trabajo previo restantes para ejecutar ahora sin estos:

  1. Familiarízate con la herramienta de línea de comandos de gcloud
  2. Vuelve a implementar la app de muestra con gcloud app deploy
  3. Confirma que la app se ejecuta en App Engine sin problemas

Una vez que hayas ejecutado esos pasos correctamente y confirmes que tu app del módulo 15 funciona, En la página inicial, se muestra un formulario en el que se solicita un archivo de artefacto de visita para subir, junto con una opción, un botón "Omitir", para inhabilitar la carga:

f5b5f9f19d8ae978.png

Una vez que los usuarios suben un archivo o lo omiten, la app renderiza la página conocida "Visitas más recientes":

f5ac6b98ee8a34cb.png

Las visitas que incluyan un artefacto tendrán un vínculo "ver" a la derecha de la marca de tiempo de la visita para mostrar (o descargar) el artefacto. Una vez que confirmes la funcionalidad de la app, podrás migrar de los servicios heredados de App Engine (webapp2, NDB y Blobstore) a alternativas contemporáneas (Flask, Cloud NDB y Cloud Storage).

4. Actualiza archivos de configuración

Para la versión actualizada de nuestra app, se usan tres archivos de configuración. Las tareas requeridas son las siguientes:

  1. Actualizar las bibliotecas de terceros integradas requeridas en app.yaml y dejar la puerta abierta para una migración a Python 3
  2. Agrega un requirements.txt, que especifica todas las bibliotecas requeridas que no están integradas.
  3. Agrega appengine_config.py para que la app admita bibliotecas de terceros integradas y no integradas.

app.yaml

Edita el archivo app.yaml actualizando la sección libraries. Quita jinja2 y agrega grpcio, setuptools y ssl. Elige la versión más reciente disponible para las tres bibliotecas. También agrega la directiva runtime de Python 3, pero marcada como comentario. Cuando termines, debería verse de la siguiente manera (si seleccionaste Python 3.9):

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: jinja2
  version: latest

DESPUÉS:

#runtime: python39
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

Los cambios se relacionan principalmente con las bibliotecas integradas de Python 2 disponibles en los servidores de App Engine (para que no tengas que empaquetarlas por tu cuenta). Quitamos Jinja2 porque viene con Flask, que agregaremos a reqs.txt. Siempre que se usen las bibliotecas cliente de Google Cloud, como las de Cloud NDB y Cloud Storage, se necesitarán grpcio y setuptools. Por último, Cloud Storage requiere la biblioteca ssl. La directiva de entorno de ejecución marcada como comentario en la parte superior es para cuando tengas todo listo para portar esta app a Python 3. Abordaremos este tema al final de este instructivo.

requirements.txt

Agrega un archivo requirements.txt, que requiere el framework de Flask y las bibliotecas cliente de Cloud NDB y Cloud Storage, ninguna de las cuales está integrada. Crea el archivo con este contenido:

flask
google-cloud-ndb
google-cloud-storage

El entorno de ejecución de App Engine para Python 2 requiere el autoempaquetado de bibliotecas de terceros no integradas, por lo que debes ejecutar el siguiente comando para instalar estas bibliotecas en la carpeta lib:

pip install -t lib -r requirements.txt

Si tienes Python 2 y 3 en tu máquina de desarrollo, es posible que debas usar el comando pip2 para asegurarte de obtener las versiones de Python 2 de estas bibliotecas. Una vez que actualices a Python 3, ya no necesitarás empaquetar por tu cuenta.

appengine_config.py

Agrega un archivo appengine_config.py que admita bibliotecas de terceros integradas y no integradas. Crea el archivo con este contenido:

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)

Los pasos que acabas de completar deben ser similares o idénticos a los que se indican en la sección Instala bibliotecas para apps de Python 2 de la documentación de App Engine y, más específicamente, el contenido de appengine_config.py debe coincidir con lo que se indica en el paso 5.

El trabajo en los archivos de configuración está completo, así que pasemos a la aplicación.

5. Modifica los archivos de la aplicación

Importaciones

El primer conjunto de cambios para main.py incluye intercambiar todo lo que se reemplazará. Estos son los cambios:

  1. Flask reemplaza a webapp2
  2. En lugar de usar Jinja2 de webapp2_extras, usa el Jinja2 que viene con Flask.
  3. Blobstore y NDB de App Engine se reemplazan por Cloud NDB y Cloud Storage
  4. Los controladores de Blobstore en webapp se reemplazan por una combinación del módulo de biblioteca estándar io, Flask y las utilidades de werkzeug
  5. De forma predeterminada, Blobstore escribe en un bucket de Cloud Storage con el nombre de la URL de tu app (PROJECT_ID.appspot.com). Como estamos migrando a la biblioteca cliente de Cloud Storage, se usa google.auth para obtener el ID del proyecto y especificar exactamente el mismo nombre de bucket. (Puedes cambiar el nombre del bucket, ya que no está codificado).

ANTES:

import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers

Implementa los cambios de la lista anterior reemplazando la sección de importación actual en main.py por el siguiente fragmento de código.

DESPUÉS:

import io

from flask import (Flask, abort, redirect, render_template,
        request, send_file, url_for)
from werkzeug.utils import secure_filename

import google.auth
from google.cloud import exceptions, ndb, storage

Inicialización y compatibilidad innecesaria con Jinja2

El siguiente bloque de código que se debe reemplazar es el BaseHandler que especifica el uso de Jinja2 de webapp2_extras. Esto no es necesario porque Jinja2 viene con Flask y es su motor de plantillas predeterminado, así que quítalo.

En el módulo 16, crea una instancia de los objetos que no teníamos en la app anterior. Esto incluye inicializar la app de Flask y crear clientes de API para Cloud NDB y Cloud Storage. Por último, unimos el nombre del bucket de Cloud Storage como se describió anteriormente en la sección de importaciones. Aquí se muestra el antes y el después de implementar estas actualizaciones:

ANTES:

class BaseHandler(webapp2.RequestHandler):
    'Derived request handler mixing-in Jinja2 support'
    @webapp2.cached_property
    def jinja2(self):
        return jinja2.get_jinja2(app=self.app)

    def render_response(self, _template, **context):
        self.response.write(self.jinja2.render_template(_template, **context))

DESPUÉS:

app = Flask(__name__)
ds_client = ndb.Client()
gcs_client = storage.Client()
_, PROJECT_ID = google.auth.default()
BUCKET = '%s.appspot.com' % PROJECT_ID

Actualiza el acceso a Datastore

Cloud NDB es, en su mayoría, compatible con App Engine NDB. Una diferencia que ya se mencionó es la necesidad de un cliente de API. Otra es que esta última requiere que el acceso a Datastore se controle mediante el administrador de contexto de Python del cliente de API. En esencia, esto significa que todas las llamadas de acceso a Datastore que usan la biblioteca cliente de Cloud NDB solo pueden ocurrir dentro de los bloques with de Python.

Ese es un cambio. El otro es que Cloud Storage no admite Blobstore ni sus objetos, p. ej., BlobKey, por lo que debes cambiar file_blob por ndb.StringProperty. A continuación, se muestran la clase de modelo de datos y las funciones store_visit() y fetch_visits() actualizadas que reflejan estos cambios:

ANTES:

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()

def store_visit(remote_addr, user_agent, upload_key):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent),
            file_blob=upload_key).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

DESPUÉS:

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()

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()

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return Visit.query().order(-Visit.timestamp).fetch(limit)

Aquí tienes una representación gráfica de los cambios que se realizaron hasta el momento:

a8f74ca392275822.png

Actualiza los controladores

Controlador de carga

Los controladores en webapp2 son clases, mientras que en Flask son funciones. En lugar de un método de verbo HTTP, Flask usa el verbo para decorar la función. Blobstore y sus controladores webapp se reemplazan por la funcionalidad de Cloud Storage, así como por Flask y sus utilidades:

ANTES:

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)

DESPUÉS:

@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)

Algunas notas sobre esta actualización:

  • En lugar de un blob_id, los artefactos de archivo ahora se identifican por el nombre de archivo (fname) si está presente, y None de lo contrario (el usuario optó por no subir un archivo).
  • Los controladores de Blobstore abstrajeron el proceso de carga de sus usuarios, pero Cloud Storage no lo hace, por lo que puedes ver el código recién agregado que establece el objeto blob y la ubicación (bucket) del archivo, así como la llamada que realiza la carga real. (upload_from_file()).
  • webapp2 usa una tabla de enrutamiento en la parte inferior del archivo de la aplicación, mientras que las rutas de Flask se encuentran en cada controlador decorado.
  • Ambos controladores finalizan su funcionalidad redireccionando a la página principal ( /) y conservando la solicitud POST con un código de retorno HTTP 307.

Controlador de descargas

La actualización del controlador de descargas sigue un patrón similar al del controlador de cargas, solo que hay mucho menos código para revisar. Reemplaza la funcionalidad de Blobstore y webapp por los equivalentes de Cloud Storage y Flask:

ANTES:

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)

DESPUÉS:

@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)

Notas sobre esta actualización:

  • Nuevamente, Flask decora las funciones de controlador con su ruta, mientras que webapp lo hace en una tabla de enrutamiento en la parte inferior, por lo que reconoce la sintaxis de coincidencia de patrones de este último (('/view/([^/]+)?') en comparación con la de Flask ('/view/<path:fname>').
  • Al igual que con el controlador de carga, se requiere un poco más de trabajo en el lado de Cloud Storage para la funcionalidad abstraída por los controladores de Blobstore, es decir, identificar el archivo (BLOB) en cuestión y descargar de forma explícita el objeto binario en comparación con la llamada de método send_blob() única del controlador de Blobstore.
  • En ambos casos, se devuelve un error HTTP 404 al usuario si no se encuentra un artefacto.

Controlador principal

Los cambios finales en la aplicación principal se realizan en el controlador principal. Los métodos de verbo HTTP webapp2 se reemplazan por una sola función que combina su funcionalidad. Reemplaza la clase MainHandler por la función root() y quita la tabla de enrutamiento webapp2, como se muestra a continuación:

ANTES:

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)

DESPUÉS:

@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)

En lugar de separar los métodos get() y post(), son esencialmente una sentencia if-else en root(). Además, como root() es una sola función, solo hay una llamada para renderizar la plantilla para GET y POST, mientras que no es posible en webapp2.

Aquí tienes una representación gráfica de este segundo y último conjunto de cambios en main.py:

5ec38818c32fec2.png

"Mejora" de retrocompatibilidad (opcional)

Por lo tanto, la solución que se creó anteriormente funciona a la perfección, pero solo si comienzas desde cero y no tienes archivos creados por Blobstore. Como actualizamos la app para identificar los archivos por nombre en lugar de BlobKey, la app del módulo 16 completada tal como está no podrá ver los archivos de Blobstore. En otras palabras, realizamos un cambio incompatible con versiones anteriores cuando hicimos esta migración. Ahora, presentamos una versión alternativa de main.py llamada main-migrate.py (que se encuentra en el repositorio) que intenta cerrar esta brecha.

La primera "extensión" que admite archivos creados por Blobstore es un modelo de datos que tiene un BlobKeyProperty (además de un StringProperty para los archivos creados por 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()

La propiedad file_blob se usará para identificar los archivos creados por Blobstore, mientras que file_gcs es para los archivos de Cloud Storage. Ahora, cuando crees visitas nuevas, almacena de forma explícita un valor en file_gcs en lugar de file_blob, de modo que store_visit se vea un poco diferente:

ANTES:

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()

DESPUÉS:

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()

Cuando recuperes las visitas más recientes, "normaliza" los datos antes de enviarlos a la plantilla:

ANTES:

@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)

DESPUÉS:

@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))
    return render_template('index.html', **context)

A continuación, confirma la existencia de file_blob o file_gcs (o ninguna de las dos). Si hay un archivo disponible, elige el que existe y usa ese identificador (BlobKey para los archivos creados por Blobstore o el nombre de archivo para los archivos creados por Cloud Storage). Cuando decimos "archivos creados por Cloud Storage", nos referimos a los archivos creados con la biblioteca cliente de Cloud Storage. Blobstore también escribe en Cloud Storage, pero, en este caso, serían archivos creados por Blobstore.

Ahora, lo que es más importante, ¿qué es esta función etl_visits() que se usa para normalizar o realizar el proceso de ETL (extracción, transformación y carga) de los datos para el usuario final? El aspecto resultante será el siguiente:

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]

Probablemente se vea como esperabas: el código recorre todas las visitas y, para cada una, toma los datos del visitante y de la marca de tiempo de forma literal, luego verifica si existe file_gcs o file_blob y, si es así, elige uno de ellos (o None si no existe ninguno).

Aquí tienes una ilustración de las diferencias entre main.py y main-migrate.py:

718b05b2adadb2e1.png

Si comienzas desde cero sin archivos creados por Blobstore, usa main.py, pero si estás en transición y deseas que se admitan los archivos creados por Blobstore y Cloud Storage, consulta main-migrate.py como un ejemplo de cómo abordar este caso para ayudarte a planificar las migraciones de tus propias apps. Cuando se realizan migraciones complejas, es probable que surjan casos especiales, por lo que este ejemplo tiene como objetivo mostrar una mayor afinidad con la modernización de apps reales con datos reales.

6. Resumen/Limpieza

En esta sección, se concluye este codelab con la implementación de la app y la verificación de que funcione según lo previsto y en cualquier resultado reflejado. Después de la validación de la app, realiza los pasos de limpieza y considera los pasos siguientes.

Implementa y verifica la aplicación

Antes de volver a implementar tu app, asegúrate de ejecutar pip install -t lib -r requirements.txt para obtener esas bibliotecas de terceros autoagrupadas en paquetes en la carpeta lib. Si quieres ejecutar la solución retrocompatible, primero cambia el nombre de main-migrate.py a main.py. Ahora, ejecuta gcloud app deploy y confirma que la app funcione de manera idéntica a la del módulo 15. La pantalla del formulario se ve de la siguiente manera:

f5b5f9f19d8ae978.png

La página de visitas más recientes se ve de la siguiente manera:

f5ac6b98ee8a34cb.png

Felicitaciones por completar este codelab en el que se reemplaza Blobstore de App Engine por Cloud Storage, NDB de App Engine por Cloud NDB y webapp2 por Flask. Tu código ahora debería coincidir con el que se encuentra en la carpeta FINISH (Módulo 16). La alternativa main-migrate.py también está presente en esa carpeta.

"Migración" a Python 3

La directiva runtime de Python 3 marcada como comentario en la parte superior de app.yaml es todo lo que se necesita para transferir esta app a Python 3. El código fuente en sí ya es compatible con Python 3, por lo que no es necesario realizar cambios allí. Para implementar esto como una app de Python 3, sigue estos pasos:

  1. Quita el comentario de la directiva runtime de Python 3 en la parte superior de app.yaml.
  2. Borra todas las demás líneas de app.yaml.
  3. Borra el archivo appengine_config.py. (no se usa en el entorno de ejecución de Python 3)
  4. Borra la carpeta lib si existe. (innecesario con el entorno de ejecución de Python 3)

Limpia

General

Si terminaste por ahora, te recomendamos que inhabilites tu app de App Engine para evitar que se generen cargos de facturación. Sin embargo, si quieres realizar más pruebas o experimentos, la plataforma de App Engine tiene una cuota gratuita, por lo que, siempre que no excedas ese nivel de uso, no se te cobrará. Eso es para el procesamiento, pero también puede haber cargos por los servicios de App Engine pertinentes, así que consulta su página de precios para obtener más información. Si esta migración involucra otros servicios de Cloud, estos se facturan por separado. En cualquier caso, si corresponde, consulta la sección "Específico de este codelab" que aparece más abajo.

Para que tengas toda la información, la implementación en una plataforma de computación sin servidores de Google Cloud, como App Engine, genera costos menores de compilación y almacenamiento. Cloud Build tiene su propia cuota gratuita, al igual que Cloud Storage. El almacenamiento de esa imagen consume parte de esa cuota. Sin embargo, es posible que vivas en una región que no tenga ese nivel gratuito, por lo que debes tener en cuenta el uso del almacenamiento para minimizar los costos potenciales. Estas son algunas de las "carpetas" específicas de Cloud Storage que debes revisar:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • Los vínculos de almacenamiento anteriores dependen de tu PROJECT_ID y de la ubicación de *LOC*, por ejemplo, "us" si tu app se aloja en EE.UU.

Por otro lado, si no vas a continuar con esta aplicación ni con otros codelabs de migración relacionados y quieres borrar todo por completo, cierra el proyecto.

Específico de este codelab

Los servicios que se indican a continuación son exclusivos de este codelab. Consulta la documentación de cada producto para obtener más información:

Ten en cuenta que, si migraste del módulo 15 al 16, seguirás teniendo datos en Blobstore, por lo que incluimos su información de precios más arriba.

Próximos pasos

Además de este instructivo, hay otros módulos de migración que se enfocan en dejar de usar los servicios agrupados heredados, como los siguientes:

  • Módulo 2: Migra de ndb de App Engine a Cloud NDB
  • Módulos 7 a 9: Migra de las tareas de envío de la lista de tareas en cola de App Engine a Cloud Tasks
  • Módulos 12 y 13: Migra de Memcache de App Engine a Cloud Memorystore
  • Módulos 18 y 19: Migra de la lista de tareas en cola de App Engine (tareas de extracción) a Cloud Pub/Sub

App Engine ya no es la única plataforma sin servidores en Google Cloud. Si tienes una app de App Engine pequeña o una con funcionalidad limitada y deseas convertirla en un microservicio independiente, o si quieres dividir una app monolítica en varios componentes reutilizables, estos son buenos motivos para considerar migrar a Cloud Functions. Si la creación de contenedores se convirtió en parte de tu flujo de trabajo de desarrollo de aplicaciones, en especial si consiste en una canalización de CI/CD (integración continua/entrega o implementación continuas), considera migrar a Cloud Run. Estas situaciones se abordan en los siguientes módulos:

  • Migra de App Engine a Cloud Functions: consulta el módulo 11
  • Migra de App Engine a Cloud Run: Consulta el módulo 4 para alojar tu app en contenedores con Docker o el módulo 5 para hacerlo sin contenedores, conocimientos de Docker o Dockerfiles

Cambiar a otra plataforma sin servidores es opcional, y te recomendamos que consideres las mejores opciones para tus apps y casos de uso antes de realizar cualquier cambio.

Independientemente del módulo de migración que consideres a continuación, se puede acceder a todo el contenido de la estación de migración sin servidores (codelabs, videos y código fuente [cuando esté disponible]) en su repositorio de código abierto. El archivo README del repositorio también proporciona orientación sobre qué migraciones considerar y cualquier "orden" relevante de los módulos de migración.

7. Recursos adicionales

Problemas o comentarios sobre el codelab

Si encuentras algún problema con este Codelab, primero busca el problema antes de enviarlo. Vínculos para buscar y crear problemas nuevos:

Recursos de migración

En la siguiente tabla, se incluyen vínculos a las carpetas del repositorio para el módulo 15 (START) y el módulo 16 (FINISH). También se puede acceder a ellos desde el repositorio de todas las migraciones de codelabs de App Engine, que puedes clonar o descargar como un archivo ZIP.

Codelab

Python 2

Python 3

Módulo 15

código

N/A

Módulo 16 (este codelab)

código

(igual que Python 2)

Recursos en línea

A continuación, se incluyen recursos en línea que pueden ser pertinentes para este instructivo:

Blobstore de App Engine y Cloud Storage

Plataforma de App Engine

Información de otra nube

Python

Videos

Licencia

Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.