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

1. Übersicht

Die Codelab-Reihe „Serverless Migration Station“ (praktische Anleitungen im eigenen Tempo) und die zugehörigen Videos sollen Entwicklern von serverlosen Google Cloud-Anwendungen helfen, ihre Anwendungen zu modernisieren. Dazu werden sie durch eine oder mehrere Migrationen geführt, bei denen sie sich hauptsächlich von Legacy-Diensten verabschieden. Dadurch werden Ihre Anwendungen portabler und Sie erhalten mehr Optionen und Flexibilität. So können Sie eine größere Auswahl an Cloud-Produkten einbinden und darauf zugreifen und einfacher auf neuere Sprachversionen upgraden. Diese Reihe konzentriert sich zwar anfangs auf die ersten Cloud-Nutzer, hauptsächlich Entwickler der App Engine (Standardumgebung), ist aber breit genug, um andere serverlose Plattformen wie Cloud Functions und Cloud Run oder gegebenenfalls andere Plattformen einzubeziehen.

In diesem Codelab erfahren Sie, wie Sie von App Engine Blobstore zu Cloud Storage migrieren. Es gibt auch 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 zur Migration zu Cloud Storage vorbereiten

Voraussetzungen

Umfrage

Wie möchten Sie diese Anleitung verwenden?

Nur durchlesen Durchlesen und die Übungen machen

Wie würden Sie Ihre Erfahrung mit Python bewerten?

Anfänger Fortgeschritten Experte

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

Anfänger Fortgeschritten Experte

2. Hintergrund

Dieses Codelab beginnt mit der Beispiel-App aus Modul 15 und zeigt, wie Sie von Blobstore (und NDB) zu Cloud Storage (und Cloud NDB) migrieren. Bei der Migration werden Abhängigkeiten von den gebündelten Legacy-Diensten von App Engine ersetzt. So können Sie Ihre Anwendungen bei Bedarf auf eine andere serverlose Cloud-Plattform oder eine andere Hosting-Plattform verschieben.

Diese Migration erfordert im Vergleich zu den anderen Migrationen in dieser Reihe etwas mehr Aufwand. Blobstore ist vom ursprünglichen Web-App-Framework abhängig. Deshalb wird in der Beispiel-App das webapp2-Framework anstelle von Flask verwendet. In dieser Anleitung werden Migrationen zu Cloud Storage, Cloud NDB, Flask und Python 3 behandelt.

Die App registriert weiterhin „Besuche“ von Endnutzern und zeigt die zehn neuesten an. Im vorherigen Codelab (Modul 15) wurde jedoch eine neue Funktion für die Blobstore-Nutzung hinzugefügt: 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 dieselbe Ausgabe wie in früheren Versionen dieser App gerendert und die letzten Besuche werden angezeigt. Eine weitere Besonderheit ist, dass Besuche mit entsprechenden Artefakten einen Link „Ansehen“ enthalten, über den das Artefakt eines Besuchs angezeigt werden kann. In diesem Codelab werden die oben genannten Migrationen implementiert, wobei die beschriebene Funktionalität beibehalten wird.

3. Einrichtung/Vorbereitung

Bevor wir zum Hauptteil der Anleitung kommen, richten wir das Projekt ein, rufen den Code ab und stellen die Baseline-Anwendung bereit, damit wir wissen, dass wir mit funktionierendem Code begonnen haben.

1. Projekt einrichten

Wenn Sie die Anwendung 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 verwenden. Achten Sie darauf, dass 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 diese nicht haben, können Sie sie aus dem Ordner „START“ von Modul 15 herunterladen (Link unten). In diesem Codelab werden Sie durch die einzelnen Schritte geführt. Am Ende erhalten Sie Code, der dem Code im Ordner „FINISH“ von Modul 16 ähnelt.

