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

1. Descripción general

El objetivo de la serie de codelabs de Serverless Migration Station (instructivos prácticos y de autoaprendizaje) y los videos relacionados es 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 alejándose de los 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á integrar y acceder a una mayor variedad de productos de Cloud, y actualizarte más fácilmente a versiones de lenguaje más recientes. Si bien, en un principio, se enfoca en los primeros usuarios de Cloud, principalmente los 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 cualquier otro lugar si corresponde.

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

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/biblioteca de Blobstore de App Engine
  • Almacena las cargas de los usuarios en el servicio de Blobstore
  • Preparación 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 heredados agrupados en paquetes de App Engine, lo que te permite trasladar 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 framework webapp original, y es por eso que la app de ejemplo usa el framework webapp2 en lugar de Flask. En este instructivo, se muestran migraciones a Cloud Storage, Cloud NDB, Flask y Python 3.

La app sigue registrando las "visitas" de los usuarios finales y muestra las diez más recientes, pero el codelab anterior (Módulo 15) agregó una nueva función para admitir el uso de Blobstore: la app les solicita a los usuarios finales que suban un artefacto (un archivo) correspondiente a su "visita". Los usuarios pueden hacerlo o seleccionar "Omitir" para rechazar la opción. Independientemente de la decisión del usuario, la página siguiente renderiza el mismo resultado que las versiones anteriores de esta app, y muestra las visitas más recientes. Un giro adicional es que las visitas con artefactos correspondientes incluyen un vínculo de "ver" para mostrar el artefacto de una visita. En este codelab, se implementan las migraciones mencionadas anteriormente y se conserva la funcionalidad descrita.

3. Configurar/trabajo previo

Antes de comenzar con la parte principal del instructivo, configuremos nuestro proyecto, obtengamos el código e implementemos la app de modelo de referencia para que comencemos a trabajar con el código.

1. Configura el proyecto

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

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

Uno de los requisitos previos para 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). Este codelab te guiará en cada paso y concluirá con un código que se parezca al que se encuentra en la carpeta "FINISH" del módulo 16.

El directorio de archivos de INICIO del módulo 15 debería verse así:

$ 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 del valor predeterminado de la URL asignada de una app según el ID del proyecto: PROJECT_ID.appspot.com. Este archivo no participa en este codelab (módulo 16), pero se pueden aplicar técnicas de migración similares a ese archivo si lo deseas.

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 de manera correcta y confirmes que funciona tu app del módulo 15 En la página inicial, se saluda a los usuarios con un formulario en el que se les solicita que suban un archivo de artefacto de visita, junto con un botón de "omitir" para rechazar la solicitud:

f5b5f9f19d8ae978.png

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

f5ac6b98ee8a34cb.png

Las visitas que incluyan un artefacto tendrán un vínculo de "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, Blobstore) a alternativas contemporáneas (Flask, Cloud NDB, Cloud Storage).

4. Actualiza archivos de configuración

En la versión actualizada de nuestra app, entran en juego tres archivos de configuración. Las tareas requeridas son las siguientes:

  1. Actualiza las bibliotecas integradas de terceros necesarias en app.yaml y deja la puerta abierta a una migración a Python 3.
  2. Agrega un requirements.txt que especifique 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

Actualiza la sección libraries para editar el archivo app.yaml. Se quita jinja2 y se agregan 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 como comentario. Cuando termines, debería verse así (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 (por lo que no tienes que empaquetarlas por tu cuenta). Quitamos Jinja2 porque viene con Flask, que agregaremos a reqs.txt. Siempre que se usen 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 tiempo de ejecución comentada en la parte superior es para cuando esté todo listo para portar esta app a Python 3. Hablaremos de este tema al final del instructivo.

requirements.txt

Agrega un archivo requirements.txt que requiera 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 que se agrupen en paquetes las 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 realizar la agrupación por tu cuenta.

appengine_config.py

Agrega un archivo appengine_config.py que admita bibliotecas integradas y de terceros 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 Instalación de 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 de esa sección.

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

5. Modifica los archivos de la aplicación

Importaciones

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

  1. webapp2 se reemplaza por Flask
  2. En lugar de usar Jinja2 desde 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 transfiriendo a la biblioteca cliente de Cloud Storage, se usa google.auth para obtener el ID del proyecto y especificar el mismo nombre de bucket. (Puedes cambiar el nombre del bucket, ya que no está codificado de forma rígida).

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 en la lista anterior reemplazando la sección de importación actual en main.py con 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 desde webapp2_extras. Esto no es necesario porque Jinja2 viene con Flask y es su motor de plantillas predeterminado, por lo que debes quitarlo.

En el módulo 16, crea instancias de 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, armamos el nombre del bucket de Cloud Storage como se describió anteriormente en la sección de importaciones. A continuación, se muestran los resultados antes y 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 abordó es la necesidad de un cliente de API. Otro es que este último requiere que el administrador de contexto de Python del cliente de la API controle el acceso a Datastore. Básicamente, 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., BlobKeys, por lo que debes cambiar file_blob por un ndb.StringProperty. A continuación, se muestran la clase del 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)

