Von App Engine Blobstore zu Cloud Storage migrieren (Modul 16)

1. Übersicht

Die Codelabs der Reihe „Serverless Migration Station“ (selbstgesteuerte, praktische Anleitungen) und die zugehörigen Videos sollen serverlosen Google Cloud-Entwicklern helfen, ihre Anwendungen zu modernisieren. Dazu werden sie durch eine oder mehrere Migrationen geführt, bei denen es in erster Linie darum geht, von Legacy-Diensten wegzukommen. Dadurch werden Ihre Apps portierbarer und Sie erhalten mehr Optionen und Flexibilität. Sie können eine größere Auswahl an Cloud-Produkten einbinden und darauf zugreifen und einfacher auf neuere Sprachversionen upgraden. Die Reihe konzentriert sich zwar anfangs auf die ersten Cloud-Nutzer, vor allem auf App Engine-Entwickler (Standardumgebung), ist aber breit genug, um auch andere serverlose Plattformen wie Cloud Functions und Cloud Run oder andere Plattformen einzubeziehen, sofern zutreffend.

In diesem Codelab erfahren Sie, wie Sie von App Engine Blobstore zu Cloud Storage migrieren. Außerdem gibt es implizite Migrationen von:

Weitere Informationen finden Sie in den entsprechenden Migrationsmodulen.

Lerninhalte

  • Verwendung der App Engine Blobstore API/Bibliothek hinzufügen
  • Nutzer-Uploads im Blobstore-Dienst speichern
  • Nächsten Schritt für die Migration zu Cloud Storage vorbereiten

Voraussetzungen

Umfrage

Wie werden Sie diese Anleitung verwenden?

Nur lesen Lesen und Übungen durchführen

Wie würden Sie Ihre Erfahrung mit Python bewerten?

Anfänger Mittelstufe Fortgeschrittene

Wie würden Sie Ihre Erfahrungen mit Google Cloud-Diensten bewerten?

Anfänger Mittelstufe Fortgeschritten

2. Hintergrund

In diesem Codelab wird mit der Beispiel-App aus Modul 15 begonnen und gezeigt, wie Sie von Blobstore (und NDB) zu Cloud Storage (und Cloud NDB) migrieren. Bei der Migration müssen Sie Abhängigkeiten von den gebündelten Legacy-Diensten von App Engine ersetzen. So können Sie Ihre Anwendungen bei Bedarf auf eine andere serverlose Cloud-Plattform oder eine andere Hostingplattform migrieren.

Diese Migration erfordert etwas mehr Aufwand als die anderen Migrationen in dieser Reihe. Blobstore ist vom ursprünglichen Webapp-Framework abhängig. Deshalb wird in der Beispielanwendung das Webapp2-Framework anstelle von Flask verwendet. In dieser Anleitung werden Migrationen zu Cloud Storage, Cloud NDB, Flask und Python 3 beschrieben.

Die App registriert weiterhin Endnutzerbesuche und zeigt die zehn letzten an. Im vorherigen Codelab (Modul 15) wurde jedoch eine neue Funktion hinzugefügt, um die Verwendung von Blobstore zu ermöglichen: Die App fordert Endnutzer auf, ein Artefakt (eine Datei) hochzuladen, das ihrem Besuch entspricht. Nutzer können dies tun oder „Überspringen“ auswählen, um die Funktion zu deaktivieren. Unabhängig von der Entscheidung des Nutzers wird auf der nächsten Seite die gleiche Ausgabe wie bei früheren Versionen dieser App gerendert und die letzten Besuche werden angezeigt. Eine weitere Besonderheit ist, dass Besuche mit entsprechenden Artefakten einen Link zum Anzeigen des Artefakts enthalten. In diesem Codelab werden die oben genannten Migrationen implementiert, wobei die beschriebene Funktionalität beibehalten wird.

3. Einrichtung/Vorbereitung

Bevor wir zum Hauptteil des Tutorials kommen, richten wir unser Projekt ein, rufen den Code ab und stellen die Baseline-App bereit, damit wir wissen, dass wir mit funktionierendem Code begonnen haben.

1. Projekt einrichten

