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

1. Visão geral

A série de codelabs da Serverless Migration Station (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 a migração de 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 mais fácil para versões de linguagem mais recentes. 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 em outro lugar, 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 Blobstore do App Engine
  • Armazenar uploads de usuários no serviço Blobstore
  • Preparar a próxima etapa para migrar 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 NDB) para o Cloud Storage (e Cloud NDB). O processo de migração envolve a substituição de dependências dos serviços legados incluídos 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 webapp original. Por isso, o app de exemplo usa o framework 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 "visitas" de usuários finais 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 pede 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. Independente 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. Outra novidade é que 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 prosseguirmos com a parte principal do tutorial, vamos configurar o nosso projeto, obter o código e, em seguida, implantar o aplicativo de referência para saber que começamos a trabalhar com o código em funcionamento.

1. Configurar projeto

Se você já implantou o app do Módulo 15, recomendamos reutilizar o mesmo projeto (e código). Se preferir, crie um novo projeto ou reutilize outro. 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 para este codelab é ter um app de exemplo do Módulo 15. Se você não tiver um, acesse a pasta "START" do Módulo 15 (link abaixo). Este codelab orienta você em cada etapa, concluindo com um código semelhante ao da pasta "FINISH" do Módulo 16.

O diretório dos arquivos INICIAIS do Módulo 15 deve ter a seguinte aparência:

$ 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 a seleção de um bucket do Cloud Storage diferente do padrão de um URL atribuído a um app com base no ID do projeto: PROJECT_ID.appspot.com. Esse arquivo não tem nenhuma função neste codelab (Módulo 16), além de que técnicas de migração semelhantes podem ser aplicadas a ele, se desejado.

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 está funcionando. A página inicial recebe os usuários com um formulário que solicita o upload de um arquivo de artefato de visita e uma opção, um botão "pular", para desativar:

f5b5f9f19d8ae978.png

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

f5ac6b98ee8a34cb.png

As visitas com um artefato têm um link "Visualizar" à direita do carimbo de data/hora para mostrar (ou baixar) o artefato. Depois de confirmar a funcionalidade do app, você 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 ação para a versão atualizada do nosso app. As tarefas necessárias são:

  1. Atualize as bibliotecas integradas de terceiros necessárias em app.yaml e deixe 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 integradas e de terceiros não integradas.

app.yaml

Edite o arquivo app.yaml atualizando a seção libraries. jinja2 foi removido, e grpcio, setuptools e ssl foram adicionados. Escolha a versão mais recente disponível para todas as três bibliotecas. Adicione também a diretiva runtime do Python 3, mas com um comentário. Quando terminar, ele vai ficar assim (se você selecionou Python 3.9):

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

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

libraries:
- name: jinja2
  version: latest

AFTER:

#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 se referem principalmente às bibliotecas integradas do Python 2 disponíveis nos servidores do App Engine (para que você não precise agrupar 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, forem usadas, o grpcio e o setuptools serão necessários. Por fim, o próprio Cloud Storage exige 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, exigindo 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 obtidas. Depois de fazer upgrade para o Python 3, não é mais necessário fazer o pacote automático.

appengine_config.py

Adicione um arquivo appengine_config.py compatível com 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 concluídas agora devem ser semelhantes ou idênticas às listadas na seção Instalar bibliotecas para apps 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 foi 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 da 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 app (PROJECT_ID.appspot.com). Como estamos migrando para a biblioteca de cliente do Cloud Storage, google.auth é usado para receber o ID do projeto e especificar 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.

AFTER:

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 de webapp2_extras. Isso é desnecessário porque o Jinja2 vem com o Flask e é o mecanismo de modelos padrão. Portanto, remova-o.

No lado do Módulo 16, crie instâncias de objetos que não tínhamos no app mais antigo. Isso inclui inicializar o app 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 as capturas de tela antes e depois da implementação dessas 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))

AFTER:

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 é quase totalmente compatível com o NDB do App Engine. Uma diferença já abordada é a necessidade de um cliente de API. Outra diferença é que o último exige que o acesso ao Datastore seja controlado pelo gerenciador de contexto Python do cliente de API. Isso significa que todas as chamadas de acesso ao Datastore usando 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, por exemplo, 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)

AFTER:

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 ilustrada 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 manipuladores webapp são substituídos pela funcionalidade do Cloud Storage, do Flask e dos 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)

AFTER:

@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 presente, e None caso contrário (o usuário desativou o envio de um arquivo).
  • Os manipuladores do Blobstore abstraíram o processo de upload dos usuários, mas o Cloud Storage não faz isso. Assim, é 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 manipulador decorado.
  • Os dois manipuladores encerram a funcionalidade redirecionando para a página inicial ( / ), 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 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)