Das Verzeichnis der START-Dateien von 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. Sie ermöglicht die Auswahl eines Cloud Storage-Buckets, der sich vom Standard-Bucket der zugewiesenen URL einer Anwendung unterscheidet, die auf der Projekt-ID basiert: PROJECT_ID.appspot.com. Diese Datei spielt in diesem Codelab (Modul 16) keine Rolle, außer dass ähnliche Migrationstechniken auf diese Datei angewendet werden können, falls gewünscht.

3. Baseline-Anwendung (neu) bereitstellen

Die verbleibenden Vorbereitungsschritte, die Sie jetzt ausführen müssen:

  1. Machen Sie sich noch einmal mit dem gcloud-Befehlszeilentool vertraut.
  2. Beispiel-App mit gcloud app deploy noch einmal bereitstellen
  3. Prüfen, ob die Anwendung ohne Probleme in App Engine ausgeführt wird

Nachdem 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 Artefaktdatei für den Besuch hochzuladen. Außerdem gibt es die Option, dies zu überspringen:

f5b5f9f19d8ae978.png

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

f5ac6b98ee8a34cb.png

Bei Besuchen mit einem Artefakt wird rechts neben dem Zeitstempel des Besuchs ein Link „Ansehen“ angezeigt, über den Sie das Artefakt aufrufen oder herunterladen können. Nachdem Sie die Funktionalität der Anwendung bestätigt haben, können Sie von den App Engine-Legacy-Diensten (webapp2, NDB, Blobstore) zu modernen Alternativen (Flask, Cloud NDB, Cloud Storage) migrieren.

4. Konfigurationsdateien aktualisieren

Für die aktualisierte Version unserer Anwendung kommen drei Konfigurationsdateien ins Spiel. Die erforderlichen Aufgaben sind:

  1. Erforderliche integrierte Drittanbieterbibliotheken in app.yaml aktualisieren und die Möglichkeit einer Migration zu Python 3 offenlassen
  2. Fügen Sie eine requirements.txt hinzu, in der 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 aktuelle Version aus, die für alle drei Bibliotheken verfügbar ist. Fügen Sie auch die Python 3-Anweisung runtime hinzu, aber auskommentiert. 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 in Flask enthalten ist, das wir der Datei „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. Für Cloud Storage selbst ist die SSL-Bibliothek erforderlich. Die auskommentierte Laufzeit-Anweisung oben ist für den Fall gedacht, dass Sie diese Anwendung auf 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, die das Flask-Framework sowie die Cloud NDB- und Cloud Storage-Clientbibliotheken erfordert. Keine davon ist integriert. Erstellen Sie die Datei mit folgendem Inhalt:

flask
google-cloud-ndb
google-cloud-storage

Die Python 2-App Engine-Laufzeit erfordert, dass nicht integrierte Drittanbieterbibliotheken selbst gebündelt werden. 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 die Python 2-Versionen dieser Bibliotheken zu erhalten. Nach dem Upgrade auf Python 3 ist das nicht mehr erforderlich.

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-Anwendungen installieren der App Engine-Dokumentation ähneln oder mit ihnen identisch sein. Insbesondere sollte der Inhalt von appengine_config.py mit dem Inhalt von Schritt 5 übereinstimmen.

Die Arbeit an den Konfigurationsdateien ist abgeschlossen. Kommen wir nun zur Anwendung.

5. Anwendungsdateien ändern

Importe

Die ersten Änderungen für main.py umfassen das Ersetzen aller zu ersetzenden Elemente. 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 Standardbibliotheksmodul io, Flask und werkzeug-Dienstprogrammen ersetzt.
  5. Standardmäßig schreibt Blobstore in einen Cloud Storage-Bucket, der nach der URL Ihrer Anwendung (PROJECT_ID.appspot.com) benannt ist. Da wir zur Cloud Storage-Clientbibliothek portieren, 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 hartcodiert 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 obigen Liste, indem Sie den aktuellen Importabschnitt 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 zu ersetzende Codeblock ist der BaseHandler, der die Verwendung von Jinja2 aus webapp2_extras angibt. Das ist nicht erforderlich, da Jinja2 in Flask enthalten und die Standard-Vorlagen-Engine ist. Entfernen Sie es also.