Wenn Sie die App aus Modul 15 bereits bereitgestellt haben, empfehlen wir, dasselbe Projekt (und denselben Code) wiederzuverwenden. Alternativ können Sie ein neues Projekt erstellen oder ein anderes vorhandenes Projekt wiederverwenden. Prüfen Sie, ob das Projekt ein aktives Rechnungskonto hat und App Engine aktiviert ist.

2. Beispiel-App für die Baseline abrufen

Eine der Voraussetzungen für dieses Codelab ist eine funktionierende Beispiel-App aus Modul 15. Wenn Sie sie nicht haben, können Sie sie aus dem Ordner „START“ für Modul 15 (Link unten) herunterladen. In diesem Codelab werden Sie durch jeden Schritt geführt. Am Ende erhalten Sie Code, der dem im Ordner „FINISH“ von Modul 16 ähnelt.

Das Verzeichnis der START-Dateien für Modul 15 sollte so aussehen:

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

Die Datei main-gcs.py ist eine alternative Version von main.py aus Modul 15, mit der ein Cloud Storage-Bucket ausgewählt werden kann, der sich vom Standard einer der App zugewiesenen URL basierend auf der ID des Projekts unterscheidet: PROJECT_ID.appspot.com. Diese Datei spielt in diesem Codelab (Modul 16) keine Rolle. Bei Bedarf können jedoch ähnliche Migrationsverfahren auf diese Datei angewendet werden.

3. Referenz-App (erneut) bereitstellen

Das sind die verbleibenden Schritte, die Sie jetzt ausführen müssen:

  1. gcloud-Befehlszeilentool
  2. Beispiel-App mit gcloud app deploy noch einmal bereitstellen
  3. Prüfen, ob die App problemlos in App Engine ausgeführt wird

Sobald Sie diese Schritte ausgeführt und bestätigt haben, dass Ihre App aus Modul 15 funktioniert. Auf der Startseite wird Nutzern ein Formular angezeigt, in dem sie aufgefordert werden, eine Datei mit Besuchsartefakten hochzuladen. Außerdem wird ihnen eine Option und eine Schaltfläche zum Überspringen angezeigt:

f5b5f9f19d8ae978.png

Nachdem Nutzer eine Datei hochgeladen oder den Vorgang übersprungen haben, wird in der App die bekannte Seite „Letzte Besuche“ gerendert:

f5ac6b98ee8a34cb.png

Bei Besuchen mit einem Artefakt wird rechts neben dem Zeitstempel des Besuchs ein Link zum Aufrufen (oder Herunterladen) des Artefakts angezeigt. Sobald Sie die Funktionalität der App bestätigt haben, können Sie die Migration von App Engine-Legacy-Diensten (webapp2, NDB, Blobstore) zu modernen Alternativen (Flask, Cloud NDB, Cloud Storage) durchführen.

4. Konfigurationsdateien aktualisieren

Für die aktualisierte Version unserer App sind drei Konfigurationsdateien erforderlich. Die erforderlichen Aufgaben sind:

  1. Aktualisieren Sie die erforderlichen integrierten Drittanbieterbibliotheken in app.yaml und lassen Sie die Möglichkeit einer Python 3-Migration offen.
  2. Fügen Sie ein requirements.txt hinzu, in dem alle erforderlichen Bibliotheken angegeben sind, die nicht integriert sind.
  3. Fügen Sie appengine_config.py hinzu, damit die App sowohl integrierte als auch nicht integrierte Drittanbieterbibliotheken unterstützt.

app.yaml

Bearbeiten Sie die Datei app.yaml, indem Sie den Abschnitt libraries aktualisieren. Entfernen Sie jinja2 und fügen Sie grpcio, setuptools und ssl hinzu. Wählen Sie die neueste Version aus, die für alle drei Bibliotheken verfügbar ist. Fügen Sie auch die Python 3-Anweisung runtime hinzu, aber kommentieren Sie sie aus. Wenn Sie fertig sind, sollte es so aussehen (wenn Sie Python 3.9 ausgewählt haben):

VORHER:

runtime: python27
threadsafe: yes
api_version: 1

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

libraries:
- name: jinja2
  version: latest

DANACH:

#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