AFTER:

@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 de 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 lado do 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 única chamada de método send_blob() 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 acontecem no gerenciador principal. Os métodos de verbo HTTP webapp2 são substituídos por uma única função que combina a funcionalidade 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)

AFTER:

@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 métodos get() e post() separados, 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, o que não é possível em webapp2.

Confira uma representação ilustrada desse segundo e último conjunto de mudanças no main.py:

5ec38818c32fec2.png

(opcional) "Melhoria" da compatibilidade com versões anteriores

Portanto, 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 por nome em vez de BlobKey, o app concluído do Módulo 16 não poderá acessar os arquivos do Blobstore. 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" para oferecer suporte a 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 repositório de blobs, 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()

AFTER:

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)

AFTER:

@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 dos dois). Se houver um arquivo disponível, escolha o que existe e use esse identificador (BlobKey para arquivos criados no Blobstore ou nome do arquivo para arquivos criados no Cloud Storage). Quando dizemos "arquivos criados pelo Cloud Storage", nos referimos a arquivos criados usando a biblioteca de cliente do Cloud Storage. O Blobstore também grava no Cloud Storage, mas, nesse caso, seriam arquivos criados pelo Blobstore.

Agora, o mais importante: o que é essa função etl_visits() usada para normalizar ou ETL (extrair, transformar e carregar) os 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, ele vai parecer o que você esperava: o código passa por todas as visitas e, para cada uma delas, usa os dados do visitante e do carimbo de data/hora exatamente como estão. Depois, verifica se file_gcs ou file_blob existe e, se sim, escolhe um deles (ou None se nenhum dos dois 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 você estiver fazendo a transição e quiser arquivos de suporte criados pelo Blobstore e pelo Cloud Storage, confira main-migrate.py como um exemplo de como lidar com esse cenário para ajudar você a planejar migrações para seus próprios apps. Ao fazer migrações complexas, é provável que surjam casos especiais. Portanto, este exemplo mostra uma maior afinidade com a modernização de apps reais com dados reais.

6. Resumo/limpeza

Esta seção conclui o codelab implantando o app e verificando se ele funciona conforme o esperado e em qualquer saída refletida. Depois da validação do app, faça as etapas de limpeza e considere as próximas etapas.

Implantar e verificar o aplicativo

Antes de fazer a nova implantação do app, execute pip install -t lib -r requirements.txt para colocar as bibliotecas de terceiros autoempacotadas na pasta "lib". Se 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 forma idêntica ao app do módulo 15. A tela do formulário é assim:

f5b5f9f19d8ae978.png

A página de visitas mais recentes tem esta aparência:

f5ac6b98ee8a34cb.png

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

"Migração" do 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 isso como um app Python 3, execute as seguintes etapas:

  1. Remova o 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 ela existir. (desnecessário com o ambiente de execução do Python 3)

Limpar

Geral

Se você terminou por enquanto, recomendamos que desative o app do App Engine para evitar cobranças. No entanto, se você quiser testar ou experimentar mais, a plataforma do App Engine tem uma cota sem custo financeiro. Portanto, enquanto você não exceder esse nível de uso, não vai receber 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 qualquer caso, se aplicável, consulte a seção "Específico para este codelab" abaixo.

Para total transparência, a implantação em uma plataforma de computação sem servidor do Google Cloud, como o App Engine, gera custos mínimos de build e armazenamento. O Cloud Build e o Cloud Storage têm cotas sem custo financeiro próprias. O armazenamento dessa imagem usa parte dessa cota. No entanto, talvez você more em uma região que não tem um 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ê precisa 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 da sua PROJECT_ID e *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 completamente, desligue seu 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 migrar dos serviços agrupados legados incluem:

  • Módulo 2: migrar do App Engine ndb 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 contêinerização se tornou parte do seu fluxo de trabalho de desenvolvimento de aplicativos, principalmente se ele consistir 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 contentorizar 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. Recomendamos considerar as melhores opções para seus apps e casos de uso antes de fazer qualquer mudança.

Independente 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 oferece orientações sobre quais migrações considerar e a "ordem" relevante dos 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) podem ser encontrados na tabela abaixo. Elas também podem ser acessadas no repositório de todas as migrações de codelab do App Engine, que você pode 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 recursos on-line que podem ser relevantes para este tutorial:

Blobstore do App Engine e Cloud Storage

Plataforma do App Engine

Outras informações da nuvem

Python

Vídeos

Licença

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