Módulo 1: Migra de webapp2 de App Engine a Flask

Esta serie de Codelabs (instructivos prácticos y de autoaprendizaje) tienen como objetivo ayudar a los desarrolladores de Google App Engine (estándar) a modernizar sus apps con una serie de migraciones. El paso más importante es dejar de lado los servicios agrupados del entorno de ejecución porque los entornos de ejecución de última generación son más flexibles y ofrecen a los usuarios una mayor variedad de opciones de servicios. El cambio al entorno de ejecución de generación más reciente te permite la integración en productos de Google Cloud con más facilidad, usar una mayor variedad de servicios compatibles y admitir versiones actuales de lenguaje.

En este instructivo inicial, se muestran los primeros pasos de migración para modernizar el framework web en apps de App Engine: pasar de webapp2 a Flask. En tu aplicación, puedes usar cualquier framework web que maneje el enrutamiento, pero, en este instructivo, usamos Flask, ya que la comunidad lo usa en general.

Obtendrás información para hacer las siguientes acciones

  • Usar bibliotecas de terceros (integradas o de otra forma)
  • Actualizar archivos de configuración
  • Migrar una app simple de webapp2 a Flask

Qué necesitará

Encuesta

¿Cómo usarás este codelab?

Solo te recomendamos que lo leas Léelo y completa los ejercicios

El framework webapp se incluyó cuando App Engine se lanzó en Python 2.5 por primera vez en 2008. Años posteriores se reemplazó por el sucesor webapp2 cuando el entorno de ejecución 2.7 se dejó de usar 2.5 en 2013.

Si bien webapp2 (consulta los documentos) aún existe y se puede usar fuera de App Engine como un framework web que cumple con WSGI, no hace su propio enrutamiento de solicitudes de usuario al código correspondiente en la aplicación. En cambio, se basa en App Engine, los archivos de configuración y el desarrollador para realizar ese enrutamiento de tráfico web a los “controladores” correspondientes. Además, los beneficios principales de webapp2 están, en última instancia, vinculados a los servicios integrados de App Engine, por lo que la daremos de baja, aunque funciona en Python 3 (consulta el problema relacionado).

Este módulo proporciona a los profesionales experiencia práctica para migrar una aplicación webapp2 simple a Flask, un framework compatible con App Engine y muchos más servicios fuera de Google Cloud, lo que hace que las aplicaciones sean mucho más portátiles. Si Flask no es un framework deseado para mover tu aplicación, puedes seleccionar otra siempre que realice su propio enrutamiento. Este codelab muestra a los encargados de la toma de decisiones sobre la tecnología de la información (ITDM) y a los desarrolladores los pasos de migración, por lo que puedes familiarizarte con este proceso, sin importar el framework en el que migras.

A continuación, se muestran los pasos principales para esta migración:

  1. Configurar/trabajo previo
  2. Agregar la biblioteca de terceros de Flask
  3. Actualizar archivos de la aplicación
  4. Actualizar el archivo de plantilla HTML

Antes de comenzar con la parte principal del instructivo, vamos a configurar nuestro proyecto, obtenemos el código y, luego, te volvemos a familiarizar con el comando de gcloud e implementaremos la app de modelo de referencia para que comencemos a trabajar con el código.

1. Configura el proyecto

Como desarrollador existente, es probable que el panel de App Engine ya muestre los servicios que tienes en ejecución. A los fines de este instructivo, te recomendamos crear un proyecto nuevo o reutilizar uno existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y que App Engine (app) esté habilitado.

2. Descarga la app de muestra de modelo de referencia

El repositorio de migración de GAE tiene todos los códigos que necesitas. Clona o descarga el archivo ZIP. Para este instructivo, comenzarás con el código en la carpeta del módulo 0 (INICIAR) y, cuando lo hayas completado, tu código debe coincidir con la carpeta del módulo 1 (FINALIZAR). De lo contrario, revisa las diferencias para pasar al siguiente lab.

La carpeta Módulo 0 debe tener archivos que se vean de la siguiente manera, como se ilustra con el comando ls de POSIX:

$ ls
app.yaml        index.html      main.py

3. Vuelve a familiarizarte con los comandos de gcloud

