Este guia aborda como migrar do Blobstore do App Engine para o Cloud Storage.
O Cloud Storage é semelhante ao Blobstore do App Engine, uma vez que pode usar o Cloud Storage para publicar objetos de dados grandes (blobs), como ficheiros de vídeo ou de imagem, e permitir que os utilizadores carreguem ficheiros de dados grandes. Embora o App Engine Blobstore seja acessível apenas através dos serviços agrupados legados do App Engine, o Cloud Storage é um produto autónomo Google Cloud que é acedido através das bibliotecas cliente da Google Cloud. O Cloud Storage oferece à sua app uma solução de armazenamento de objetos mais moderna e dá-lhe a flexibilidade de migrar para o Cloud Run ou outra plataforma de alojamento de Google Cloud apps Google Cloud mais tarde.
Para Google Cloud projetos criados após novembro de 2016, o Blobstore usa contentores do Cloud Storage nos bastidores. Isto significa que, quando migrar a sua app para o Cloud Storage, todos os objetos e autorizações existentes nesses contentores do Cloud Storage permanecem inalterados. Também pode começar a aceder a esses contentores existentes através das bibliotecas cliente do Google Cloud para o 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 tem uma dependência de webapp.
- A Blobstore API para Python 3 usa classes de utilidade para usar controladores do Blobstore.
- Para o Blobstore, o número máximo de ficheiros que podem ser carregados para o Blobstore é 500. Não existe limite para o número de objetos que pode criar num contentor do Cloud Storage.
O Cloud Storage não suporta:
- Classes de processadores do Blobstore
- Objetos do Blobstore
Semelhanças entre o Cloud Storage e o Blobstore do App Engine:
- Capacidade de ler e escrever objetos de dados grandes num ambiente de tempo de execução, bem como armazenar e publicar objetos de dados grandes estáticos, como filmes, imagens ou outro conteúdo estático. O limite de tamanho do objeto para o Cloud Storage é de 5 TiB.
- Permite-lhe armazenar objetos num contentor do Cloud Storage.
- Ter um nível gratuito.
Antes de começar
- Deve rever e compreender os preços e as quotas do Cloud Storage:
- O Cloud Storage é um serviço pago e tem os seus próprios preços para o armazenamento de dados com base na classe de armazenamento dos seus dados e na localização dos seus contentores.
- As cotas do Cloud Storage têm algumas diferenças das cotas e dos limites do Blobstore do App Engine, o que pode afetar as cotas de pedidos do App Engine.
- Ter uma app do App Engine Python 2 ou Python 3 existente que esteja a usar o Blobstore.
- Os exemplos neste guia mostram uma app que migra para o
Cloud Storage através da framework Flask. Tenha em atenção que pode usar qualquer framework Web, incluindo permanecer no
webapp2
, quando migrar para o Cloud Storage.
Vista geral
A um nível elevado, o processo de migração para o Cloud Storage a partir do Blobstore do App Engine consiste nos seguintes passos:
- Atualize os ficheiros de configuração
- Atualize a sua app Python:
- Atualize a sua framework Web
- Importe e inicialize o Cloud Storage
- Atualize os controladores do Blobstore
- Opcional: atualize o seu modelo de dados se usar o Cloud NDB ou o App Engine NDB
- Teste e implemente a sua app
Atualize os ficheiros de configuração
Antes de modificar o código da aplicação para mudar do Blobstore para o Cloud Storage, atualize os ficheiros de configuração para usar a biblioteca do Cloud Storage.
Atualize o ficheiro
app.yaml
. Siga as instruções para a sua versão do Python:Python 2
Para apps Python 2:
- Remova a secção
handlers
e todas as dependências da app Web desnecessárias na secçãolibraries
. - Se usar as bibliotecas cliente da Google Cloud, adicione as versões mais recentes das bibliotecas
grpcio
esetuptools
. - Adicione a biblioteca
ssl
, uma vez que é necessária para o Cloud Storage.
Segue-se um exemplo de um ficheiro
app.yaml
com as alterações 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 Python 3, elimine todas as linhas, exceto o elemento
runtime
. Por exemplo:runtime: python310 # or another support version
O tempo de execução do Python 3 instala bibliotecas automaticamente, pelo que não tem de especificar bibliotecas incorporadas do tempo de execução do Python 2 anterior. Se a sua app Python 3 estiver a usar outros serviços agrupados antigos quando migrar para o Cloud Storage, deixe o ficheiro
app.yaml
tal como está.- Remova a secção
Atualize o ficheiro
requirements.txt
. Siga as instruções para a sua versão do Python:Python 2
Adicione as bibliotecas de cliente da nuvem para o Cloud Storage à sua lista de dependências no ficheiro
requirements.txt
.google-cloud-storage
Em seguida, execute
pip install -t lib -r requirements.txt
para atualizar a lista de bibliotecas disponíveis para a sua app.Python 3
Adicione as bibliotecas de cliente do Google Cloud para o Cloud Storage à sua lista de dependências no ficheiro
requirements.txt
.google-cloud-storage
O App Engine instala automaticamente estas dependências durante a implementação da app no runtime do Python 3. Por isso, elimine a pasta
lib
, se existir.Para apps Python 2, se a sua app estiver a usar bibliotecas incorporadas ou copiadas, tem de especificar esses caminhos no ficheiro
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 a sua app Python
Depois de modificar os ficheiros de configuração, atualize a sua app Python.
Atualize a sua framework Web Python 2
Para apps Python 2 que usam a framework webapp2
, é recomendável migrar
da framework webapp2
desatualizada. Consulte o
cronograma de compatibilidade do tempo de execução
para saber a data de fim da compatibilidade do Python 2.
Pode migrar para outra framework Web, como o Flask, o Django ou o WSGI. Uma vez que o armazenamento na nuvem exclui dependências de webapp2
e os controladores do Blobstore não são suportados, pode eliminar ou substituir outras bibliotecas relacionadas com a app Web.
Se optar por continuar a usar webapp2
, tenha em atenção que os exemplos ao longo deste guia usam o Cloud Storage com o Flask.
Se planeia usar os serviços do Google Cloud além do armazenamento na nuvem ou obter acesso às versões de tempo de execução mais recentes, deve considerar atualizar a sua app para o tempo de execução do Python 3. Para mais informações, consulte a vista geral da migração do Python 2 para o Python 3.
Importe e inicialize o Cloud Storage
Modifique os ficheiros da aplicação atualizando as linhas de importação e inicialização:
Remova as declarações de importação do Blobstore, como as seguintes:
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
Adicione as declarações de importação para o Cloud Storage e as bibliotecas de autenticação da Google, 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 de autenticação Google é necessária para obter o mesmo ID do projeto que foi usado no Blobstore para o Cloud Storage. Importe outras bibliotecas, como a Cloud NBD, se aplicável à sua app.
Crie um novo cliente para o Cloud Storage e especifique o contentor que é usado no Blobstore. Por exemplo:
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
Para Google Cloud projetos posteriores a novembro de 2016, o Blobstore escreve num contentor do Cloud Storage com o nome do URL da sua app e segue o formato
PROJECT_ID.appspot.com
. Usa a autenticação Google para obter o ID do projeto para especificar o contentor do Cloud Storage que é usado para armazenar blobs no Blobstore.
Atualize os controladores do Blobstore
Uma vez que o Cloud Storage não suporta os controladores de carregamento e transferência do Blobstore, tem de usar uma combinação da funcionalidade do Cloud Storage, do módulo da biblioteca padrão, da sua framework Web e das utilidades do Python para carregar e transferir objetos (blobs) no Cloud Storage.io
O exemplo seguinte demonstra como atualizar os controladores da Blobstore usando o Flask como a framework Web de exemplo:
Substitua as classes do controlador de carregamento do Blobstore por uma função de carregamento no Flask. Siga as instruções para a sua versão do Python:
Python 2
Os controladores Blobstore no Python 2 são
webapp2
classes, conforme mostrado no seguinte exemplo do Blobstore: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 carregamento da app Web por uma função de carregamento do Flask.
- Substitua o controlador de carregamento e o encaminhamento por um método
POST
do Flask decorado com o encaminhamento.
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 do Cloud Storage atualizado, a app identifica agora os artefactos de objetos pelo respetivo nome do objeto (
fname
) em vez deblob_id
. O encaminhamento também ocorre na parte inferior do ficheiro de aplicação.Para obter o objeto carregado, o método
get_uploads()
do Blobstore é substituído pelo métodorequest.files.get()
do Flask. No Flask, pode usar o métodosecure_filename()
para obter um nome sem carateres de caminho, como/
, para o ficheiro, e identificar o objeto através degcs_client.bucket(BUCKET).blob(fname)
para especificar o nome do contentor e o nome do objeto.A chamada Cloud Storage
upload_from_file()
executa o carregamento, conforme mostrado no exemplo atualizado.Python 3
A classe do controlador de carregamento no Blobstore para Python 3 é uma classe de utilidade e requer a utilização do dicionário WSGI
environ
como um parâmetro de entrada, conforme mostrado no exemplo do Blobstore seguinte: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 Blobstore 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 do Cloud Storage atualizado, a app identifica agora os artefactos de objetos pelo respetivo nome do objeto (
fname
) em vez deblob_id
. O encaminhamento também ocorre na parte inferior do ficheiro de aplicação.Para obter o objeto carregado, o método
get_uploads()
do Blobstore é substituído pelo métodorequest.files.get()
do Flask. No Flask, pode usar o métodosecure_filename()
para obter um nome sem carateres de caminho, como/
, para o ficheiro, e identificar o objeto através degcs_client.bucket(BUCKET).blob(fname)
para especificar o nome do contentor e o nome do objeto.O método Cloud Storage
upload_from_file()
executa o carregamento conforme mostrado no exemplo atualizado.Substitua as classes do controlador de transferência do Blobstore por uma função de transferência no Flask. Siga as instruções para a sua versão do Python:
Python 2
O exemplo de controlador de transferência seguinte mostra a utilização da classe
BlobstoreDownloadHandler
, que usa o 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 Blobstore para usar o métododownload_as_bytes()
do Cloud Storage. - Altere o encaminhamento 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 do Cloud Storage atualizado, o Flask decora a rota na função Flask e identifica o objeto através de
'/view/<path:fname>'
. O Cloud Storage identifica o objeto pelo nome do objeto e pelo nome do contentor, e usa o métododownload_as_bytes()
para transferir o objeto como bytes, em vez de usar o métodosend_blob
do Blobstore.blob
Se o artefacto não for encontrado, a app devolve um erro HTTP404
.Python 3
Tal como o controlador de carregamento, a classe do controlador de transferência no Blobstore para Python 3 é uma classe de utilidade e requer a utilização do dicionário WSGI
environ
como um parâmetro de entrada, conforme mostrado no seguinte exemplo do Blobstore: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 método
send_blob(request.environ, blob_key)
do Blobstore 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 do Cloud Storage atualizado,
blob_key
é substituído porfname
e o Flask identifica o objeto através do URL'/view/<path:fname>'
. O métodogcs_client.bucket(BUCKET).blob(fname)
é usado para localizar o nome do ficheiro e o nome do contentor. O métododownload_as_bytes()
do Cloud Storage transfere o objeto como bytes, em vez de usar o métodosend_blob()
do Blobstore.- Atualize o método
Se a sua app usar um controlador principal, substitua a classe
MainHandler
pela funçãoroot()
no Flask. Siga as instruções para a sua versão do Python:Python 2
Segue-se um exemplo de utilização da classe
MainHandler
do Blobstore: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)
, uma vez que o Flask processa o encaminhamento por si. - Simplifique o código do Blobstore com o Flask.
- Remova o encaminhamento da app Web 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 usou o Flask, não tem uma classe
MainHandler
, mas a função raiz do Flask tem de ser atualizada se o blobstore for usado. O exemplo seguinte 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 obter 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
Teste e implemente a sua app
O servidor de desenvolvimento local permite-lhe testar a execução da sua app, mas não vai poder testar o Cloud Storage até implementar uma nova versão, porque todos os pedidos do Cloud Storage têm de ser enviados através da Internet para um contentor do Cloud Storage real. Consulte o artigo Testar e implementar a sua aplicação para saber como executar a sua aplicação localmente. Em seguida, implemente uma nova versão para confirmar que a app tem o mesmo aspeto que antes.
Apps que usam o App Engine NDB ou o Cloud NDB
Tem de atualizar o modelo de dados do Datastore se a sua app usar o NDB do App Engine ou o NDB do Google Cloud para incluir propriedades relacionadas com o Blobstore.
Atualize o modelo de dados
Uma vez que as propriedades BlobKey
do NDB não são suportadas pelo Cloud Storage, tem de modificar as linhas relacionadas com o Blobstore para usar equivalentes incorporados do NDB, frameworks Web ou noutro local.
Para atualizar o modelo de dados:
Encontre as linhas que usam
BlobKey
no modelo de dados, como as seguintes: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()
Substituir
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 também estiver a atualizar do App Engine NDB para o Cloud NDB durante a migração, consulte o guia de migração do Cloud NDB para obter orientações sobre como refatorar o código NDB para usar gestores de contexto do Python.
Retrocompatibilidade para o modelo de dados do Datastore
Na secção anterior, a substituição de ndb.BlobKeyProperty
por
ndb.StringProperty
tornou a app incompatível com versões anteriores, o que significa que a app
não vai poder processar entradas mais antigas criadas pelo Blobstore. Se precisar de reter 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.
A partir dos exemplos nas secções anteriores, faça as seguintes alterações:
Crie dois campos de propriedades separados quando definir o modelo de dados. Use a propriedade
file_blob
para identificar objetos criados pelo Blobstore e a propriedadefile_gcs
para identificar objetos 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()
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()
Altere o código para que
file_gcs
seja usado para entradas recentes. Por 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 seguinte mostra a utilização de extração, transformação e carregamento (ETL) para percorrer todas as visitas e usa os dados de visitante e data/hora para verificar se existe
file_gcs
oufile_gcs
: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 a função
fetch_visits()
à 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 uma app Python 2 para o Cloud Storage, compare o exemplo de código do Blobstore para Python 2 e o exemplo de código do Cloud Storage no GitHub.
- Para ver um exemplo de como migrar uma app Python 3 para o Cloud Storage, compare o exemplo de código do Blobstore para Python 3 e o exemplo de código do Cloud Storage no GitHub.
O que se segue?
- Para um tutorial prático, consulte o codelab Migre do Blobstore do App Engine para o Cloud Storage para Python.
- Saiba como armazenar e fornecer ficheiros estáticos a partir do Cloud Storage.
- Consulte a documentação do Cloud Storage para ver mais detalhes.