Migrar do App Engine Blobstore para o Cloud Storage (módulo 16)

1. Visão geral

A série de codelabs da Estação de migração sem servidor (tutoriais práticos e autoguiados) e os vídeos relacionados têm como objetivo ajudar os desenvolvedores sem servidor do Google Cloud a modernizar os aplicativos, orientando-os em uma ou mais migrações, principalmente para deixar de usar serviços legados. Isso torna seus apps mais portáteis e oferece mais opções e flexibilidade, permitindo que você se integre e acesse uma variedade maior de produtos do Cloud e faça upgrade para versões mais recentes de linguagens com mais facilidade. Embora o foco inicial seja nos primeiros usuários do Cloud, principalmente desenvolvedores do App Engine (ambiente padrão), esta série é ampla o suficiente para incluir outras plataformas sem servidor, como o Cloud Functions e o Cloud Run, ou outras, se aplicável.

Neste codelab, você vai aprender a migrar do Blobstore do App Engine para o Cloud Storage. Também há migrações implícitas de:

Consulte os módulos de migração relacionados para mais informações detalhadas.

Você vai aprender a

  • Adicionar o uso da API/biblioteca do Blobstore do App Engine
  • Armazenar uploads de usuários no serviço Blobstore
  • Preparar-se para a próxima etapa de migração para o Cloud Storage

O que é necessário

Pesquisa

Como você vai usar este tutorial?

Apenas leitura Ler e fazer os exercícios

Como você classificaria sua experiência com Python?

Iniciante Intermediário Proficiente

Como você classificaria sua experiência de uso dos serviços do Google Cloud?

Iniciante Intermediário Proficiente

2. Contexto

Este codelab começa com o app de exemplo do módulo 15 e demonstra como migrar do Blobstore (e do NDB) para o Cloud Storage (e o Cloud NDB). O processo de migração envolve a substituição de dependências nos serviços integrados legados do App Engine, o que permite mover seus apps para outra plataforma sem servidor do Cloud ou outra plataforma de hospedagem, se quiser.

Essa migração exige um pouco mais de esforço em comparação com as outras migrações desta série. O Blobstore tem dependências do framework do webapp original, e é por isso que o app de exemplo usa o framework do webapp2 em vez do Flask. Este tutorial apresenta migrações para o Cloud Storage, o Cloud NDB, o Flask e o Python 3.

O app ainda registra as "visitas" do usuário final e mostra as dez mais recentes, mas o codelab anterior (Módulo 15) adicionou uma nova funcionalidade para acomodar o uso do Blobstore: o app solicita que os usuários finais façam upload de um artefato (um arquivo) correspondente à "visita". Os usuários podem fazer isso ou selecionar "pular" para recusar. Independentemente da decisão do usuário, a próxima página renderiza a mesma saída que as versões anteriores do app, mostrando as visitas mais recentes. Além disso, as visitas com artefatos correspondentes têm um link "Visualizar" para mostrar o artefato de uma visita. Este codelab implementa as migrações mencionadas anteriormente, preservando a funcionalidade descrita.

3. Configuração/Pré-trabalho

Antes de chegarmos à parte principal do tutorial, vamos configurar o projeto, acessar o código e implantar o app de referência para sabermos que começamos com um código funcional.

1. Configurar projeto

Se você já implantou o app do Módulo 15, recomendamos reutilizar o mesmo projeto (e código). Como alternativa, é possível criar um projeto totalmente novo ou reutilizar outro projeto. Verifique se o projeto tem uma conta de faturamento ativa e se o App Engine está ativado.

2. Receber app de amostra do valor de referência

Um dos pré-requisitos deste codelab é ter um app de exemplo do módulo 15 em funcionamento. Se você não tiver, baixe na pasta "START" do módulo 15 (link abaixo). Este codelab mostra cada etapa, concluindo com um código semelhante ao que está na pasta "FINISH" do módulo 16.