A continuación, se muestra 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, de lo contrario, por None (el usuario inhabilitó la opción de 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 agregado recientemente 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 completan su funcionalidad con un redireccionamiento a la página principal ( /) y conservan la solicitud POST con un código de retorno HTTP 307.

Controlador de descarga

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 analizar. 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:

  • Una vez más, 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 debes reconocer 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 del lado de Cloud Storage para la funcionalidad que abstraen los controladores de Blobstore, es decir, identificar el archivo (blob) en cuestión y descargar explícitamente el binario en comparación con la única llamada al método send_blob() del controlador de Blobstore.
  • En ambos casos, se muestra 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 métodos get() y post() separados, son esencialmente una instrucción 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 en webapp2 no es posible.

A continuación, se muestra una representación gráfica de este segundo y último conjunto de cambios en main.py:

5ec38818c32fec2.png

(Opcional) "Mejora" de la retrocompatibilidad

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

La primera "extensión" para admitir archivos creados con Blobstore es un modelo de datos que tiene un BlobKeyProperty (además de un StringProperty para los archivos creados con 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 se usará para los archivos de Cloud Storage. Ahora, cuando crees visitas nuevas, almacena explícitamente 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). Si hay un archivo disponible, elige el que exista 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 más importante: ¿qué es esta función etl_visits() que se usa para normalizar o ETL (extraer, transformar y cargar) 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 en bucle todas las visitas y, para cada una, toma los datos de visitantes y marcas 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).

Esta es 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 realizando una transición y deseas admitir archivos creados por Blobstore y Cloud Storage, consulta main-migrate.py como ejemplo de cómo abordar situaciones como esta 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 por la modernización de apps reales con datos reales.

6. Resumen/Limpieza

En esta sección, se completa el 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 necesarios y considera los próximos pasos.

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 compatible con versiones anteriores, primero cambia el nombre de main-migrate.py a main.py. Ahora ejecuta gcloud app deploy y confirma que la app funciona de forma idéntica a la app 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 así:

f5ac6b98ee8a34cb.png

Felicitaciones por completar este codelab en el que reemplazaste Blobstore de App Engine por Cloud Storage, NDB de App Engine por Cloud NDB y webapp2 por Flask. El código ahora debería coincidir con el contenido de la carpeta FINISH (módulo 16). El main-migrate.py alternativo también está presente en esa carpeta.

"Migración" a Python 3

La directiva runtime de Python 3 comentada en la parte superior de app.yaml es todo lo que se necesita para portar esta app a Python 3. El código fuente ya es compatible con Python 3, por lo que no es necesario realizar ningún cambio. Para implementar esto como una app de Python 3, sigue estos pasos:

  1. Quita la marca de comentario de la directiva runtime de Python 3 en la parte superior de app.yaml.
  2. Borra todas las demás líneas en 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 incurrir en cargos de facturación. Sin embargo, si deseas realizar más pruebas o experimentos, la plataforma de App Engine tiene una cuota gratuita, por lo que no se te cobrará siempre que no excedas ese nivel de uso. Esto se aplica a la capacidad de procesamiento, pero también puede haber cargos por los servicios relevantes de App Engine, por lo que debes consultar su página de precios para obtener más información. Si esta migración involucra otros servicios de Cloud, estos se facturarán por separado. En cualquier caso, si corresponde, consulta la sección "Específico para este codelab" que se encuentra más abajo.

Para divulgar 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 usa 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 tu uso de almacenamiento para minimizar los costos potenciales. Las "carpetas" específicas de Cloud Storage que debes revisar incluyen las siguientes:

  • 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 *LOC*ación, por ejemplo, "us" si tu app está alojada 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 tu proyecto.

Específico para 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, otros módulos de migración que se enfocan en dejar de usar los servicios agrupados heredados que debes tener en cuenta son 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 de Google Cloud. Si tienes una app de App Engine pequeña o una que tiene funcionalidad limitada y deseas convertirla en un microservicio independiente, o bien quieres dividir una app monolítica en varios componentes reutilizables, estos son buenos motivos para considerar la posibilidad de migrar a Cloud Functions. Si la contenerización se convirtió en parte de tu flujo de trabajo de desarrollo de aplicaciones, en especial si consta de una canalización de CI/CD (integración continua/entrega o implementación continua), considera migrar a Cloud Run. Estos casos 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 organizar tu app en contenedores con Docker o el módulo 5 para hacerlo sin contenedores, conocimientos de Docker ni 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 Serverless Migration Station (codelabs, videos, código fuente [cuando esté disponible]) en su repositorio de código abierto. El README del repo 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 de los codelabs

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, puedes encontrar vínculos a las carpetas del repositorio para el módulo 15 (INICIAR) y el módulo 16 (FINALIZAR). También se puede acceder a ellos desde el repositorio de todas las migraciones de codelab de App Engine, que puedes clonar o descargar 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

Otra información de la nube

Python

Videos

Licencia

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