Instanziieren Sie auf der Seite von Modul 16 Objekte, die in der älteren Anwendung nicht vorhanden waren. Dazu gehören die Initialisierung der Flask-Anwendung und die Erstellung von API-Clients für Cloud NDB und Cloud Storage. Schließlich setzen wir den Namen des Cloud Storage-Buckets wie oben im Abschnitt „Importe“ beschrieben zusammen. Hier sehen Sie die Situation vor und nach der Implementierung dieser Updates:

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

Datastore-Zugriff aktualisieren

Cloud NDB ist weitgehend mit App Engine NDB kompatibel. Ein bereits erwähnter Unterschied ist die Notwendigkeit eines API-Clients. Ein weiterer Unterschied besteht darin, dass der Zugriff auf Datastore über den Python-Kontextmanager des API-Clients gesteuert werden muss. Im Wesentlichen bedeutet dies, dass alle Datastore-Zugriffsaufrufe mit der Cloud NDB-Clientbibliothek nur innerhalb von Python-with-Blöcken erfolgen können.

Das ist eine Änderung. Die andere ist, dass Blobstore und seine Objekte, z. B. BlobKeys, von Cloud Storage nicht unterstützt werden. Ändern Sie daher den file_blob in einen ndb.StringProperty. Im Folgenden finden 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 ist eine bildliche Darstellung der bisher vorgenommenen Änderungen:

a8f74ca392275822.png

Handler aktualisieren

Upload-Handler

Handler in webapp2 sind Klassen, während sie in Flask Funktionen sind. 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 die zugehörigen Dienstprogramme 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 einer blob_id werden Datei-Artefakte jetzt durch den Dateinamen (fname) identifiziert, falls vorhanden, und andernfalls durch None (Nutzer hat sich gegen das Hochladen einer Datei entschieden).
  • Die Blobstore-Handler haben den Uploadprozess von den Nutzern abstrahiert, Cloud Storage tut dies jedoch nicht. Sie können also den neu hinzugefügten Code sehen, der das Blob-Objekt und den Speicherort (Bucket) der Datei festlegt, sowie den Aufruf, der den eigentlichen Upload durchführt. (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 zur Startseite ( /) weiterleiten und die POST-Anfrage mit einem HTTP-307-Rückgabecode beibehalten.

Download-Handler

Das Aktualisieren des Download-Handlers folgt einem ähnlichen Muster wie beim Upload-Handler, nur dass es viel weniger Code gibt. Ersetzen Sie die Blobstore- und webapp-Funktionen durch die entsprechenden Cloud Storage- und Flask-Funktionen:

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:

  • Auch hier dekoriert Flask Handler-Funktionen mit ihrer Route, während webapp dies in einer Routingtabelle am Ende tut. Beachten Sie daher die Syntax für den Musterabgleich von (('/view/([^/]+)?') im Vergleich zu der von Flask ('/view/<path:fname>').
  • Wie beim Upload-Handler ist auch hier auf der Cloud Storage-Seite etwas mehr Arbeit erforderlich, um die von den Blobstore-Handlern abstrahierten Funktionen zu nutzen. Dazu gehören die Identifizierung der betreffenden Datei (Blob) und das explizite Herunterladen der Binärdatei im Vergleich 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 werden im Haupthandler vorgenommen. Die HTTP-Verb-Methoden webapp2 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)

Anstatt separate get()- und post()-Methoden zu verwenden, handelt es sich im Wesentlichen um eine if-else-Anweisung in root(). Da root() eine einzelne Funktion ist, gibt es außerdem nur einen Aufruf, um die Vorlage für GET und POST zu rendern. In webapp2 ist das nicht wirklich möglich.

Hier ist eine bildliche Darstellung dieser zweiten und letzten Gruppe von Ä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 vorn beginnen und keine von Blobstore erstellten Dateien haben. Da wir die Anwendung so aktualisiert haben, dass Dateien anhand des Dateinamens anstelle von BlobKey identifiziert werden, können mit der fertigen Anwendung aus Modul 16 keine Blobstore-Dateien angezeigt werden. Mit anderen Worten: Wir haben bei dieser Migration eine nicht abwärtskompatible Änderung vorgenommen. Wir stellen nun eine alternative Version von main.py namens main-migrate.py vor (im Repository zu finden), die versucht, diese Lücke zu schließen.

Die erste „Erweiterung“ zur Unterstützung von mit Blobstore erstellten Dateien ist ein Datenmodell mit einem BlobKeyProperty (zusätzlich zu einem StringProperty für mit Cloud Storage erstellte 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, speichern Sie explizit einen Wert in file_gcs anstelle von file_blob, sodass store_visit etwas anders aussieht:

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)

Bestätigen Sie als Nächstes, ob file_blob oder file_gcs (oder keines von beiden) vorhanden ist. 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 Dateiname für von Cloud Storage erstellte Dateien). Mit „von Cloud Storage erstellten Dateien“ sind Dateien gemeint, die mit der Cloud Storage-Clientbibliothek erstellt wurden. Blobstore schreibt ebenfalls in Cloud Storage, aber in diesem Fall handelt es sich um von Blobstore erstellte Dateien.