O diretório de arquivos START do Módulo 15 deve ficar assim:

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

O arquivo main-gcs.py é uma versão alternativa de main.py do Módulo 15 que permite selecionar um bucket do Cloud Storage diferente do padrão do URL atribuído a um app com base no ID do projeto: PROJECT_ID.appspot.com. Esse arquivo não faz parte deste codelab (Módulo 16), mas técnicas de migração semelhantes podem ser aplicadas a ele, se você quiser.

3. (Re) Implantar aplicativo de referência

As etapas de pré-trabalho restantes para serem executadas agora:

  1. Conheça melhor a ferramenta de linha de comando gcloud
  2. Reimplantar o aplicativo de amostra com gcloud app deploy
  3. Confirmar se o aplicativo é executado no App Engine sem problemas

Depois de executar essas etapas e confirmar que o app do Módulo 15 funciona. A página inicial recebe os usuários com um formulário que solicita o upload de um arquivo de artefato de visita, além de uma opção, um botão "pular", para recusar:

f5b5f9f19d8ae978.png

Depois que os usuários fazem upload de um arquivo ou pulam essa etapa, o app renderiza a página "Visitas mais recentes":

f5ac6b98ee8a34cb.png

As visitas com um artefato terão um link "Visualizar" à direita do carimbo de data/hora da visita para exibir (ou baixar) o artefato. Depois de confirmar a funcionalidade do app, você vai poder migrar dos serviços legados do App Engine (webapp2, NDB, Blobstore) para alternativas contemporâneas (Flask, Cloud NDB, Cloud Storage).

4. Atualizar os arquivos de configuração

Três arquivos de configuração entram em jogo para a versão atualizada do nosso app. As tarefas necessárias são:

  1. Atualizar as bibliotecas de terceiros integradas necessárias em app.yaml e deixar a porta aberta para uma migração do Python 3
  2. Adicione um requirements.txt, que especifica todas as bibliotecas necessárias que não são integradas.
  3. Adicione appengine_config.py para que o app seja compatível com bibliotecas de terceiros integradas e não integradas.

app.yaml

Edite o arquivo app.yaml atualizando a seção libraries. Remova jinja2 e adicione grpcio, setuptools e ssl. Escolha a versão mais recente disponível para as três bibliotecas. Adicione também a diretiva runtime do Python 3, mas comentada. Quando terminar, o resultado será assim (se você tiver selecionado o Python 3.9):

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

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

libraries:
- name: jinja2
  version: latest

DEPOIS:

#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

As mudanças lidam principalmente com as bibliotecas integradas do Python 2 disponíveis nos servidores do App Engine (para que você não precise agrupá-las por conta própria). Removemos o Jinja2 porque ele vem com o Flask, que vamos adicionar ao reqs.txt. Sempre que as bibliotecas de cliente do Google Cloud, como as do Cloud NDB e do Cloud Storage, são usadas, grpcio e setuptools são necessários. Por fim, o próprio Cloud Storage requer a biblioteca ssl. A diretiva de ambiente de execução comentada na parte de cima é para quando você estiver pronto para portar esse app para o Python 3. Vamos abordar esse assunto no final deste tutorial.

requirements.txt

Adicione um arquivo requirements.txt, que requer o framework Flask e as bibliotecas de cliente do Cloud NDB e do Cloud Storage, que não são integradas. Crie o arquivo com este conteúdo:

flask
google-cloud-ndb
google-cloud-storage

O ambiente de execução do App Engine para Python 2 exige o autoempacotamento de bibliotecas de terceiros não integradas. Portanto, execute o seguinte comando para instalar essas bibliotecas na pasta lib:

pip install -t lib -r requirements.txt

Se você tiver o Python 2 e o 3 na sua máquina de desenvolvimento, talvez seja necessário usar o comando pip2 para garantir que as versões do Python 2 dessas bibliotecas sejam usadas. Depois de fazer upgrade para o Python 3, não será mais necessário fazer o auto-bundle.