Si aún no tienes el comando de gcloud en tu máquina, instala el SDK de Google Cloud y asegúrate de que gcloud esté disponible como parte de tu ruta de ejecución y que te familiarices con los siguientes comandos de gcloud:

  1. gcloud components update: actualiza el SDK de Google Cloud
  2. gcloud auth login: accede a tu cuenta con credenciales
  3. gcloud config list: enumera la configuración de un proyecto de GCP
  4. gcloud config set project PROJECT_ID: establece el ID del proyecto de GCP
  5. gcloud app deploy: implementa tu aplicación de App Engine

Si no has realizado desarrollos de App Engine con gcloud recientemente, debes ejecutar los primeros cuatro comandos (del #1 al #4) para configurarlos antes de continuar con los siguientes pasos. Repasemos rápidamente estos comandos.

En primer lugar, gcloud components update garantiza que tengas la última versión del SDK de Cloud. Si ejecutas este comando, debería obtener un resultado como el siguiente:

$ gcloud components update

Your current Cloud SDK version is: 317.0.0
You will be upgraded to version: 318.0.0

┌──────────────────────────────────────────────────┐
│        These components will be updated.         │
├──────────────────────────┬────────────┬──────────┤
│           Name           │  Version   │   Size   │
├──────────────────────────┼────────────┼──────────┤
│ Cloud SDK Core Libraries │ 2020.11.06 │ 15.5 MiB │
│ gcloud cli dependencies  │ 2020.11.06 │ 10.6 MiB │
└──────────────────────────┴────────────┴──────────┘

The following release notes are new in this upgrade.
Please read carefully for information about new features, breaking changes,
and bugs fixed.  The latest full release notes can be viewed at:
  https://cloud.google.com/sdk/release_notes

318.0.0 (2020-11-10)

      . . .
      (release notes)
      . . .

    Subscribe to these release notes at
    https://groups.google.com/forum/#!forum/google-cloud-sdk-announce.

Do you want to continue (Y/n)?

╔════════════════════════════════════════════════════════════╗
╠═ Creating update staging area                             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Uninstalling: Cloud SDK Core Libraries                   ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Uninstalling: gcloud cli dependencies                    ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud SDK Core Libraries                     ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: gcloud cli dependencies                      ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Creating backup and activating new installation          ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.

Update done!

To revert your SDK to the previously installed version, you may run:
  $ gcloud components update --version 317.0.0

A continuación, usa gcloud auth login a fin de autenticarte para los comandos de gcloud que emitirás en el futuro:

$ gcloud auth login
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?response_type=code&client_id= . . .

You are now logged in as [YOUR_EMAIL].
Your current project is [PROJECT_ID].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID

Usa gcloud config list para ver cuál es la configuración actual de tu proyecto:

$ gcloud config list
[core]
account = YOUR_EMAIL
disable_usage_reporting = False
project = PROJECT_ID

Your active configuration is: [default]

El comando anterior debe guiarte para crear un proyecto nuevo o seleccionar uno existente. Si la salida de gcloud config list no coincide con el proyecto seleccionado que planeas usar para este instructivo, ejecuta gcloud config set project PROJECT_ID a fin de configurar el ID del proyecto. Luego, ejecuta gcloud config list nuevamente para confirmar el ID del proyecto correcto.

$ gcloud config set project PROJECT_ID
Updated property [core/project].

Si prefieres usar Cloud Console, puedes seguir la interfaz de usuario para crear un proyecto nuevo si lo deseas o usar cualquier proyecto existente que ya tengas. En el panel de tu proyecto, deberías ver la tarjeta de información del proyecto que muestra su ID (junto con el nombre y el número del proyecto):

Tarjeta de información del proyecto

El último comando (#5), gcloud app deploy, es para implementar tu app en App Engine. Dado que este es solo el comienzo, la ejecución es opcional, pero no desaconsejamos implementar el código del módulo 0 para confirmar que funciona. Luego de la ejecución, selecciona la región geográfica en la que desees que se ejecute la app (por lo general, dónde te encuentras). Sin embargo, no se puede cambiar una vez definido. Luego, mira el resto de la información de la implementación. Cuando se complete, recibirás una notificación sobre la URL en la que se entregará tu app. A continuación, se muestra una versión resumida de lo que puede aparecer:

$ gcloud app deploy
Services to deploy:

descriptor:      [/private/tmp/mod0-baseline/app.yaml]
source:          [/private/tmp/mod0-baseline]
target project:  [PROJECT_ID]
target service:  [default]
target version:  [20201116t220827]
target url:      [https://PROJECT_ID.REG_ABBR.r.appspot.com]

Do you want to continue (Y/n)?

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 1 file to Google Cloud Storage                 ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://PROJECT_ID.REG_ABBR.r.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

Si no has usado App Engine por un tiempo, es posible que notes que el comando appcfg.py update de la implementación original se reemplazó por gcloud app deploy. Para obtener más información sobre gcloud app deploy, consulta su página de documentación.

Otro cambio reciente es la URL de las apps implementadas y se modificó de http://PROJECT_ID.appspot.com a http://PROJECT_ID.REG_ABBR.r.appspot.com. Con el tiempo, la mayoría de las apps se convertirán al formato nuevo. Obtén más información sobre el formato de URL en la documentación de solicitudes y enrutamiento.

Después de implementar la aplicación, actualiza el navegador (posiblemente varias veces) para ver las visitas más recientes:

app de visitme

Si tu aplicación es nueva, solo verás una o algunas visitas.

El entorno de ejecución de App Engine para Python 2 proporciona un conjunto de bibliotecas de terceros “integradas”, en las que solo debes especificarlos en los archivos app.yaml que usarás. Aunque esta migración no requiere su uso, estarán en el siguiente instructivo de migración (para el módulo 2).

Las bibliotecas de terceros que no están integradas deben especificarse en un archivo llamado requirements.txt y deben instalarse de forma local en la carpeta lib del mismo directorio que el código de la aplicación, en el que se sube todo a App Engine. En la documentación para agrupar bibliotecas de terceros encontrarás más información.

Las bibliotecas copiadas, como Flask, requieren que indiques que App Engine que las busque en la carpeta lib mediante el archivo de configuración appengine_config.py. El archivo de configuración appengine_config.py se ubica en la misma carpeta de la aplicación de nivel superior a requirements.txt y lib. En esta parte del instructivo, harás lo que se indica a continuación:

  • Crea requirements.txt (especifica las bibliotecas copiadas [no integradas] de terceros)
  • Crea appengine_config.py (reconoce las bibliotecas de terceros)
  • Instala paquetes y dependencias (de terceros)

1. Crea requirements.txt

Crea un archivo requirements.txt para especificar tus paquetes. En nuestro caso, Flask es la biblioteca de terceros que se necesita. En el momento en el que se redacta este documento, la versión más reciente es 1.1.2, por lo que debes crear requirements.txt con esta línea:

Flask==1.1.2

Consulta la documentación de requirements.txt para obtener más información sobre los formatos aceptados.

2. Crea appengine_config.py

En el siguiente paso, harás que App Engine reconozca las bibliotecas externas de terceros. Crea un archivo llamado appengine_config.py con el siguiente contenido:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

Este código hace exactamente lo que especificamos, es decir, apunta a App Engine a la carpeta lib para las bibliotecas copiadas.

3. Instala paquetes y dependencias

Ahora, ejecuta el comando pip install para crear la carpeta lib y, luego, instala Flask y sus dependencias allí:

$ pip install -t lib -r requirements.txt

Ya sea que hayas usado pip o pip2, una vez que se completa la instalación del paquete, debes tener una carpeta lib con contenido similar al siguiente:

$ ls lib
bin/
click/
click-7.1.2.dist-info/
flask/
Flask-1.1.2.dist-info/
itsdangerous/
itsdangerous-1.1.0.dist-info/
jinja2/
Jinja2-2.11.2.dist-info/
markupsafe/
MarkupSafe-1.1.1.dist-info/
werkzeug/
Werkzeug-1.0.1.dist-info/

Ahora, actualicemos el archivo de la aplicación, main.py.

1. Importaciones

Las importaciones aparecen primero en todos los archivos de Python. La importación del framework webapp2 está seguida de la biblioteca ndb de Datastore y, por último, la extensión de App Engine que procesa plantillas con estilo de Django. Debería ver lo siguiente:

  • ANTES:
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

Cuando migras a Flask, importas Flask y las imágenes del procesador de plantillas al mismo tiempo. Borra el par de importaciones relacionadas con webapp2 y reemplázalas de la siguiente manera (deja la importación de ndb como está):

  • DESPUÉS:
from flask import Flask, render_template, request
from google.appengine.ext import ndb

2. Inicio

Las apps que usan webapp2 requieren un solo array (lista de Python) que enumera todas las rutas y controladores en cualquier archivo de Python (puede haber otros):

  • ANTES:
app = webapp2.WSGIApplication([
    ('/', MainHandler),
], debug=True)

Ten en cuenta que app.yaml realiza un enrutamiento de un nivel superior y puede llamar a controladores diferentes. La app de muestra es lo suficientemente simple como para que todas las rutas lleguen al controlador main.py.

Flask no usa tablas de enrutamiento como esta, por lo que debes borrar estas líneas en main.py. Flask también requiere inicialización, así que agregue la siguiente línea arriba de main.py justo debajo de las importaciones:

  • DESPUÉS:
app = Flask(__name__)

En Flask, debes inicializar el framework y, luego, usar los decoradores para definir las rutas. Además, las rutas se vinculan con funciones, no con clases ni métodos.

Está fuera del alcance incluir un instructivo de Flask en este codelab, por lo que debes revisar el instructivo de Flask y la documentación de Flask para familiarizarte con el framework.

3. Modelo de datos

No hay cambios aquí. Datastore se enfocará en el siguiente codelab.

4. Controladores

La aplicación, sin importar el framework que uses (webapp2 o Flask), realiza las siguientes 3 acciones:

  1. Controla solicitudes GET de la ruta de acceso raíz (/)
  2. Registra una página web “visita” (crea y almacenar el objeto Visit)
  3. Muestra las 10 visitas más recientes (con una plantilla predefinida, index.html).

El framework webapp2 usa un modelo de ejecución basado en clases en el que se crean los controladores para cada método HTTP compatible. En nuestro caso simple, solo tenemos GET, por lo que se define un método get():

  • ANTES:
class MainHandler(webapp2.RequestHandler):
    def get(self):
        store_visit(self.request.remote_addr, self.request.user_agent)
        visits = fetch_visits(10) or ()  # empty sequence if None
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(tmpl, {'visits': visits}))

Como se mencionó antes, Flask hace su propio enrutamiento. En lugar de una clase de controlador, puedes escribir funciones y decorar con la ruta por la que deberían llamarse. Los usuarios pueden especificar métodos HTTP controlados en la llamada de decorador, es decir, @app.route('/app/', methods=['GET', 'POST']). Dado que el valor predeterminado es solo GET (implícitamente HEAD), se puede dejar en blanco.

Cuando migres a Flask, reemplaza la clase MainHandler y su método get() por la siguiente función de enrutamiento de Flask:

  • DESPUÉS:
@app.route('/')
def root():
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10) or ()  # empty sequence if None
    return render_template('index.html', visits=visits)

Por supuesto, esto no representa a tu app, que será más compleja que esta muestra. Uno de los objetivos principales de estos instructivos es ayudarte a comenzar, compilar parte de esa “memoria muscular” y comprender dónde realizar cambios en el código específico de App Engine. Para confirmar que realizaste este cambio correctamente, compara el suyo con el Módulo 1 main.py.

5. Archivos auxiliares

No hay cambios en el archivo .gcloudignore. Su propósito es especificar archivos para no implementar en App Engine y que no son necesarios implementar y ejecutar en la aplicación, incluidos, sin limitaciones a Python auxiliar, control de código fuente, código fuente del repositorio y otros archivos. Nuestro .gcloudignore se ve así (no se incluyen los comentarios por brevedad):

.gcloudignore
.git
.gitignore
.hgignore
.hg/
*.pyc
*.pyo
__pycache__/
/setup.cfg
README.md

1. Mueve el archivo de plantilla

En la carpeta del repositorio de referencia (módulo 0), el archivo de plantilla index.html se encuentra en la misma carpeta que los archivos de la aplicación. Como Flask requiere archivos HTML ubicados en una carpeta templates, debes crear esa carpeta (mkdir templates) y mover index.html allí. En un sistema que cumple con POSIX, como Linux o Mac OS X, los comandos serían los siguientes:

mkdir templates
mv index.html templates

2. Actualiza el archivo de plantilla

Una vez que muevas index.html a templates, será hora de hacer una edición pequeña, pero obligatoria. Veamos el archivo de plantilla original en su totalidad:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

</body>
</html>

Mientras que webapp2 usa plantillas de Django que ejecutan funciones que admiten llamadas como visit.timestamp.ctime sin paréntesis ( ), Jinja2 las necesita de forma explícita. Si bien esto parece a un pequeño ajuste, las plantillas de Jinja son más eficaces, listas para usar porque puede pasar argumentos en llamadas.

En Django, tienes que crear una “etiqueta de plantilla” o escribir un filtro. Con esta idea, actualiza index.html y agrega un par de paréntesis a la llamada visit.timestamp.ctime:

  • ANTES:
<li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
  • DESPUÉS:
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>

Este es el único cambio requerido. no se necesitan cambios adicionales para index.html en todos los Codelabs de migración restantes.

Implemente la aplicación

Cuando hayas completado todos los cambios de este instructivo, los archivos en la carpeta de la aplicación deben ser idénticos (o casi iguales) al archivo en la carpeta del repositorio del módulo 1. Ahora implementa y ve que tu aplicación Flask del módulo 1 se ejecuta de forma idéntica a la versión webapp2 del módulo 0.

Usa el comando gcloud app deploy como hicimos antes cuando se implementó el código original del módulo 0. Accede a la aplicación en PROJECT_ID.appspot.com, ya sea desde un navegador web o desde un comando curl o wget para confirmar que funciona como se espera.

Si recibes algún tipo de error de servidor, por lo general, significa que se escribirá algún error de ortografía en el código de Python. Consulta los registros de la aplicación para investigar. También compara sus archivos con los del repositorio del módulo 1 (vínculo arriba de la sección).

Opcional: Limpieza

¿Qué te parece limpiar a fin de evitar que se te facture hasta que estés listo para pasar a la siguiente codelab de migración? Como desarrolladores existentes, es probable que ya estés al día en la información sobre precios de App Engine.

Opcional: Inhabilita la app

Si aún no estás listo para continuar con el siguiente instructivo, inhabilita tu app a fin de evitar que se apliquen cargos. Cuando estés listo para pasar al siguiente codelab, puedes volver a habilitarla. Aunque tu app esté inhabilitada, no recibirá tráfico que genere cargos. Sin embargo, si se excede la cuota gratuita, se te cobrará por el uso de Datastore. así que borra lo suficiente para que quede dentro de ese límite.

Por otro lado, si no vas a continuar con las migraciones y quieres borrar todo por completo, puedes cerrar tu proyecto.

Próximos pasos

Hay dos módulos de migración que COMIENZAN con el código del módulo 1, módulo 2 y 7 completado:

  • Módulo 2 (obligatorio si usas Datastore)
    • Migra de App Engine ndb a Cloud NDB
    • Después de cambiar a Cloud NDB, hay muchas otras opciones disponibles
      • Crea contenedores para tu app a fin de que se ejecute en Cloud Run
      • Migra aún más tu aplicación a la biblioteca cliente de Cloud Datastore
      • Migra tu app a Cloud Firestore para acceder a las funciones de Firebase
  • Módulo 7 (obligatorio si usas las listas de tareas en cola [enviar])
    • Agrega el uso de taskqueue de App Engine (enviar).
    • Prepara la app del módulo 1 para la migración a Cloud Tasks en el módulo 8

Problemas o comentarios de los Codelabs del módulo de migración de App Engine

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

Codelab

Python 2

Python 3

Módulo 0

código

(n/a)

Módulo 1

código

(n/a)

Recursos de App Engine

A continuación, se muestran recursos adicionales con respecto a esta migración específica: