1. Descripción general
La serie de codelabs Serverless Migration Station (instructivos prácticos y autoaprendizaje) y los videos relacionados tienen como objetivo ayudar a los desarrolladores de Google Cloud sin servidores a modernizar sus apps guiándolos a través de una o más migraciones, en especial al alejarse 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á integrarlas con una variedad más amplia de productos de Cloud y acceder a ella, y actualizar con mayor facilidad a versiones de idiomas más recientes. Aunque inicialmente 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 cualquier otra, si corresponde.
En este codelab del módulo 15, se explica cómo agregar el uso de blobstore
de App Engine a la app de ejemplo del módulo 0. Luego, estarás listo para migrar ese uso a Cloud Storage a continuación en el módulo 16.
En un próximo lab,
- Agrega el uso de la API o la biblioteca de Blobstore de App Engine
- Almacena las cargas de los usuarios al servicio
blobstore
- Prepárate para el próximo paso de la migración a Cloud Storage
Requisitos
- Un proyecto de Google Cloud Platform con una cuenta de facturación de GCP activa
- Habilidades básicas de Python
- Conocimiento práctico de los comandos comunes de Linux
- Conocimientos básicos sobre el desarrollo y la implementación de aplicaciones de App Engine
- Una aplicación de App Engine del módulo 0 que funcione (obtener del repositorio)
Encuesta
¿Cómo usarás este instructivo?
¿Cómo calificarías tu experiencia en Python?
¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud?
2. Información general
Para migrar desde la API de Blobstore de App Engine, agrega su uso a la app de referencia existente de App Engine ndb
del módulo 0. La app de ejemplo muestra las diez visitas más recientes del usuario. Estamos modificando la app para solicitarle al usuario final que suba un artefacto (un archivo) que corresponda a su “visita”. Si el usuario no desea hacerlo, hay una "omisión" de 12 a 1 con la nueva opción de compresión. Independientemente de la decisión del usuario, la página siguiente muestra el mismo resultado que la app del Módulo 0 (y muchos de los otros módulos de esta serie). Con esta integración blobstore
de App Engine implementada, podemos migrarla a Cloud Storage en el siguiente codelab (módulo 16).
App Engine proporciona acceso a los sistemas de plantillas de Django y Jinja2, y un aspecto que hace diferente este ejemplo (además de agregar el acceso a Blobstore) es que cambia de Django en el módulo 0 a Jinja2 en el módulo 15. Un paso clave para modernizar las aplicaciones de App Engine es migrar los frameworks web de webapp2
a Flask. El último usa Jinja2 como su sistema de plantillas predeterminado, por lo que comenzamos a movernos en esa dirección implementando Jinja2 sin perder el acceso a webapp2
para el acceso a Blobstore. Como Flask usa Jinja2 de forma predeterminada, no se requerirán cambios en la plantilla más adelante en el módulo 16.
3. Configurar/trabajo previo
Antes de pasar a la parte principal del instructivo, configura tu proyecto, obtén el código y, luego, implementa la app de referencia para comenzar a trabajar con código.
1. Configura el proyecto
Si ya implementaste la app del módulo 0, te recomendamos que vuelvas a usar el mismo proyecto (y código). Como alternativa, puedes crear un proyecto completamente nuevo o reutilizar otro proyecto existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y 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 0 que funcione. Si no la tienes, puedes obtenerla en el Módulo 0: "COMENZAR". (vínculo más abajo). En este codelab, se explica cada paso y se concluye con un código similar al que se muestra en el módulo 15, "FINALIZAR" carpeta.
- INICIO: Carpeta del módulo 0 (Python 2)
- FIN: Carpeta del módulo 15 (Python 2)
- Repositorio completo (para clonar o descargar un archivo ZIP)
El directorio de los archivos STARTing del módulo 0 debería verse de la siguiente manera:
$ ls README.md index.html app.yaml main.py
3. (vuelve a) implementa la aplicación de modelo de referencia
Los pasos del trabajo previo restantes para ejecutar ahora sin estos:
- Familiarízate con la herramienta de línea de comandos de
gcloud
- Vuelve a implementar la app de muestra con
gcloud app deploy
- Confirma que la app se ejecuta en App Engine sin problemas
Una vez que hayas ejecutado correctamente esos pasos y veas que tu aplicación web funciona (con un resultado similar al que se muestra a continuación), estarás listo para agregar el uso del almacenamiento en caché a tu aplicación.
4. Actualiza archivos de configuración
app.yaml
No hay cambios sustanciales en la configuración de la aplicación. Sin embargo, como se mencionó antes, pasamos de las plantillas de Django (configuración predeterminada) a Jinja2, por lo que, para realizar el cambio, los usuarios deben especificar la versión más reciente de Jinja2 disponible en los servidores de App Engine, y puedes hacerlo agregándola a la sección de bibliotecas de terceros integradas de app.yaml
.
ANTES:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
Agrega una nueva sección libraries
para editar tu archivo app.yaml
, como se muestra aquí:
DESPUÉS:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
No es necesario actualizar otros archivos de configuración, así que avancemos a los archivos de la aplicación.
5. Modifica archivos de la aplicación
Compatibilidad con importaciones y Jinja2
El primer conjunto de cambios para main.py
incluye agregar el uso de la API de Blobstore y reemplazar las plantillas de Django por Jinja2. Esto es lo que cambiará:
- El propósito del módulo
os
es crear un nombre de ruta de archivo para una plantilla de Django. Dado que cambiamos a Jinja2, donde se controla esto, ya no es necesario usaros
ni el procesador de plantillas de Django,google.appengine.ext.webapp.template
, por lo que se quitarán. - Importa la API de Blobstore:
google.appengine.ext.blobstore
- Importa los controladores de Blobstore que se encuentran en el framework
webapp
original; no están disponibles enwebapp2
:google.appengine.ext.webapp.blobstore_handlers
- Importa compatibilidad con Jinja2 desde el paquete
webapp2_extras
ANTES:
import os
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template
Para implementar los cambios de la lista anterior, reemplaza la sección de importación actual en main.py
con el siguiente fragmento de código.
DESPUÉS:
import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers
Después de las importaciones, agrega código estándar para admitir el uso de Jinja2, como se define en los documentos de webapp2_extras
. El siguiente fragmento de código une la clase de controlador de solicitudes webapp2 estándar con la funcionalidad Jinja2, así que agrega este bloque de código a main.py
justo después de las importaciones:
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))
Cómo agregar compatibilidad con Blobstore
A diferencia de otras migraciones en esta serie en las que mantenemos la funcionalidad o el resultado de la app de ejemplo idénticos (o casi iguales) sin (mucho) cambios en la UX, en este ejemplo, se desvía más la norma. En lugar de registrar inmediatamente una visita nueva y, luego, mostrar los diez más recientes, actualizaremos la app para pedirle al usuario un artefacto de archivo con el que registrar su visita. Los usuarios finales pueden subir el archivo correspondiente o seleccionar “Omitir” de no subir nada. Una vez que se complete este paso, las "visitas más recientes" cuando se muestre la página de inicio de sesión.
Este cambio permite que nuestra app use el servicio de Blobstore para almacenar (y, posiblemente, renderizar más adelante) esa imagen o algún otro tipo de archivo en la página de visitas más recientes.
Actualiza el modelo de datos y, luego, implementa su uso
Estamos almacenando más datos, específicamente, actualizando el modelo de datos para almacenar el ID (llamado “BlobKey
”) del archivo subido a Blobstore y agregando una referencia para guardarlo en store_visit()
. Dado que estos datos adicionales se muestran junto con todo lo demás en la consulta, fetch_visits()
se mantiene igual.
Aquí puedes ver el antes y el después de estas actualizaciones que incluyen file_blob
, una ndb.BlobKeyProperty
:
ANTES:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).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.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)
Esta es una representación ilustrativa de los cambios que se realizaron hasta el momento:
Cómo admitir cargas de archivos
El cambio más significativo en la funcionalidad es la compatibilidad con cargas de archivos, ya sea cuando se le solicita al usuario un archivo o con la opción de "omitir" o el procesamiento de un archivo correspondiente a una visita. Todo es parte de la imagen. Estos son los cambios necesarios para admitir las cargas de archivos:
- La solicitud del controlador principal
GET
ya no recupera las visitas más recientes para mostrarlas. En cambio, solicita al usuario que realice una carga. - Cuando un usuario final envía un archivo para subir el proceso o lo omite, un
POST
del formulario pasa el control al nuevoUploadHandler
, que se deriva degoogle.appengine.ext.webapp.blobstore_handlers.BlobstoreUploadHandler
. - El método
POST
deUploadHandler
realiza la carga, llama astore_visit()
para registrar la visita y activa un redireccionamiento HTTP 307 para devolver al usuario a "/", donde... - El método
POST
del controlador principal realiza consultas (a través defetch_visits()
) y muestra las visitas más recientes. Si el usuario selecciona "omitir", no se sube ningún archivo, pero la visita se registra seguida por el mismo redireccionamiento. - La visualización de las visitas más recientes incluye un campo nuevo que se muestra al usuario, ya sea una "vista" con hipervínculo si hay un archivo de carga disponible o "ninguno" de lo contrario. Estos cambios se realizan en la plantilla HTML junto con la adición de un formulario de carga (más información sobre esto próximamente).
- Si un usuario final hace clic en el botón para cualquier visita con un video subido, realiza una solicitud
GET
a una nuevaViewBlobHandler
, derivada degoogle.appengine.ext.webapp.blobstore_handlers.BlobstoreDownloadHandler
, que procesa el archivo si es una imagen (en el navegador si es compatible), solicita que se descargue si no es así o muestra un error HTTP 404 si no se encuentra. - Además del nuevo par de clases de controlador y un nuevo par de rutas para enviarles tráfico, el controlador principal necesita un nuevo método
POST
para recibir el redireccionamiento 307 que se describió anteriormente.
Antes de estas actualizaciones, la app del Módulo 0 solo mostraba un controlador principal con un método GET
y una sola ruta:
ANTES:
class MainHandler(webapp2.RequestHandler):
'main application (GET) handler'
def get(self):
store_visit(self.request.remote_addr, self.request.user_agent)
visits = fetch_visits(10)
tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(tmpl, {'visits': visits}))
app = webapp2.WSGIApplication([
('/', MainHandler),
], debug=True)
Con esas actualizaciones implementadas, ahora hay tres controladores: 1) un controlador de carga con un método POST
, y 2) un "BLOB de vista". descargar el controlador con un método GET
y 3) el controlador principal con los métodos GET
y POST
. Realiza estos cambios para que el resto de tu app se vea de la siguiente manera.
DESPUÉS:
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)
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)
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)
Hay varias llamadas clave en este código que acabamos de agregar:
- En
MainHandler.get
, hay una llamada ablobstore.create_upload_url
. Esta llamada genera la URL en la quePOST
está el formulario y llama al controlador de carga para enviar el archivo a Blobstore. - En
UploadHandler.post
, hay una llamada ablobstore_handlers.BlobstoreUploadHandler.get_uploads
. Este es el verdadero comando mágico que coloca el archivo en Blobstore y muestra un ID único y persistente para ese archivo, suBlobKey
. - En
ViewBlobHandler.get
, llamar ablobstore_handlers.BlobstoreDownloadHandler.send
con elBlobKey
de un archivo genera la recuperación del archivo y lo reenvía al navegador del usuario final.
Estas llamadas representan la mayor parte del acceso a las funciones que se agregaron a la app. A continuación, se incluye una representación visual del segundo y último conjunto de cambios de main.py
:
Actualizar plantilla HTML
Algunas de las actualizaciones de la aplicación principal afectan la interfaz de usuario (IU) de la app, por lo que se requieren los cambios correspondientes en la plantilla web. De hecho, son dos:
- Se requiere un formulario de carga de archivos con 3 elementos de entrada: un archivo y un par de botones de envío para subir y omitir archivos, respectivamente.
- Agregue una "vista" para actualizar el resultado de las visitas más recientes vínculo para visitas con un archivo correspondiente cargado o "ninguno" de lo contrario.
ANTES:
<!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>
Implementa los cambios de la lista anterior para comprender la plantilla actualizada:
DESPUÉS:
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>
<h1>VisitMe example</h1>
{% if upload_url %}
<h3>Welcome... upload a file? (optional)</h3>
<form action="{{ upload_url }}" method="POST" enctype="multipart/form-data">
<input type="file" name="file"><p></p>
<input type="submit"> <input type="submit" value="Skip">
</form>
{% else %}
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }}
<i><code>
{% if visit.file_blob %}
(<a href="/view/{{ visit.file_blob }}" target="_blank">view</a>)
{% else %}
(none)
{% endif %}
</code></i>
from {{ visit.visitor }}
</li>
{% endfor %}
</ul>
{% endif %}
</body>
</html>
En esta imagen, se muestran las actualizaciones necesarias de index.html
:
Un último cambio es que Jinja2 prefiere sus plantillas en una carpeta templates
, así que crea esa carpeta y mueve index.html
dentro de ella. Con este traslado final, ya terminaste con todos los cambios necesarios para agregar el uso de Blobstore a la app de ejemplo del módulo 0.
“Mejora” de Cloud Storage (opcional)
El almacenamiento de Blobstore evolucionó para transformarse en Cloud Storage. Esto significa que las cargas de Blobstore se pueden ver en la consola de Cloud, específicamente en el navegador de Cloud Storage. La pregunta es dónde. La respuesta es el bucket predeterminado de Cloud Storage de tu aplicación de App Engine. Es el nombre del dominio completo de tu aplicación de App Engine, PROJECT_ID
.appspot.com
. Es muy conveniente porque todos los IDs de los proyectos son únicos, ¿no?
Las actualizaciones realizadas en la aplicación de ejemplo suprimen los archivos subidos en ese bucket, pero los desarrolladores tienen la opción de elegir una ubicación más específica. Se puede acceder al bucket predeterminado de manera programática a través de google.appengine.api.app_identity.get_default_gcs_bucket_name()
, lo que requiere una importación nueva si quieres acceder a este valor, por ejemplo, que se debe usar como prefijo para organizar los archivos subidos. Por ejemplo, ordenar por tipo de archivo:
Para implementar algo así para las imágenes, por ejemplo, tendrás un código como este junto con algunos códigos que verificaban los tipos de archivos para elegir el nombre de bucket deseado:
ROOT_BUCKET = app_identity.get_default_gcs_bucket_name()
IMAGE_BUCKET = '%s/%s' % (ROOT_BUCKET, 'images')
También validarás las imágenes subidas con una herramienta como el módulo imghdr
de la biblioteca estándar de Python para confirmar el tipo de imagen. Por último, te recomendamos limitar el tamaño de las cargas en caso de que haya personas que actúan de mala fe.
Digamos que ya está todo listo. ¿Cómo podemos actualizar nuestra aplicación para que admita especificar dónde almacenar los archivos subidos? La clave es ajustar la llamada a blobstore.create_upload_url
en MainHandler.get
y especificar la ubicación deseada en Cloud Storage para la carga agregando el parámetro gs_bucket_name
de la siguiente manera:
blobstore.create_upload_url('/upload', gs_bucket_name=IMAGE_BUCKET))
Como esta es una actualización opcional si deseas especificar dónde deben ir las cargas, no forma parte del archivo main.py
del repositorio. En su lugar, hay una alternativa llamada main-gcs.py
disponible para que la revises en el repositorio. En lugar de usar una “carpeta” de bucket diferente, El código en main-gcs.py
almacena las cargas en la sección “raíz”. (PROJECT_ID
.appspot.com
) al igual que main.py
, pero proporciona la estructura que necesitas si derivar la muestra en algo más, como se sugiere en esta sección. A continuación, se muestra una ilustración de las "diferencias" entre main.py
y main-gcs.py
.
6. Resumen/limpieza
En esta sección, se finaliza este codelab implementando la app y verificando que funcione según lo previsto y en cualquier resultado reflejado. Después de validar la app, realiza los pasos de limpieza y considera los siguientes.
Implementa y verifica la aplicación
Vuelve a implementar la app con gcloud app deploy
y confirma que funcione según lo anunciado, y que la experiencia del usuario (UX) sea diferente de la app del Módulo 0. Ahora hay dos pantallas diferentes en tu app, y la primera es el mensaje de formulario para subir archivos de visita:
Luego, los usuarios finales suben un archivo y hacen clic en “Enviar” o haz clic en "Omitir" de no subir nada. En cualquier caso, el resultado es la pantalla de la visita más reciente, ahora aumentada con "view" vínculos o "ninguno" entre las marcas de tiempo de la visita y la información del visitante:
Felicitaciones por completar este codelab en el que se agregó el uso de App Engine Blobstore a la app de ejemplo del módulo 0. Tu código ahora debería coincidir con el que está en la carpeta FINISH (Módulo 15). El main-gcs.py
alternativo también está presente en esa carpeta.
Limpia
General
Si ya terminaste, te recomendamos que inhabilites la aplicación de App Engine para evitar que se te facture. Sin embargo, si deseas probar o experimentar un poco más, la plataforma de App Engine tiene una cuota gratuita y, siempre y cuando no superes ese nivel de uso, no se te debería cobrar. Eso es para procesamiento, pero es posible que también se apliquen cargos por servicios relevantes de App Engine, así que consulta la página de precios para obtener más información. Si esta migración implica otros servicios de Cloud, estos se facturan por separado. En ambos casos, si corresponde, consulta la sección "Específico de este codelab" a continuación.
Para una divulgación completa, la implementación en una plataforma de procesamiento 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 ocupa parte de esa cuota. Sin embargo, es posible que vivas en una región que no cuenta con ese nivel gratuito, así que ten en cuenta el uso que haces del almacenamiento para minimizar posibles costos. “Carpetas” específicas de Cloud Storage que debes revisar incluyen
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
*; por ejemplo, "us
" si tu app está alojada en EE.UU.
Por otro lado, si no vas a continuar con esta aplicación o algún otro codelab de migración relacionado y quieres borrar todo por completo, cierra tu proyecto.
Información específica 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:
- El servicio de Blobstore de App Engine está incluido en las cuotas y los límites de datos almacenados, así que revísalos, así como la página de precios de los servicios en paquete heredados.
- El servicio de App Engine Datastore lo proporciona Cloud Datastore (Cloud Firestore en modo Datastore), que también tiene un nivel gratuito. consulta su página de precios para obtener más información.
Próximos pasos
La siguiente migración lógica que se debe considerar se analiza en el módulo 16, en la que se muestra a los desarrolladores cómo migrar del servicio de Blobstore de App Engine al uso de la biblioteca cliente de Cloud Storage. Los beneficios de la actualización incluyen poder acceder a más funciones de Cloud Storage, familiarizarse con una biblioteca cliente que funciona para aplicaciones fuera de App Engine, ya sea en Google Cloud, otras nubes o incluso de forma local. Si no sientes que necesitas todas las funciones disponibles en Cloud Storage o te preocupan sus efectos en el costo, puedes permanecer en Blobstore de App Engine.
Más allá del módulo 16, hay un montón de migraciones posibles, como Cloud NDB y Cloud Datastore, Cloud Tasks o Cloud Memorystore. También hay migraciones de productos cruzados a Cloud Run y Cloud Functions. El repositorio de migración incluye todas las muestras de código, te vincula a todos los codelabs y videos disponibles, y también proporciona orientación sobre qué migraciones considerar y cualquier "pedido" relevante. de las migraciones.
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, encontrarás los vínculos a las carpetas de repositorio del Módulo 0 (COMENZAR) y Módulo 15 (FINALIZAR). También puedes acceder a ellos desde el repositorio para todas las migraciones del codelab de App Engine. Puedes clonar o descargar un archivo ZIP.
Codelab | Python 2 | Python 3 |
Módulo 0 | N/A | |
Módulo 15 (este codelab) | N/A |
Recursos en línea
A continuación, hay recursos en línea que pueden ser relevantes para este tutorial:
App Engine
- Servicio de Blobstore de App Engine
- Cuotas y límites de datos almacenados de App Engine
- Documentación de App Engine
- Entorno de ejecución de App Engine (entorno estándar) para Python 2
- Usa las bibliotecas integradas de App Engine en Python 2 de App Engine
- Información de precios y cuotas de App Engine
- Lanzamiento de la plataforma de App Engine de segunda generación (2018)
- Comparación primero y plataformas de segunda generación
- Asistencia a largo plazo para entornos de ejecución heredados
- Repositorio de muestras de migración de documentación
- Repositorio de muestras de migración aportadas por la comunidad
Google Cloud
- Python en Google Cloud Platform
- Bibliotecas cliente de Python de Google Cloud
- Google Cloud “Siempre gratuito” nivel
- SDK de Google Cloud (herramienta de línea de comandos de gcloud)
- Toda la documentación de Google Cloud
Python
- Sistemas de plantillas de Django y Jinja2
- Framework web de
webapp2
- Documentación de
webapp2
- Vínculos de
webapp2_extras
- Documentación de
webapp2_extras
de Jinja2
Videos
- Estación de migración sin servidores
- Expediciones sin servidores
- Suscríbete a Google Cloud Tech
- Suscríbete a Google Developers.
Licencia
Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.