appengine_config.py

Adicione um arquivo appengine_config.py que ofereça suporte a bibliotecas de terceiros integradas e não integradas. Crie o arquivo com este conteúdo:

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)

As etapas que você acabou de concluir devem ser semelhantes ou idênticas às listadas na seção Como instalar bibliotecas para apps em Python 2 da documentação do App Engine. Mais especificamente, o conteúdo de appengine_config.py precisa corresponder ao que está na Etapa 5.

O trabalho nos arquivos de configuração está concluído. Vamos passar para o aplicativo.

5. Modificar arquivos do aplicativo

Importações

O primeiro conjunto de mudanças para main.py inclui a troca de tudo o que está sendo substituído. Confira o que vai mudar:

  1. webapp2 é substituído pelo Flask
  2. Em vez de usar o Jinja2 de webapp2_extras, use o Jinja2 que vem com o Flask.
  3. O Blobstore e o NDB do App Engine são substituídos pelo Cloud NDB e pelo Cloud Storage
  4. Os gerenciadores do Blobstore em webapp são substituídos por uma combinação do módulo de biblioteca padrão io, do Flask e dos utilitários werkzeug.
  5. Por padrão, o Blobstore grava em um bucket do Cloud Storage nomeado com base no URL do aplicativo (PROJECT_ID.appspot.com). Como estamos fazendo a portabilidade para a biblioteca de cliente do Cloud Storage, google.auth é usado para acessar o ID do projeto e especificar exatamente o mesmo nome de bucket. É possível mudar o nome do bucket, já que ele não está mais codificado.

ANTES:

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

Implemente as mudanças na lista acima substituindo a seção de importação atual em main.py pelo snippet de código abaixo.

DEPOIS:

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

Inicialização e suporte desnecessário ao Jinja2

O próximo bloco de código a ser substituído é o BaseHandler, que especifica o uso do Jinja2 do webapp2_extras. Isso é desnecessário porque o Jinja2 vem com o Flask e é o mecanismo de modelagem padrão dele. Portanto, remova-o.

No módulo 16, instancie objetos que não estavam no app antigo. Isso inclui inicializar o app do Flask e criar clientes de API para o Cloud NDB e o Cloud Storage. Por fim, juntamos o nome do bucket do Cloud Storage, conforme descrito acima na seção de importações. Confira o antes e o depois de implementar essas atualizações:

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

DEPOIS:

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

Atualizar o acesso ao Datastore

O Cloud NDB é compatível com o NDB do App Engine na maioria dos casos. Uma diferença já abordada é a necessidade de um cliente de API. Outra é que o último exige que o acesso ao Datastore seja controlado pelo gerenciador de contexto do Python do cliente da API. Basicamente, isso significa que todas as chamadas de acesso do Datastore que usam a biblioteca de cliente do Cloud NDB só podem ocorrer em blocos with do Python.

Essa é uma mudança.A outra é que o Blobstore e os objetos dele, como BlobKeys, não são compatíveis com o Cloud Storage. Portanto, mude o file_blob para um ndb.StringProperty. Confira abaixo a classe do modelo de dados e as funções store_visit() e fetch_visits() atualizadas que refletem essas mudanças:

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)

DEPOIS:

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)

Confira uma representação gráfica das mudanças feitas até agora:

a8f74ca392275822.png

Como atualizar os gerenciadores

Gerenciador de uploads

Os gerenciadores em webapp2 são classes, enquanto no Flask são funções. Em vez de um método de verbo HTTP, o Flask usa o verbo para decorar a função. O Blobstore e os gerenciadores webapp dele são substituídos pela funcionalidade do Cloud Storage, bem como pelo Flask e os utilitários dele:

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)

DEPOIS:

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