Die Änderungen betreffen hauptsächlich die integrierten Python 2-Bibliotheken, die auf App Engine-Servern verfügbar sind (Sie müssen sie also nicht selbst bündeln). Wir haben Jinja2 entfernt, da es mit Flask geliefert wird, das wir „reqs.txt“ hinzufügen werden. Wenn Google Cloud-Clientbibliotheken wie die für Cloud NDB und Cloud Storage verwendet werden, sind grpcio und setuptools erforderlich. Schließlich ist die SSL-Bibliothek für Cloud Storage selbst erforderlich. Die auskommentierte Laufzeitanweisung oben ist für den Fall, dass Sie diese App zu Python 3 portieren möchten. Am Ende dieser Anleitung erfahren Sie, wie Sie dazu vorgehen.

requirements.txt

Fügen Sie eine requirements.txt-Datei hinzu, für die das Flask-Framework sowie die Cloud NDB- und Cloud Storage-Clientbibliotheken erforderlich sind. Keine dieser Bibliotheken ist integriert. Erstellen Sie die Datei mit folgendem Inhalt:

flask
google-cloud-ndb
google-cloud-storage

Für die Python 2-App Engine-Laufzeit ist das Selbstbündeln von Drittanbieterbibliotheken erforderlich, die nicht integriert sind. Führen Sie daher den folgenden Befehl aus, um diese Bibliotheken im Ordner „lib“ zu installieren:

pip install -t lib -r requirements.txt

Wenn Sie sowohl Python 2 als auch Python 3 auf Ihrem Entwicklungscomputer haben, müssen Sie möglicherweise den Befehl „pip2“ verwenden, um sicherzustellen, dass Sie die Python 2-Versionen dieser Bibliotheken erhalten. Nach dem Upgrade auf Python 3 müssen Sie die erforderlichen Bibliotheken nicht mehr selbst bündeln.

appengine_config.py

Fügen Sie eine appengine_config.py-Datei hinzu, die integrierte und nicht integrierte Drittanbieterbibliotheken unterstützt. Erstellen Sie die Datei mit folgendem Inhalt:

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)

Die gerade ausgeführten Schritte sollten den Schritten im Abschnitt Bibliotheken für Python 2-Apps installieren in der App Engine-Dokumentation ähneln oder mit ihnen identisch sein. Insbesondere sollte der Inhalt von appengine_config.py mit dem Inhalt von Schritt 5 dort übereinstimmen.

Die Arbeit an den Konfigurationsdateien ist abgeschlossen. Wir können uns also der Anwendung zuwenden.

5. Anwendungsdateien ändern

Importe

Die erste Gruppe von Änderungen für main.py umfasst das Ersetzen aller Elemente, die ersetzt werden sollen. Folgendes wird sich ändern:

  1. webapp2 wird durch Flask ersetzt
  2. Verwenden Sie anstelle von Jinja2 aus webapp2_extras das Jinja2, das mit Flask geliefert wird.
  3. App Engine Blobstore und NDB werden durch Cloud NDB und Cloud Storage ersetzt
  4. Die Blobstore-Handler in webapp werden durch eine Kombination aus dem io-Standardbibliotheksmodul, Flask und werkzeug-Dienstprogrammen ersetzt.
  5. Standardmäßig schreibt Blobstore in einen Cloud Storage-Bucket, der nach der URL Ihrer App benannt ist (PROJECT_ID.appspot.com). Da wir zur Cloud Storage-Clientbibliothek migrieren, wird google.auth verwendet, um die Projekt-ID abzurufen und genau denselben Bucket-Namen anzugeben. Sie können den Bucket-Namen ändern, da er nicht mehr fest codiert ist.

VORHER:

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

Implementieren Sie die Änderungen in der Liste oben, indem Sie den aktuellen Importbereich in main.py durch das folgende Code-Snippet ersetzen.

DANACH:

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

Initialisierung und unnötige Jinja2-Unterstützung

Der nächste Codeblock, der ersetzt werden muss, ist BaseHandler, in dem die Verwendung von Jinja2 aus webapp2_extras angegeben wird. Das ist nicht erforderlich, da Jinja2 mit Flask geliefert wird und die Standardvorlagen-Engine ist. Entfernen Sie es daher.