Was ist nun diese etl_visits()-Funktion, die zum Normalisieren oder ETL (Extrahieren, Transformieren und Laden) der Daten für den Endnutzer verwendet wird? 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 sieht wahrscheinlich so aus, wie Sie es erwartet haben: Er durchläuft alle Besuche und übernimmt für jeden Besuch die Besucher- und Zeitstempeldaten unverändert. Dann prüft er, ob file_gcs oder file_blob vorhanden ist, und wählt einen der beiden Werte aus (oder None, wenn keiner von beiden vorhanden ist).

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

718b05b2adadb2e1.png

Wenn Sie ohne von Blobstore erstellte Dateien ganz von vorn beginnen, verwenden Sie main.py. Wenn Sie jedoch migrieren und Dateien unterstützen möchten, die sowohl von Blobstore als auch von Cloud Storage erstellt wurden, sehen Sie sich main-migrate.py als Beispiel für den Umgang mit einem solchen Szenario an, um die Migration für Ihre eigenen Anwendungen zu planen. Bei komplexen Migrationen treten wahrscheinlich Sonderfälle auf. Dieses Beispiel soll daher eine größere Affinität zur Modernisierung echter Anwendungen mit echten Daten zeigen.

6. Zusammenfassung/Bereinigung

In diesem Abschnitt wird das Codelab abgeschlossen. Dazu wird die Anwendung bereitgestellt und geprüft, ob sie wie vorgesehen funktioniert und die Ausgabe korrekt ist. Führen Sie nach der App-Validierung alle Bereinigungsschritte aus und überlegen Sie sich die nächsten Schritte.

Anwendung bereitstellen und prüfen

Führen Sie vor der erneuten Bereitstellung Ihrer Anwendung pip install -t lib -r requirements.txt aus, um die selbst gebündelten Drittanbieterbibliotheken in den Ordner „lib“ zu verschieben. Wenn Sie die abwärtskompatible Lösung ausführen möchten, benennen Sie zuerst main-migrate.py in main.py um. Führen Sie jetzt gcloud app deploy aus und prüfen Sie, ob die Anwendung genauso funktioniert wie die Anwendung aus Modul 15. Der Formularbildschirm sieht so aus:

f5b5f9f19d8ae978.png

Die Seite mit den letzten Besuchen sieht so aus:

f5ac6b98ee8a34cb.png

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

Python 3-Migration

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

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

Bereinigen

Allgemein

Wenn Sie vorerst fertig sind, empfehlen wir Ihnen, Ihre App Engine-Anwendung zu deaktivieren, um Abrechnungskosten zu vermeiden. Wenn Sie jedoch weitere Tests oder Experimente durchführen möchten, bietet die App Engine-Plattform ein kostenloses Kontingent. Solange Sie diese Nutzungsstufe nicht überschreiten, fallen keine Kosten an. 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 diese Migration andere Cloud-Dienste umfasst, werden diese separat abgerechnet. In beiden Fällen finden Sie unten im Abschnitt „Spezifisch für dieses Codelab“ weitere Informationen.

Der Vollständigkeit halber sei erwähnt, dass bei der Bereitstellung auf einer serverlosen Google Cloud-Computing-Plattform wie App Engine geringe Build- und Speicherkosten anfallen. Cloud Build hat ein eigenes kostenloses Kontingent, ebenso wie Cloud Storage. Das Speichern dieses Bildes verbraucht einen Teil dieses Kontingents. Möglicherweise leben Sie jedoch in einer Region, in der es keine solche kostenlose Stufe gibt. Achten Sie daher auf Ihre Speichernutzung, um potenzielle Kosten zu minimieren. Zu den spezifischen Cloud Storage-„Ordnern“, die Sie sich ansehen sollten, gehören:

  • 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 fortsetzen und alles vollständig löschen möchten, beenden Sie Ihr Projekt.

Spezifisch für dieses Codelab

Die unten aufgeführten Dienste sind nur in diesem Codelab verfügbar. Weitere Informationen finden Sie in der Dokumentation zum jeweiligen Produkt:

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

Nächste Schritte

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

  • Modul 2: Von App Engine-ndb zu Cloud NDB migrieren
  • Module 7–9: Von der App Engine-Aufgabenwarteschlange (Push-Aufgaben) zu Cloud Tasks migrieren
  • Module 12–13: Von App Engine Memcache zu Cloud Memorystore migrieren
  • Module 18–19: Von der 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 diese in einen eigenständigen Mikrodienst umwandeln möchten oder wenn Sie 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 Workflows für die Anwendungsentwicklung geworden ist, insbesondere wenn sie 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: siehe Modul 11
  • Von App Engine zu Cloud Run migrieren: Informationen zum Containerisieren Ihrer Anwendung mit Docker finden Sie in Modul 4. Informationen zum Containerisieren ohne Container, Docker-Kenntnisse oder Dockerfiles finden Sie in Modul 5.

Der Wechsel zu einer anderen serverlosen Plattform ist optional. Wir empfehlen, die besten Optionen für Ihre Anwendungen 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 Inhalte der Serverless Migration Station (Codelabs, Videos, Quellcode [falls verfügbar]) in ihrem Open-Source-Repository zugreifen. Die README des Repositorys enthält auch Informationen dazu, welche Migrationen in Betracht gezogen werden sollten, und zur relevanten „Reihenfolge“ der Migrationsmodule.

7. Zusätzliche Ressourcen

Probleme mit Codelab/Feedback zu Codelab

Wenn Sie Probleme mit diesem Codelab haben, suchen Sie 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 darauf zugreifen. Sie können das Repository klonen oder eine ZIP-Datei herunterladen.

Codelab

Python 2

Python 3

Modul 15

code

–

Modul 16 (dieses Codelab)

code

(wie Python 2)

Online-Ressourcen

Im Folgenden finden Sie Onlineressourcen, die für diese Anleitung relevant sein könnten:

App Engine Blobstore und Cloud Storage

App Engine-Plattform

Andere Cloud-Informationen

Python

Videos

Lizenz

Dieses Werk unterliegt einer Creative-Commons-Lizenz „Namensnennung 2.0“.