Algumas observações sobre essa atualização:

  • Em vez de um blob_id, os artefatos de arquivo agora são identificados pelo nome do arquivo (fname), se houver, e por None, caso contrário (o usuário optou por não fazer upload de um arquivo).
  • Os gerenciadores do Blobstore abstraíram o processo de upload dos usuários, mas o Cloud Storage não. Por isso, é possível ver o código recém-adicionado que define o objeto blob e o local (bucket) do arquivo, bem como a chamada que realiza o upload real. (upload_from_file()).
  • O webapp2 usa uma tabela de roteamento na parte de baixo do arquivo do aplicativo, enquanto as rotas do Flask são encontradas em cada gerenciador decorado.
  • Os dois gerenciadores finalizam a funcionalidade redirecionando para a página inicial ( /) e preservando a solicitação POST com um código de retorno HTTP 307.

Gerenciador de downloads

A atualização do gerenciador de download segue um padrão semelhante ao do gerenciador de upload, mas há muito menos código para analisar. Substitua a funcionalidade do Blobstore e do webapp pelos equivalentes do Cloud Storage e do 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)

DEPOIS:

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

Observações sobre esta atualização:

  • Novamente, o Flask decora as funções do gerenciador com a rota delas, enquanto o webapp faz isso em uma tabela de roteamento na parte de baixo. Portanto, reconheça a sintaxe de correspondência de padrões do último (('/view/([^/]+)?') em comparação com a do Flask ('/view/<path:fname>').
  • Assim como no gerenciador de upload, é necessário um pouco mais de trabalho no Cloud Storage para a funcionalidade abstraída pelos gerenciadores do Blobstore, ou seja, identificar o arquivo (blob) em questão e fazer o download explícito do binário em vez da chamada de método send_blob() única do gerenciador do Blobstore.
  • Em ambos os casos, um erro HTTP 404 será retornado ao usuário se um artefato não for encontrado.

Gerenciador principal

As mudanças finais no aplicativo principal ocorrem no gerenciador principal. Os métodos de verbo HTTP webapp2 foram substituídos por uma única função que combina as funcionalidades deles. Substitua a classe MainHandler pela função root() e remova a tabela de roteamento webapp2, conforme mostrado abaixo:

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)

DEPOIS:

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

Em vez de separar os métodos get() e post(), eles são essencialmente uma instrução if-else em root(). Além disso, como root() é uma única função, há apenas uma chamada para renderizar o modelo para GET e POST, enquanto isso não é possível em webapp2.

Confira uma representação gráfica desse segundo e último conjunto de mudanças em main.py:

5ec38818c32fec2.png

(Opcional) "Melhoria" de compatibilidade com versões anteriores

A solução criada acima funciona perfeitamente, mas apenas se você estiver começando do zero e não tiver arquivos criados pelo Blobstore. Como atualizamos o app para identificar arquivos pelo nome de arquivo em vez de BlobKey, o app do Módulo 16 concluído no estado em que se encontra não poderá visualizar arquivos do repositório de blobs. Em outras palavras, fizemos uma mudança incompatível com versões anteriores ao realizar essa migração. Agora, apresentamos uma versão alternativa de main.py chamada main-migrate.py (encontrada no repositório) que tenta preencher essa lacuna.

A primeira "extensão" compatível com arquivos criados pelo Blobstore é um modelo de dados que tem um BlobKeyProperty (além de um StringProperty para arquivos criados pelo 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()

A propriedade file_blob será usada para identificar arquivos criados no Blobstore, enquanto file_gcs é para arquivos do Cloud Storage. Agora, ao criar novas visitas, armazene explicitamente um valor em file_gcs em vez de file_blob. Assim, store_visit vai ficar um pouco 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()

DEPOIS:

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

Ao buscar as visitas mais recentes, "normalize" os dados antes de enviá-los ao modelo:

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)

DEPOIS:

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