Auf der Seite von Modul 16 werden Objekte instanziiert, die in der älteren App nicht vorhanden waren. Dazu gehört das Initialisieren der Flask-App und das Erstellen von API-Clients für Cloud NDB und Cloud Storage. Schließlich stellen wir den Namen des Cloud Storage-Buckets wie oben beschrieben im Importbereich zusammen. Hier sehen Sie, wie die Seite vor und nach der Implementierung dieser Änderungen aussieht:

VORHER:

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

DANACH:

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

Datenspeicherzugriff aktualisieren

Cloud NDB ist weitgehend mit App Engine NDB kompatibel. Ein Unterschied, der bereits behandelt wurde, ist die Notwendigkeit eines API-Clients. Außerdem muss der Datastore-Zugriff durch den Python-Kontextmanager des API-Clients gesteuert werden. Das bedeutet, dass alle Datastore-Zugriffsaufrufe mit der Cloud NDB-Clientbibliothek nur innerhalb von Python-Blöcken with erfolgen können.

Das ist eine Änderung. Die andere besteht darin, dass Blobstore und seine Objekte, z. B. BlobKeys, von Cloud Storage nicht unterstützt werden. Ändern Sie daher file_blob in ndb.StringProperty. Unten sehen Sie die Datenmodellklasse und die aktualisierten Funktionen store_visit() und fetch_visits(), die diese Änderungen widerspiegeln:

VORHER:

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)

DANACH:

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)

Hier eine bildliche Darstellung der bisher vorgenommenen Änderungen:

a8f74ca392275822.png

Handler aktualisieren

Upload-Handler

Handler in webapp2 sind Klassen, in Flask sind sie Funktionen. Anstelle einer HTTP-Verb-Methode verwendet Flask das Verb, um die Funktion zu dekorieren. Blobstore und die zugehörigen webapp-Handler werden durch Funktionen von Cloud Storage sowie Flask und den zugehörigen Dienstprogrammen ersetzt:

VORHER:

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)

DANACH:

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

Einige Hinweise zu diesem Update:

  • Anstelle von blob_id werden Datei-Artefakte jetzt anhand des Dateinamens (fname) identifiziert, sofern vorhanden, und andernfalls anhand von None (Nutzer hat das Hochladen einer Datei deaktiviert).
  • Die Blobstore-Handler haben den Uploadprozess für die Nutzer abstrahiert. Bei Cloud Storage ist das nicht der Fall. Daher sehen Sie den neu hinzugefügten Code, mit dem das Blob-Objekt und der Speicherort (Bucket) der Datei festgelegt werden, sowie den Aufruf, mit dem der eigentliche Upload erfolgt. (upload_from_file()).
  • webapp2 verwendet eine Routingtabelle am Ende der Anwendungsdatei, während Flask-Routen in jedem dekorierten Handler zu finden sind.
  • Beide Handler schließen ihre Funktionalität ab, indem sie mit einem HTTP-Rückgabecode 307 zur Startseite ( / ) weiterleiten und dabei die POST-Anfrage beibehalten.

Download-Handler

Die Aktualisierung des Download-Handlers folgt einem ähnlichen Muster wie der Upload-Handler. Allerdings ist hier viel weniger Code zu berücksichtigen. Ersetzen Sie die Blobstore- und webapp-Funktionen durch die Cloud Storage- und Flask-Entsprechungen:

VORHER:

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)

DANACH:

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

Hinweise zu diesem Update:

  • Flask dekoriert Handler-Funktionen mit ihrem Pfad, während webapp dies in einer Routingtabelle unten tut. Achten Sie daher auf die Syntax für den Musterabgleich von webapp (('/view/([^/]+)?') im Vergleich zu Flask ('/view/<path:fname>').
  • Wie beim Upload-Handler ist auf der Cloud Storage-Seite etwas mehr Arbeit erforderlich, um die von den Blobstore-Handlern abstrahierte Funktionalität zu nutzen. Dazu gehört das Identifizieren der betreffenden Datei (des Blobs) und das explizite Herunterladen des Binärinhalts im Gegensatz zum einzelnen send_blob()-Methodenaufruf des Blobstore-Handlers.
  • In beiden Fällen wird dem Nutzer ein HTTP 404-Fehler zurückgegeben, wenn ein Artefakt nicht gefunden wird.

Haupt-Handler

Die endgültigen Änderungen an der Hauptanwendung erfolgen im Haupthandler. Die webapp2-HTTP-Verb-Methoden werden durch eine einzelne Funktion ersetzt, die ihre Funktionalität kombiniert. Ersetzen Sie die Klasse MainHandler durch die Funktion root() und entfernen Sie die Routingtabelle webapp2, wie unten gezeigt:

VORHER:

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)

