Neste guia, explicamos como migrar do Blobstore do App Engine para o Cloud Storage.
O Cloud Storage é semelhante ao Blobstore do App Engine, porque você pode usá-lo para exibir objetos de dados grandes (blobs), como arquivos de vídeo ou imagem, e permitir que os usuários façam upload de arquivos de dados grandes. Enquanto o Blobstore do App Engine pode ser acessado apenas por meio dos serviços em pacote legados do App Engine, o Cloud Storage é um produto independente do Google Cloud acessado pelas Bibliotecas de cliente do Cloud. O Cloud Storage oferece ao app uma solução de armazenamento de objetos mais moderna e oferece a flexibilidade de migrar para o Cloud Run ou outra plataforma de hospedagem de apps do Google Cloud posteriormente.
Para projetos do Google Cloud criados após novembro de 2016, o Blobstore usa buckets do Cloud Storage nos bastidores. Isso significa que, quando você migra seu app para o Cloud Storage, todos os objetos e permissões atuais nesses buckets do Cloud Storage permanecem inalterados. Também é possível começar a acessar esses buckets usando as bibliotecas de cliente do Cloud para Cloud Storage.
Principais diferenças e semelhanças
O Cloud Storage exclui as seguintes dependências e limitações do Blobstore:
- A API Blobstore para Python 2 depende do webapp.
- A API Blobstore para Python 3 usa classes de utilitário para usar gerenciadores Blobstore.
- Para o Blobstore, o número máximo de arquivos que podem ser enviados para o Blobstore é 500. Não há limite para o número de objetos que podem ser criados em um bucket do Cloud Storage.
O Cloud Storage não é compatível com:
- Classes do gerenciador Blobstore
- Objetos do Blobstore
Semelhanças entre o Cloud Storage e o Blobstore no App Engine:
- Sabe ler e gravar objetos de dados grandes em um ambiente de execução, bem como armazenar e exibir objetos de dados grandes e estáticos, como filmes, imagens ou outros conteúdos estáticos. O limite de tamanho de objeto para o Cloud Storage é de 5 TiB.
- Permite armazenar objetos em um bucket do Cloud Storage.
- Ter um nível gratuito;
Antes de começar
- Analise e entenda os preços e as cotas do Cloud Storage:
- O Cloud Storage é um serviço de pagamento por utilização e tem os próprios preços de armazenamento de dados com base na classe de armazenamento dos dados e no local dos buckets.
- As cotas do Cloud Storage têm algumas diferenças em relação às cotas e limites do Blobstore, que podem afetar as cotas de solicitações do App Engine.
- Ter um aplicativo do Python 2 ou Python 3 no App Engine que esteja usando o Blobstore.
- Os exemplos neste guia mostram um app que migra para o Cloud Storage usando o framework Flask. É possível usar qualquer
framework da Web, incluindo a permanência em
webapp2
, ao migrar para o Cloud Storage.
Visão geral
Em geral, o processo para migrar do Cloud Storage para o App Engine do repositório de blobs consiste nas seguintes etapas:
- Atualizar os arquivos de configuração
- Atualize seu aplicativo Python:
- Atualizar seu framework da Web
- Importar e inicializar o Cloud Storage
- Atualizar gerenciadores do repositório de blobs
- Opcional: atualizar seu modelo de dados se estiver usando o Cloud NDB ou o App Engine
- Testar e implantar seu aplicativo
Atualizar os arquivos de configuração
Antes de modificar o código do aplicativo para migrar do repositório de blobs para o Cloud Storage, atualize seus arquivos de configuração para usar a biblioteca do Cloud Storage.
Atualize o arquivo
app.yaml
. Siga as instruções para sua versão do Python:Python 2
Para aplicativos Python 2:
- Remova a seção
handlers
e todas as dependências de webapp desnecessárias na seçãolibraries
. - Se você usa as bibliotecas de cliente do Cloud, adicione as versões mais recentes das
bibliotecas
grpcio
esetuptools
. - Adicione a biblioteca
ssl
, já que isso é exigido pelo Cloud Storage.
Veja a seguir um exemplo de arquivo
app.yaml
com as mudanças feitas: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
Python 3
Para apps do Python 3, exclua todas as linhas, exceto o elemento
runtime
. Exemplo:runtime: python310 # or another support version
O ambiente de execução do Python 3 instala bibliotecas automaticamente. Portanto, não é necessário especificar bibliotecas integradas do ambiente de execução anterior do Python 2. Se o app Python 3 estiver usando outros serviços em pacote legados ao migrar para o Cloud Storage, deixe o arquivo
app.yaml
como está.- Remova a seção
Atualize o arquivo
requirements.txt
. Siga as instruções para sua versão do Python:Python 2
Adicione as bibliotecas de cliente do Cloud para o Cloud Storage à sua lista de dependências no arquivo
requirements.txt
.google-cloud-storage
Em seguida, execute
pip install -t lib -r requirements.txt
para atualizar a lista de bibliotecas disponíveis para seu app.Python 3
Adicione as bibliotecas de cliente do Cloud para Cloud Storage à sua lista de dependências no arquivo
requirements.txt
.google-cloud-storage
O App Engine instala automaticamente essas dependências durante a implantação do app no ambiente de execução do Python 3. Portanto, exclua a pasta
lib
, se houver uma.Para apps do Python 2, se o app estiver usando bibliotecas integradas ou copiadas, você precisa especificar esses caminhos no arquivo
appengine_config.py
: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)
Atualize seu aplicativo Python
Depois de modificar os arquivos de configuração, atualize o app Python.
Atualizar seu framework da Web em Python 2
Para apps do Python 2 que usam o framework webapp2
, é recomendável migrar
do framework webapp2
desatualizado. Consulte o
Cronograma de suporte do ambiente de execução
para ver a data de término do suporte ao Python 2.
É possível migrar para outro framework da Web, como
Flask, Django ou WSGI. Como o Cloud Storage exclui dependências em webapp2
, e os gerenciadores do repositório de blobs não são compatíveis, é possível excluir ou substituir outras bibliotecas relacionadas ao app.
Se você quiser continuar usando webapp2
, os exemplos neste guia
usam o Cloud Storage com Flask.
Se você planeja usar os serviços do Google Cloud além do Cloud Storage ou tiver acesso às versões mais recentes do ambiente de execução, considere fazer upgrade do app para o ambiente de execução do Python 3. Para mais informações, consulte a visão geral de migração do Python 2 para o Python 3.
Importar e inicializar o Cloud Storage
Modifique os arquivos do aplicativo atualizando as linhas de importação e inicialização:
Remova instruções de importação do repositório de blobs, como a seguinte:
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
Adicione as instruções de importação do Cloud Storage e das bibliotecas do Google Authentication, como as seguintes:
import io from flask import (Flask, abort, redirect, render_template, request, send_file, url_for) from google.cloud import storage import google.auth
A biblioteca do Google Authentication é necessária para acessar o mesmo ID do projeto usado no repositório de blobs para o Cloud Storage. Importe outras bibliotecas, como o Cloud NBD, se aplicável ao seu app.
Criar um novo cliente para o Cloud Storage e especificar o bucket usado no repositório de blobs. Exemplo:
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
Para projetos do Google Cloud após novembro de 2016, o repositório de blobs grava em um bucket do Cloud Storage nomeado com base no URL do aplicativo e segue o formato de
PROJECT_ID.appspot.com
. Use a autenticação do Google para receber o ID do projeto e especificar o bucket do Cloud Storage usado para armazenar blobs no repositório de blobs.
Atualizar gerenciadores do repositório de blobs
Como o Cloud Storage não é compatível com os gerenciadores de upload e download do repositório de blobs, é necessário usar uma combinação da funcionalidade do Cloud Storage, do módulo da biblioteca padrão io
, do framework da Web e dos utilitários do Python para fazer upload
e fazer o download de objetos (blobs) no Cloud Storage.
Veja a seguir como atualizar os gerenciadores do repositório de blobs usando o Flask como o framework da Web de exemplo:
Substitua as classes do gerenciador de upload do repositório de blobs por uma função de upload no Flask. Siga as instruções para sua versão do Python:
Python 2
Os gerenciadores repositório de blobs no Python 2 são classes
webapp2
, conforme mostrado no exemplo a seguir do repositório de blobs: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) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Para usar o Cloud Storage:
- Substitua a classe de upload do app da Web por uma função de upload do Flask.
- Substitua o gerenciador de upload e o roteamento por um método
POST
do Flask decorado com roteamento.
Exemplo de código atualizado:
@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)
No exemplo de código atualizado do Cloud Storage, o app agora identifica os artefatos de objeto pelo nome do objeto (
fname
) em vez deblob_id
. O roteamento também ocorre na parte inferior do arquivo do aplicativo.Para receber o objeto enviado, o método
get_uploads()
do repositório de blobs é substituído pelo métodorequest.files.get()
do Flask. No Flask, é possível usar o métodosecure_filename()
para conseguir um nome sem caracteres de caminho, como/
, para o arquivo e identificar o objeto usandogcs_client.bucket(BUCKET).blob(fname)
para especificar os nomes do bucket e do objeto.A chamada
upload_from_file()
do Cloud Storage executa o upload, conforme mostrado no exemplo atualizado.Python 3
A classe de gerenciador de upload no repositório de blobs para Python 3 é uma classe de utilitário e requer o uso do WSGI
environ
como um parâmetro de entrada, como mostrado no repositório de blobs no seguinte exemplo:class UploadHandler(blobstore.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads(request.environ) if uploads: blob_id = uploads[0].key() store_visit(request.remote_addr, request.user_agent, blob_id) return redirect('/', code=307) ... @app.route('/upload', methods=['POST']) def upload(): """Upload handler called by blobstore when a blob is uploaded in the test.""" return UploadHandler().post()
Para usar o Cloud Storage, substitua o método
get_uploads(request.environ)
do repositório de blobs pelo métodorequest.files.get()
do Flask.Exemplo de código atualizado:
@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)
No exemplo de código atualizado do Cloud Storage, o app agora identifica os artefatos de objeto pelo nome do objeto (
fname
) em vez deblob_id
. O roteamento também ocorre na parte inferior do arquivo do aplicativo.Para receber o objeto enviado, o método
get_uploads()
do repositório de blobs é substituído pelo métodorequest.files.get()
do Flask. No Flask, é possível usar o métodosecure_filename()
para conseguir um nome sem caracteres de caminho, como/
, para o arquivo e identificar o objeto usandogcs_client.bucket(BUCKET).blob(fname)
para especificar os nomes do bucket e do objeto.O método
upload_from_file()
do Cloud Storage executa o upload conforme mostrado no exemplo atualizado.Substitua as classes do gerenciador de download do repositório de blobs por uma função de download no Flask. Siga as instruções para sua versão do Python:
Python 2
O exemplo de gerenciador de download a seguir mostra o uso da classe
BlobstoreDownloadHandler
, que usa webapp2: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) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Para usar o Cloud Storage:
- Atualize o método
send_blob()
do repositório de blobs para usar o métododownload_as_bytes()
do Cloud Storage. - Mude o roteamento de webapp2 para Flask.
Exemplo de código atualizado:
@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)
No exemplo de código atualizado do Cloud Storage, o Flask decora a rota na função Flask e identifica o objeto usando
'/view/<path:fname>'
. O Cloud Storage identifica o objetoblob
pelo nome do objeto e do bucket e usa o métododownload_as_bytes()
para fazer o download do objeto como bytes, em vez de usar o métodosend_blob
. no repositório de blobs. Se o artefato não for encontrado, o app retornará um erro HTTP404
.Python 3
Assim como o gerenciador de upload, a classe do gerenciador de download no repositório de blobs para Python 3 é uma classe de utilitário e requer o uso do dicionário WSGI
environ
como um parâmetro de entrada, como mostrado no exemplo de repositório de blobs a seguir:class ViewBlobHandler(blobstore.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): if not blobstore.get(blob_key): return "Photo key not found", 404 else: headers = self.send_blob(request.environ, blob_key) # Prevent Flask from setting a default content-type. # GAE sets it to a guessed type if the header is not set. headers['Content-Type'] = None return '', headers ... @app.route('/view/<blob_key>') def view_photo(blob_key): """View photo given a key.""" return ViewBlobHandler().get(blob_key)
Para usar o Cloud Storage, substitua o
send_blob(request.environ, blob_key)
do repositório de blobs pelo métodoblob.download_as_bytes()
do Cloud Storage.Exemplo de código atualizado:
@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)
No exemplo de código atualizada do Cloud Storage,
blob_key
é substituído porfname
, e o Flask identifica o objeto usando o URL'/view/<path:fname>'
. O métodogcs_client.bucket(BUCKET).blob(fname)
é usado para localizar o nome do arquivo e do bucket. O métododownload_as_bytes()
do Cloud Storage faz o download do objeto como bytes, em vez de usar o métodosend_blob()
do repositório de blobs.- Atualize o método
Se o app usa um gerenciador principal, substitua a classe
MainHandler
pela funçãoroot()
no Flask. Siga as instruções para sua versão do Python:Python 2
Veja a seguir um exemplo de como usar a classe
MainHandler
do repositório de blobs: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)
Para usar o Cloud Storage:
- Remova a classe
MainHandler(BaseHandler)
, já que o Flask trata o roteamento para você. - Simplificar o código do repositório de blobs com o Flask.
- Remova o roteamento de webapp no final.
Exemplo de código atualizado:
@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)
Python 3
Se você usou o Flask, você não terá uma classe
MainHandler
, mas sua função raiz do Flask precisará ser atualizada se o repositório de blobs for usado. O exemplo a seguir usa a funçãoblobstore.create_upload_url('/upload')
:@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = blobstore.create_upload_url('/upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
Para usar o Cloud Storage, substitua a função
blobstore.create_upload_url('/upload')
pelo métodourl_for()
do Flask para conseguir o URL da funçãoupload()
.Exemplo de código atualizado:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') # Updated to use url_for else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
- Remova a classe
Testar e implantar seu aplicativo
Com o servidor de desenvolvimento local, é possível testar se o app é executado, mas não é possível testar o Cloud Storage até que uma nova versão seja implantada, já que todas as solicitações do Cloud Storage precisam ser enviadas pela Internet para um bucket do Cloud Storage. Consulte Como testar e implantar o aplicativo para saber como executá-lo localmente. Em seguida, implante uma nova versão para confirmar que o app aparece da mesma forma que antes.
Apps que usam o App Engine NDB ou o Cloud NDB
É preciso atualizar o modelo de dados do Datastore se o app usa o App Engine NDB ou o Cloud NDB para incluir propriedades relacionadas ao repositório de blobs.
Atualizar o modelo de dados
Como as propriedades BlobKey
do NDB não são compatíveis com o Cloud Storage, é necessário modificar as linhas relacionadas ao repositório de blobs para usar equivalentes integrados do NDB, frameworks da Web ou outros.
Para atualizar o modelo de dados, faça o seguinte:
Encontre as linhas que usam
BlobKey
no modelo de dados, como a seguinte: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()
Substitua
ndb.BlobKeyProperty()
porndb.StringProperty()
: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() # Modified from ndb.BlobKeyProperty()
Se você também estiver fazendo upgrade do App Engine NDB para o Cloud NDB durante a migração, consulte o guia de migração do Cloud NDB para ver orientações sobre como refatorar o código do NDB para usar gerenciadores de contexto do Python.
Compatibilidade com versões anteriores do modelo de dados do Datastore
Na seção anterior, a substituição de ndb.BlobKeyProperty
por
ndb.StringProperty
tornou o app incompatível com versões anteriores, o que significa que o app
não poderá processar entradas mais antigas criadas pelo repositório de blobs. Se você precisar
manter dados antigos, crie um campo adicional para novas entradas do
Cloud Storage em vez de atualizar o campo ndb.BlobKeyProperty
e crie uma
função para normalizar os dados.
Nos exemplos das seções anteriores, faça as seguintes mudanças:
Crie dois campos de propriedade separados ao definir seu modelo de dados. Use a propriedade
file_blob
para identificar objetos criados no repositório de blobs e a propriedadefile_gcs
para identificar objetos criados no 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()
Encontre as linhas que fazem referência a novas visitas, como as seguintes:
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()
Mude o código para que o
file_gcs
seja usado para entradas recentes. Exemplo: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() # change file_blob to file_gcs for new requests
Crie uma nova função para normalizar os dados. O exemplo a seguir mostra o uso de extração, transformação e valor baixo (ETL) para repetir todas as visitas e usa os dados do visitante e do carimbo de data/hora para verificar se
file_gcs
oufile_gcs
. existe: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]
Encontre a linha que faz referência à função
fetch_visits()
:@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)
Una o
fetch_visits()
com a funçãoetl_visits()
, por exemplo:@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)) # etl_visits wraps around fetch_visits return render_template('index.html', **context)
Exemplos
- Para ver um exemplo de como migrar um app do Python 2 para o Cloud Storage, compare o exemplo de código do repositório de blobs para Python 2 e o mesmo exemplo de código do Cloud Storage (links em inglês) no GitHub.
- Para ver um exemplo de como migrar um app do Python 3 para o Cloud Storage, compare o mesmo exemplo de código do repositório de blobs para Python 3 e o mesmo exemplo de código do Cloud Storage no GitHub.
A seguir
- Para ver um tutorial prático, consulte o codelab Migrar do repositório de blobs do App Engine para o Cloud Storage para Python.
- Saiba como armazenar e disponibilizar arquivos estáticos do Cloud Storage.
- Consulte a documentação do Cloud Storage para ver mais detalhes.