Em seguida, confirme a existência de file_blob ou file_gcs (ou nenhum deles). Se houver um arquivo disponível, escolha o que existe e use esse identificador (BlobKey para arquivos criados no Blobstore ou nome de arquivo para arquivos criados no Cloud Storage). Quando dizemos "arquivos criados pelo Cloud Storage", estamos nos referindo a arquivos criados usando a biblioteca de cliente do Cloud Storage. O Blobstore também grava no Cloud Storage, mas, nesse caso, os arquivos são criados pelo Blobstore.

Agora, o mais importante: o que é essa função etl_visits() usada para normalizar ou fazer o ETL (extrair, transformar e carregar) dos dados para o usuário final? Esta é a aparência dela:

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]

Provavelmente, o código é o que você esperava: ele faz um loop por todas as visitas e, para cada uma, pega os dados do visitante e do carimbo de data/hora, depois verifica se file_gcs ou file_blob existe e, se sim, escolhe um deles (ou None se nenhum existir).

Confira uma ilustração das diferenças entre main.py e main-migrate.py:

718b05b2adadb2e1.png

Se você estiver começando do zero sem arquivos criados pelo Blobstore, use main.py. No entanto, se estiver fazendo a transição e quiser que os arquivos criados pelo Blobstore e pelo Cloud Storage sejam compatíveis, confira main-migrate.py como um exemplo de como lidar com um cenário como esse para ajudar a planejar migrações para seus próprios apps. Ao fazer migrações complexas, é provável que surjam casos especiais. Por isso, este exemplo tem como objetivo mostrar uma maior afinidade com a modernização de apps reais com dados reais.

6. Resumo/limpeza

Esta seção encerra o codelab com a implantação do app, a verificação de que ele funciona conforme o esperado e a análise de qualquer saída refletida. Após a validação do app, execute as etapas de limpeza e considere as próximas etapas.

Implantar e verificar o aplicativo

Antes de reimplantar o app, execute pip install -t lib -r requirements.txt para colocar as bibliotecas de terceiros autoempacotadas na pasta lib. Se você quiser executar a solução compatível com versões anteriores, renomeie main-migrate.py como main.py primeiro. Agora execute gcloud app deploy e confirme se o app funciona de maneira idêntica ao do módulo 15. A tela do formulário é assim:

f5b5f9f19d8ae978.png

A página de visitas mais recentes é assim:

f5ac6b98ee8a34cb.png

Parabéns por concluir este codelab que substitui o Blobstore do App Engine pelo Cloud Storage, o NDB do App Engine pelo Cloud NDB e o webapp2 pelo Flask. Seu código agora deve corresponder ao que está na pasta FINISH (Módulo 16). A alternativa main-migrate.py também está presente nessa pasta.

"Migração" para o Python 3

A diretiva runtime do Python 3 comentada na parte de cima de app.yaml é tudo o que é necessário para portar esse app para o Python 3. O código-fonte já é compatível com o Python 3, então não é necessário fazer mudanças. Para implantar como um app Python 3, siga estas etapas:

  1. Remova a marca de comentário da diretiva runtime do Python 3 na parte de cima de app.yaml.
  2. Exclua todas as outras linhas em app.yaml.
  3. Exclua o arquivo appengine_config.py. (não usado no ambiente de execução do Python 3)
  4. Exclua a pasta lib, se houver. (desnecessário com o ambiente de execução do Python 3)

Limpar

Geral

Se você já terminou, recomendamos que desative o aplicativo do App Engine para evitar cobranças. No entanto, se você quiser testar ou experimentar um pouco mais, a plataforma do App Engine tem uma cota sem custo financeiro. Portanto, desde que você não exceda esse nível de uso, não haverá cobranças. Isso é para computação, mas também pode haver cobranças por serviços relevantes do App Engine. Consulte a página de preços para mais informações. Se essa migração envolver outros serviços do Cloud, eles serão cobrados separadamente. Em ambos os casos, se aplicável, consulte a seção "Específico para este codelab" abaixo.