DANACH:

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

Anstelle von separaten get()- und post()-Methoden handelt es sich im Wesentlichen um eine if-else-Anweisung in root(). Da root() eine einzelne Funktion ist, gibt es nur einen Aufruf zum Rendern der Vorlage für GET und POST. In webapp2 ist das nicht möglich.

Hier ist eine bildliche Darstellung dieser zweiten und letzten Änderungen an main.py:

5ec38818c32fec2.png

(optional) „Verbesserung“ der Abwärtskompatibilität

Die oben erstellte Lösung funktioniert also perfekt – aber nur, wenn Sie von Grund auf neu beginnen und keine Dateien haben, die von Blobstore erstellt wurden. Da wir die App so aktualisiert haben, dass Dateien anhand des Dateinamens anstelle von BlobKey identifiziert werden, können Blobstore-Dateien in der fertigen App aus Modul 16 nicht angezeigt werden. Mit anderen Worten: Wir haben bei dieser Migration eine nicht abwärtskompatible Änderung vorgenommen. Wir präsentieren nun eine alternative Version von main.py namens main-migrate.py (im Repository), die versucht, diese Lücke zu schließen.

Die erste „Erweiterung“ zur Unterstützung von Blobstore-Dateien ist ein Datenmodell mit einem BlobKeyProperty (zusätzlich zu einem StringProperty für Cloud Storage-Dateien):

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

Das Attribut file_blob wird verwendet, um von Blobstore erstellte Dateien zu identifizieren, während file_gcs für Cloud Storage-Dateien verwendet wird. Wenn Sie jetzt neue Besuche erstellen, wird explizit ein Wert in file_gcs anstelle von file_blob gespeichert. „store_visit“ sieht also etwas anders aus:

VORHER:

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

DANACH:

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

Wenn Sie die letzten Besuche abrufen, normalisieren Sie die Daten, bevor Sie sie an die Vorlage senden:

VORHER:

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

DANACH:

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

Prüfen Sie als Nächstes, ob file_blob oder file_gcs vorhanden ist (oder keines von beiden). Wenn eine Datei verfügbar ist, wählen Sie die vorhandene Datei aus und verwenden Sie die entsprechende Kennung (BlobKey für von Blobstore erstellte Dateien oder den Dateinamen für von Cloud Storage erstellte Dateien). Mit „von Cloud Storage erstellte Dateien“ sind Dateien gemeint, die mit der Cloud Storage-Clientbibliothek erstellt wurden. Blobstore schreibt auch in Cloud Storage, aber in diesem Fall wären das von Blobstore erstellte Dateien.

Was ist die etl_visits()-Funktion, mit der die Daten für den Endnutzer normalisiert oder ETL-Prozesse (Extrahieren, Transformieren und Laden) durchgeführt werden? Sie sieht so aus:

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]

Der Code durchläuft alle Besuche und verwendet für jeden Besuch die Besucher- und Zeitstempeldaten unverändert. Anschließend wird geprüft, ob file_gcs oder file_blob vorhanden ist. Wenn ja, wird einer der beiden Werte ausgewählt (oder None, wenn keiner der beiden vorhanden ist).

Hier sehen Sie eine Abbildung der Unterschiede zwischen main.py und main-migrate.py:

718b05b2adadb2e1.png

Wenn Sie von Grund auf neu beginnen und keine von Blobstore erstellten Dateien haben, verwenden Sie main.py. Wenn Sie jedoch umstellen und unterstützende Dateien benötigen, die sowohl von Blobstore als auch von Cloud Storage erstellt wurden, sehen Sie sich main-migrate.py als Beispiel dafür an, wie Sie mit einem solchen Szenario umgehen können, um Migrationen für Ihre eigenen Apps zu planen. Bei komplexen Migrationen treten wahrscheinlich Sonderfälle auf. Dieses Beispiel soll daher zeigen, wie moderne Apps mit echten Daten modernisiert werden können.