Para divulgação completa, a implantação em uma plataforma de computação sem servidor do Google Cloud, como o App Engine, gera pequenos custos de build e armazenamento. O Cloud Build tem uma cota sem custo financeiro, assim como o Cloud Storage. O armazenamento dessa imagem usa parte dessa cota. No entanto, talvez você more em uma região que não tenha esse nível sem custo financeiro. Por isso, fique de olho no uso do armazenamento para minimizar possíveis custos. As "pastas" específicas do Cloud Storage que você deve analisar incluem:

  • 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
  • Os links de armazenamento acima dependem do seu PROJECT_ID e da *LOC*ação, por exemplo, "us" se o app estiver hospedado nos EUA.

Por outro lado, se você não quiser continuar com este aplicativo ou outros codelabs de migração relacionados e quiser excluir tudo, encerre o projeto.

Específico para este codelab

Os serviços listados abaixo são exclusivos deste codelab. Consulte a documentação de cada produto para mais informações:

Se você migrou do módulo 15 para o 16, ainda terá dados no Blobstore. Por isso, incluímos as informações de preços acima.

Próximas etapas

Além deste tutorial, outros módulos de migração que se concentram em deixar de usar os serviços integrados legados incluem:

  • Módulo 2: migrar do ndb do App Engine para o Cloud NDB
  • Módulos 7 a 9: migrar das tarefas push da fila de tarefas do App Engine para o Cloud Tasks
  • Módulos 12 e 13: migrar do Memcache do App Engine para o Cloud Memorystore
  • Módulos 18 e 19: migrar da fila de tarefas do App Engine (tarefas pull) para o Cloud Pub/Sub

O App Engine não é mais a única plataforma sem servidor no Google Cloud. Se você tem um app pequeno do App Engine ou um com funcionalidade limitada e quer transformá-lo em um microsserviço independente, ou se quer dividir um app monolítico em vários componentes reutilizáveis, esses são bons motivos para considerar a migração para o Cloud Functions. Se a conteinerização se tornou parte do fluxo de trabalho de desenvolvimento do aplicativo, principalmente se ele consiste em um pipeline de CI/CD (integração contínua/entrega ou implantação contínua), considere migrar para o Cloud Run. Esses cenários são abordados nos seguintes módulos:

  • Migrar do App Engine para o Cloud Functions: consulte o Módulo 11
  • Migrar do App Engine para o Cloud Run: consulte o Módulo 4 para conteinerizar seu app com o Docker ou o Módulo 5 para fazer isso sem contêineres, conhecimento do Docker ou Dockerfiles.

A mudança para outra plataforma sem servidor é opcional, e recomendamos que você considere as melhores opções para seus apps e casos de uso antes de fazer qualquer alteração.

Independentemente do módulo de migração que você considerar em seguida, todo o conteúdo da Estação de migração sem servidor (codelabs, vídeos, código-fonte [quando disponível]) pode ser acessado no repositório de código aberto. O README do repositório também fornece orientações sobre quais migrações considerar e qualquer "ordem" relevante de módulos de migração.

7. Outros recursos

Problemas/feedback do codelab

Se você encontrar problemas com este codelab, pesquise seu problema antes de preenchê-lo. Links para pesquisar e criar novos problemas:

Recursos de migração

Os links para as pastas do repositório do módulo 15 (START) e do módulo 16 (FINISH) estão na tabela abaixo. Eles também podem ser acessados no repositório de todas as migrações de codelab do App Engine, em que é possível clonar ou fazer o download de um arquivo ZIP.

Codelab

Python 2

Python 3

Módulo 15

código

N/A

Módulo 16 (este codelab)

código

(igual ao Python 2)

Recursos on-line

Confira abaixo os recursos on-line que podem ser relevantes para este tutorial:

Blobstore do App Engine e Cloud Storage

Plataforma do App Engine

Outras informações do Cloud

Python

Vídeos

Licença

Este conteúdo está sob a licença Atribuição 2.0 Genérica da Creative Commons.