6. Zusammenfassung/Bereinigung

In diesem Abschnitt wird das Codelab abgeschlossen, indem die App bereitgestellt und geprüft wird, ob sie wie vorgesehen funktioniert und ob die Ausgabe korrekt ist. Führen Sie nach der App-Validierung alle erforderlichen Bereinigungsschritte aus und überlegen Sie sich, wie Sie weiter vorgehen möchten.

Anwendung bereitstellen und überprüfen

Bevor Sie Ihre App neu bereitstellen, müssen Sie pip install -t lib -r requirements.txt ausführen, damit die selbst gebündelten Drittanbieterbibliotheken im Ordner „lib“ verfügbar sind. Wenn Sie die abwärtskompatible Lösung ausführen möchten, benennen Sie main-migrate.py zuerst in main.py um. Führen Sie nun gcloud app deploy aus und prüfen Sie, ob die App genauso funktioniert wie die App aus Modul 15. Der Formularbildschirm sieht so aus:

f5b5f9f19d8ae978.png

Die Seite „Letzte Besuche“ sieht so aus:

f5ac6b98ee8a34cb.png

Herzlichen Glückwunsch zum Abschluss dieses Codelabs, in dem Sie App Engine Blobstore durch Cloud Storage, App Engine NDB durch Cloud NDB und webapp2 durch Flask ersetzt haben. Ihr Code sollte jetzt mit dem Code im FINISH (Module 16) folder übereinstimmen. Die alternative Datei main-migrate.py ist ebenfalls in diesem Ordner vorhanden.

„Migration“ zu Python 3

Die auskommentierte Python 3-Anweisung runtime oben in app.yaml ist alles, was für die Portierung dieser App zu Python 3 erforderlich ist. Der Quellcode selbst ist bereits mit Python 3 kompatibel, sodass hier keine Änderungen erforderlich sind. Führen Sie die folgenden Schritte aus, um die Anwendung als Python 3-App bereitzustellen:

  1. Entfernen Sie die Kommentarzeichen für die Python 3-runtime-Anweisung oben in app.yaml.
  2. Löschen Sie alle anderen Zeilen in app.yaml.
  3. Löschen Sie die Datei appengine_config.py. (in der Python 3-Laufzeit nicht verwendet)
  4. Löschen Sie den Ordner lib, falls er vorhanden ist. (bei der Python 3-Laufzeit nicht erforderlich)

Bereinigen

Allgemein

Wenn Sie die App vorerst nicht mehr benötigen, empfehlen wir Ihnen, sie zu deaktivieren, um Abrechnungen zu vermeiden. Wenn Sie jedoch weitere Tests durchführen möchten, bietet die App Engine-Plattform ein kostenloses Kontingent. Solange Sie diese Nutzungsebene nicht überschreiten, werden Ihnen keine Gebühren berechnet. Das gilt für die Rechenleistung. Es können aber auch Gebühren für relevante App Engine-Dienste anfallen. Weitere Informationen finden Sie auf der Preisseite. Wenn bei dieser Migration andere Cloud-Dienste beteiligt sind, werden diese separat abgerechnet. Sehen Sie sich in beiden Fällen gegebenenfalls den Abschnitt „Spezifisch für dieses Codelab“ unten an.

Die Bereitstellung auf einer serverlosen Google Cloud-Compute-Plattform wie App Engine verursacht geringe Build- und Speicherkosten. Cloud Build und Cloud Storage haben jeweils ein eigenes kostenloses Kontingent. Das Speichern dieses Bildes verbraucht einen Teil dieses Kontingents. Möglicherweise leben Sie jedoch in einer Region, in der es kein solches kostenloses Kontingent gibt. Achten Sie daher auf Ihre Speichernutzung, um potenzielle Kosten zu minimieren. Folgende Cloud Storage-„Ordner“ sollten Sie sich ansehen:

  • 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
  • Die oben genannten Speicherlinks hängen von Ihrem PROJECT_ID und *LOC*ab, z. B. „us“, wenn Ihre App in den USA gehostet wird.

Wenn Sie diese Anwendung oder andere zugehörige Migrations-Codelabs nicht weiter verwenden und alles vollständig löschen möchten, beenden Sie Ihr Projekt.

Spezifisch für dieses Codelab

Die unten aufgeführten Dienste sind nur für dieses Codelab verfügbar. Weitere Informationen finden Sie in der Dokumentation der einzelnen Produkte:

Wenn Sie von Modul 15 zu Modul 16 migriert sind, haben Sie weiterhin Daten in Blobstore. Deshalb haben wir die Preisinformationen oben aufgeführt.

Nächste Schritte

Neben dieser Anleitung gibt es weitere Migrationsmodule, die sich mit der Umstellung von den gebündelten Legacy-Diensten befassen:

  • Modul 2: Von App Engine ndb zu Cloud NDB migrieren
  • Module 7–9: Push-Aufgaben von App Engine Task Queue zu Cloud Tasks migrieren
  • Module 12–13: Von App Engine Memcache zu Cloud Memorystore migrieren
  • Module 18–19: Von App Engine-Aufgabenwarteschlange (Pull-Aufgaben) zu Cloud Pub/Sub migrieren

App Engine ist nicht mehr die einzige serverlose Plattform in Google Cloud. Wenn Sie eine kleine App Engine-Anwendung oder eine Anwendung mit eingeschränkter Funktionalität haben und sie in einen eigenständigen Mikrodienst umwandeln möchten oder eine monolithische Anwendung in mehrere wiederverwendbare Komponenten aufteilen möchten, sind dies gute Gründe für einen Wechsel zu Cloud Functions. Wenn die Containerisierung Teil Ihres Anwendungsentwicklungs-Workflows geworden ist, insbesondere wenn er aus einer CI/CD-Pipeline (Continuous Integration/Continuous Delivery oder Deployment) besteht, sollten Sie eine Migration zu Cloud Run in Betracht ziehen. Diese Szenarien werden in den folgenden Modulen behandelt:

  • Von App Engine zu Cloud Functions migrieren: Modul 11
  • Von App Engine zu Cloud Run migrieren: Modul 4 enthält Informationen zum Containerisieren Ihrer App mit Docker und Modul 5 zum Containerisieren ohne Container, Docker-Kenntnisse oder Dockerfiles.

Der Wechsel zu einer anderen serverlosen Plattform ist optional. Wir empfehlen, die besten Optionen für Ihre Apps und Anwendungsfälle zu prüfen, bevor Sie Änderungen vornehmen.

Unabhängig davon, welches Migrationsmodul Sie als Nächstes in Betracht ziehen, können Sie auf alle Serverless Migration Station-Inhalte (Codelabs, Videos, Quellcode [sofern verfügbar]) über das zugehörige Open-Source-Repository zugreifen. Das README des Repositorys enthält auch Informationen dazu, welche Migrationen infrage kommen und welche Reihenfolge der Migrationsmodule relevant ist.

7. Zusätzliche Ressourcen

Probleme mit Codelabs/Feedback

Wenn Sie Probleme mit diesem Codelab feststellen, suchen Sie bitte zuerst nach Ihrem Problem, bevor Sie es melden. Links zum Suchen und Erstellen neuer Probleme:

Migrationsressourcen

Links zu den Repository-Ordnern für Modul 15 (START) und Modul 16 (FINISH) finden Sie in der Tabelle unten. Sie können auch über das Repository für alle App Engine-Codelab-Migrationen auf sie zugreifen. Sie können es klonen oder eine ZIP-Datei herunterladen.

Codelab

Python 2

Python 3

Modul 15

code

Modul 16 (dieses Codelab)

code

(wie bei Python 2)

Onlineressourcen

Unten finden Sie Online-Ressourcen, die für diese Anleitung relevant sein könnten:

App Engine Blobstore und Cloud Storage

App Engine-Plattform

Andere Cloud-Informationen

Python

Videos

Lizenz

Dieser Text ist mit einer Creative Commons Attribution 2.0 Generic License lizenziert.