Migracja z App Engine Blobstore do Cloud Storage (moduł 16)

1. Przegląd

Seria codelabów Serverless Migration Station (samodzielne, praktyczne samouczki) i powiązane z nimi filmy mają na celu pomóc deweloperom usług bezserwerowych Google Cloud w modernizacji aplikacji poprzez przeprowadzenie ich przez co najmniej jedną migrację, głównie z usług starszego typu. Dzięki temu Twoje aplikacje będą bardziej przenośne, a Ty zyskasz więcej opcji i elastyczności, co umożliwi Ci integrację z szerszą gamą usług w chmurze i łatwiejsze przechodzenie na nowsze wersje języka. Chociaż początkowo skupialiśmy się na pierwszych użytkownikach usług w chmurze, głównie na deweloperach App Engine (środowisko standardowe), ta seria jest wystarczająco szeroka, aby obejmować inne platformy bezserwerowe, takie jak Cloud FunctionsCloud Run, lub inne, jeśli ma to zastosowanie.

Ten przewodnik uczy, jak przeprowadzić migrację z App Engine Blobstore do Cloud Storage. Istnieją też niejawne migracje z tych usług:

Więcej szczegółowych informacji znajdziesz w powiązanych modułach migracji.

Dowiesz się, jak:

  • Dodawanie korzystania z interfejsu API/biblioteki Blobstore App Engine
  • Przechowywanie plików przesłanych przez użytkowników w usłudze Blobstore
  • Przygotowanie do następnego kroku migracji do Cloud Storage

Czego potrzebujesz

Ankieta

Jak zamierzasz korzystać z tego samouczka?

Tylko przeczytaj Przeczytaj i wykonaj ćwiczenia

Jak oceniasz swoje doświadczenie z Pythonem?

Początkujący Średnio zaawansowany Zaawansowany

Jak oceniasz korzystanie z usług Google Cloud?

Początkujący Średnio zaawansowany Zaawansowany

2. Tło

W tym laboratorium zaczynamy od przykładowej aplikacji z modułu 15 i pokazujemy, jak przeprowadzić migrację z Blobstore (i NDB) do Cloud Storage (i Cloud NDB). Proces migracji polega na zastąpieniu zależności od starszych usług pakietowych App Engine, co umożliwia przeniesienie aplikacji na inną bezserwerową platformę Cloud lub inną platformę hostingową.

Ta migracja wymaga nieco więcej wysiłku niż inne migracje z tej serii. Blobstore jest zależny od oryginalnej platformy programistycznej, dlatego w przykładowej aplikacji używana jest platforma webapp2 zamiast Flask. Ten samouczek zawiera informacje o migracjach do Cloud Storage, Cloud NDB, Flask i Python 3.

Aplikacja nadal rejestruje „wizyty” użytkowników i wyświetla 10 najnowszych, ale w poprzednim laboratorium (moduł 15) dodaliśmy nową funkcję, która umożliwia korzystanie z Blobstore: aplikacja prosi użytkowników o przesłanie artefaktu (pliku) odpowiadającego ich „wizycie”. Użytkownicy mogą to zrobić lub wybrać „Pomiń”, aby zrezygnować. Niezależnie od decyzji użytkownika następna strona renderuje te same dane wyjściowe co poprzednie wersje tej aplikacji, wyświetlając najnowsze wizyty. Dodatkowo wizyty z odpowiednimi artefaktami mają link „Wyświetl”, który pozwala wyświetlić artefakt wizyty. W tym laboratorium kodowania wdrażamy wspomniane wcześniej migracje, zachowując opisaną funkcjonalność.

3. Konfiguracja/przygotowanie

Zanim przejdziemy do głównej części samouczka, skonfigurujmy projekt, pobierzmy kod i wdrożymy aplikację podstawową, aby mieć pewność, że zaczynamy od działającego kodu.

1. Konfigurowanie projektu

Jeśli aplikacja z modułu 15 została już wdrożona, zalecamy ponowne użycie tego samego projektu (i kodu). Możesz też utworzyć zupełnie nowy projekt lub ponownie wykorzystać inny istniejący projekt. Sprawdź, czy projekt ma aktywne konto rozliczeniowe i czy usługa App Engine jest włączona.

2. Pobieranie przykładowej aplikacji podstawowej

Jednym z wymagań wstępnych tego laboratorium jest działająca przykładowa aplikacja z modułu 15. Jeśli jej nie masz, możesz ją pobrać z folderu „START” modułu 15 (link poniżej). W tym ćwiczeniu Codelabs znajdziesz instrukcje krok po kroku, a na końcu kod podobny do tego, który znajduje się w folderze „FINISH” modułu 16.

Katalog plików początkowych modułu 15 powinien wyglądać tak:

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

Plik main-gcs.py to alternatywna wersja pliku main.py z modułu 15, która umożliwia wybór zasobnika Cloud Storage innego niż domyślny adres URL przypisany do aplikacji na podstawie identyfikatora projektu: PROJECT_ID.appspot.com. Ten plik nie odgrywa żadnej roli w tym laboratorium (moduł 16). W razie potrzeby można do niego zastosować podobne techniki migracji.

3. (Ponowne) wdrażanie aplikacji bazowej

Pozostałe czynności przygotowawcze, które musisz teraz wykonać:

  1. Przypomnij sobie, jak działa narzędzie wiersza poleceń gcloud.
  2. Ponowne wdrażanie przykładowej aplikacji za pomocą gcloud app deploy
  3. Sprawdź, czy aplikacja działa w App Engine bez problemów.

Po wykonaniu tych czynności i potwierdzeniu, że aplikacja z modułu 15 działa prawidłowo. Na stronie początkowej wyświetla się formularz z prośbą o przesłanie pliku artefaktu wizyty oraz opcją rezygnacji w postaci przycisku „Pomiń”:

f5b5f9f19d8ae978.png

Gdy użytkownicy prześlą plik lub pominą ten krok, aplikacja wyświetli znaną stronę „Ostatnie wizyty”:

f5ac6b98ee8a34cb.png

Wizyty zawierające artefakt będą miały link „Wyświetl” po prawej stronie sygnatury czasowej wizyty, który umożliwia wyświetlenie (lub pobranie) artefaktu. Gdy potwierdzisz, że aplikacja działa prawidłowo, możesz przeprowadzić migrację z usług starszego typu App Engine (webapp2, NDB, Blobstore) na współczesne alternatywy (Flask, Cloud NDB, Cloud Storage).

4. Aktualizowanie plików konfiguracji

W przypadku zaktualizowanej wersji naszej aplikacji używane są 3 pliki konfiguracyjne. Wymagane zadania to:

  1. Zaktualizuj wymagane wbudowane biblioteki innych firm w app.yaml, a także pozostaw możliwość migracji do Pythona 3.
  2. Dodaj requirements.txt, który określa wszystkie wymagane biblioteki, które nie są wbudowane.
  3. Dodaj appengine_config.py, aby aplikacja obsługiwała zarówno wbudowane, jak i zewnętrzne biblioteki innych firm.

app.yaml

Edytuj plik app.yaml, aktualizując sekcję libraries. Usuń jinja2 i dodaj grpcio, setuptools i ssl. Wybierz najnowszą wersję dostępną dla wszystkich 3 bibliotek. Dodaj też dyrektywę runtime Python 3, ale w postaci komentarza. Gdy skończysz, powinno to wyglądać tak (jeśli wybrano Pythona 3.9):

PRZED:

runtime: python27
threadsafe: yes
api_version: 1

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

libraries:
- name: jinja2
  version: latest

PO:

#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

Zmiany dotyczą głównie wbudowanych bibliotek języka Python 2 dostępnych na serwerach App Engine (dzięki czemu nie musisz ich samodzielnie pakować). Usunęliśmy Jinja2, ponieważ jest on dostarczany z Flaskiem, który dodamy do pliku reqs.txt. Gdy używane są biblioteki klienta Google Cloud, takie jak biblioteki Cloud NDB i Cloud Storage, wymagane są grpcio i setuptools. Biblioteka ssl jest też wymagana przez samą usługę Cloud Storage. Zakomentowana dyrektywa środowiska wykonawczego u góry jest przeznaczona na moment, w którym będziesz gotowy do przeniesienia tej aplikacji do Pythona 3. Tym tematem zajmiemy się pod koniec tego samouczka.

requirements.txt

Dodaj plik requirements.txt, który wymaga frameworka Flask oraz bibliotek klienta Cloud NDB i Cloud Storage. Żadna z nich nie jest wbudowana. Utwórz plik z tą zawartością:

flask
google-cloud-ndb
google-cloud-storage

Środowisko wykonawcze App Engine Python 2 wymaga samodzielnego pakowania bibliotek innych firm, które nie są wbudowane, więc uruchom to polecenie, aby zainstalować te biblioteki w folderze lib:

pip install -t lib -r requirements.txt

Jeśli na komputerze używanym do programowania masz zainstalowane zarówno Pythona 2, jak i 3, może być konieczne użycie polecenia pip2, aby uzyskać wersje tych bibliotek dla Pythona 2. Po uaktualnieniu do Pythona 3 nie musisz już samodzielnie tworzyć pakietów.

appengine_config.py

Dodaj plik appengine_config.py obsługujący wbudowane i zewnętrzne biblioteki innych firm. Utwórz plik z tą zawartością:

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)

Wykonane przed chwilą czynności powinny być podobne lub identyczne z czynnościami wymienionymi w sekcji Instalowanie bibliotek w aplikacjach w Pythonie 2 w dokumentacji App Engine, a w szczególności zawartość pliku appengine_config.py powinna być zgodna z zawartością kroku 5.

Praca z plikami konfiguracji została zakończona, więc przejdźmy do aplikacji.

5. Modyfikowanie plików aplikacji

Importy

Pierwszy zestaw zmian w przypadku main.py obejmuje zamianę wszystkich elementów, które mają zostać zastąpione. Co się zmienia:

  1. webapp2 zostaje zastąpiony przez Flask
  2. Zamiast korzystać z Jinja2 z webapp2_extras, używaj Jinja2 dołączonego do Flaska.
  3. App Engine Blobstore i NDB zostały zastąpione przez Cloud NDB i Cloud Storage
  4. Obsługa Blobstore w webapp została zastąpiona kombinacją modułu biblioteki standardowej io, Flaska i narzędzi werkzeug.
  5. Domyślnie Blobstore zapisuje dane w zasobniku Cloud Storage o nazwie odpowiadającej adresowi URL aplikacji (PROJECT_ID.appspot.com). Ponieważ przenosimy dane do biblioteki klienta Cloud Storage, google.auth służy do pobierania identyfikatora projektu w celu określenia dokładnie tej samej nazwy zasobnika. (Możesz zmienić nazwę zasobnika, ponieważ nie jest już zakodowana na stałe).

PRZED:

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

Wprowadź zmiany z powyższej listy, zastępując bieżącą sekcję importu w pliku main.py fragmentem kodu poniżej.

PO:

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

Inicjalizacja i niepotrzebna obsługa Jinja2

Kolejny blok kodu do zastąpienia to BaseHandler, który określa użycie Jinja2 z webapp2_extras. Jest to niepotrzebne, ponieważ Jinja2 jest dostarczana z Flaskiem i jest jego domyślnym silnikiem szablonów, więc usuń ją.

Po stronie modułu 16 utwórz instancje obiektów, których nie było w starszej aplikacji. Obejmuje to zainicjowanie aplikacji Flask i utworzenie klientów interfejsu API dla Cloud NDB i Cloud Storage. Na koniec tworzymy nazwę zasobnika Cloud Storage zgodnie z opisem w sekcji importów. Oto przykłady przed wprowadzeniem tych zmian i po ich wprowadzeniu:

PRZED:

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

PO:

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

Aktualizowanie dostępu do Datastore

Cloud NDB jest w większości przypadków zgodna z App Engine NDB. Jedną z różnic, o której już wspomnieliśmy, jest konieczność używania klienta interfejsu API. Kolejna różnica polega na tym, że w przypadku tego drugiego dostępu do Datastore musi zarządzać menedżer kontekstu Pythona klienta API. Oznacza to, że wszystkie wywołania dostępu do Datastore za pomocą biblioteki klienta Cloud NDB mogą występować tylko w blokach Pythona with.

To jedna zmiana.Druga polega na tym, że Blobstore i jego obiekty, np. BlobKey, nie są obsługiwane przez Cloud Storage, więc zamiast file_blob użyj ndb.StringProperty. Poniżej znajdziesz klasę modelu danych oraz zaktualizowane funkcje store_visit()fetch_visits(), które odzwierciedlają te zmiany:

PRZED:

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)

PO:

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)

Oto graficzne przedstawienie wprowadzonych do tej pory zmian:

a8f74ca392275822.png

Aktualizowanie modułów obsługi

Przesyłanie

Obsługi w webapp2 to klasy, a w Flask są funkcjami. Zamiast metody czasownika HTTP Flask używa czasownika do dekorowania funkcji. Blobstore i jego moduły obsługi webapp zostały zastąpione funkcjami Cloud Storage oraz Flask i jego narzędziami:

PRZED:

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)

PO:

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

Kilka uwag dotyczących tej aktualizacji:

  • Zamiast blob_id artefakty plików są teraz identyfikowane na podstawie nazwy pliku (fname), jeśli jest dostępna, lub None w przeciwnym razie (użytkownik zrezygnował z przesyłania pliku).
  • Obsługa Blobstore odciążała użytkowników od procesu przesyłania, ale Cloud Storage tego nie robi. Dlatego możesz zobaczyć nowo dodany kod, który ustawia obiekt blob pliku i jego lokalizację (zasobnik), a także wywołanie, które wykonuje rzeczywiste przesyłanie. (upload_from_file()).
  • webapp2 korzysta z tabeli routingu u dołu pliku aplikacji, a trasy Flaska znajdują się w każdym dekorowanym module obsługi.
  • Oba moduły obsługi kończą swoje działanie, przekierowując do strony głównej ( / ) przy zachowaniu żądania POST z kodem zwrotu HTTP 307.

Moduł obsługi pobierania

Aktualizowanie modułu obsługi pobierania przebiega podobnie jak w przypadku modułu obsługi przesyłania, tylko jest tu znacznie mniej kodu do przeanalizowania. Zastąp funkcje Blobstore i webapp odpowiednikami w Cloud Storage i Flask:

PRZED:

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)

PO:

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

Uwagi dotyczące tej aktualizacji:

  • Flask dekoruje funkcje obsługi za pomocą ich trasy, a webapp robi to w tabeli routingu u dołu. Zwróć uwagę na składnię dopasowywania wzorców w przypadku webapp (('/view/([^/]+)?') i Flaska ('/view/<path:fname>').
  • Podobnie jak w przypadku modułu obsługi przesyłania, po stronie Cloud Storage trzeba wykonać nieco więcej pracy w przypadku funkcji, które są abstrakcyjnie obsługiwane przez moduły obsługi blobstore, a mianowicie zidentyfikować dany plik (BLOB) i jawnie pobrać dane binarne w porównaniu z pojedynczym wywołaniem metody send_blob() modułu obsługi blobstore.
  • W obu przypadkach, jeśli artefakt nie zostanie znaleziony, użytkownik otrzyma błąd HTTP 404.

Główny moduł obsługi

Ostateczne zmiany w głównej aplikacji są wprowadzane w głównym programie obsługi. Metody czasownika HTTP webapp2 zostały zastąpione jedną funkcją łączącą ich działanie. Zastąp klasę MainHandler funkcją root() i usuń tabelę routingu webapp2, jak pokazano poniżej:

PRZED:

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)

PO:

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

Zamiast oddzielnych metod get()post() są one w zasadzie instrukcją if-else w języku root(). Poza tym, ponieważ root() to jedna funkcja, istnieje tylko jedno wywołanie renderowania szablonu zarówno dla GET, jak i POST, co nie jest możliwe w przypadku webapp2.

Oto graficzne przedstawienie drugiego i ostatniego zestawu zmian w main.py:

5ec38818c32fec2.png

(opcjonalnie) „Ulepszenie” zgodności wstecznej

Powyższe rozwiązanie działa doskonale, ale tylko wtedy, gdy zaczynasz od zera i nie masz plików utworzonych przez Blobstore. Zaktualizowaliśmy aplikację, aby identyfikowała pliki według nazwy, a nie BlobKey, więc ukończona aplikacja z modułu 16 w obecnej postaci nie będzie mogła wyświetlać plików Blobstore. Innymi słowy, podczas tej migracji wprowadziliśmy zmianę, która nie jest wstecznie kompatybilna. Przedstawiamy teraz alternatywną wersję main.py o nazwie main-migrate.py (znajdującą się w Repo), która ma na celu wypełnienie tej luki.

Pierwszym „rozszerzeniem” obsługującym pliki utworzone w Blobstore jest model danych, który ma BlobKeyProperty (oprócz StringProperty w przypadku plików utworzonych w 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()

Właściwość file_blob będzie używana do identyfikowania plików utworzonych przez Blobstore, a właściwość file_gcs – plików Cloud Storage. Teraz podczas tworzenia nowych wizyt wyraźnie zapisuj wartość w parametrze file_gcs zamiast w parametrze file_blob, więc parametr store_visit będzie wyglądać nieco inaczej:

PRZED:

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

PO:

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

Podczas pobierania najnowszych wizyt „normalizuj” dane przed wysłaniem ich do szablonu:

PRZED:

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

PO:

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

Następnie sprawdź, czy występuje file_blob lub file_gcs (lub żadne z nich). Jeśli plik jest dostępny, wybierz istniejący plik i użyj jego identyfikatora (BlobKey w przypadku plików utworzonych w Blobstore lub nazwy pliku w przypadku plików utworzonych w Cloud Storage). Określenie „pliki utworzone przez Cloud Storage” oznacza pliki utworzone przy użyciu biblioteki klienta Cloud Storage. Blobstore też zapisuje dane w Cloud Storage, ale w tym przypadku są to pliki utworzone przez Blobstore.

Co ważniejsze, czym jest ta funkcja etl_visits(), która służy do normalizowania lub ETL (wyodrębniania, przekształcania i ładowania) danych dla użytkownika? Wygląda on następująco:

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]

Prawdopodobnie wygląda on tak, jak się spodziewasz: kod przechodzi przez wszystkie wizyty i w przypadku każdej z nich pobiera dane o użytkowniku i sygnaturze czasowej, a następnie sprawdza, czy istnieje file_gcs lub file_blob, i jeśli tak, wybiera jeden z nich (lub None, jeśli żaden z nich nie istnieje).

Ilustracja różnic między main.pymain-migrate.py:

718b05b2adadb2e1.png

Jeśli zaczynasz od zera i nie masz plików utworzonych przez Blobstore, użyj main.py. Jeśli jednak przechodzisz na nową usługę i chcesz obsługiwać pliki utworzone zarówno przez Blobstore , jak i Cloud Storage, zapoznaj się z przykładem main-migrate.py, który pokazuje, jak postępować w takim przypadku, aby ułatwić sobie planowanie migracji własnych aplikacji. Podczas złożonych migracji mogą wystąpić przypadki specjalne, dlatego ten przykład ma na celu pokazanie większego podobieństwa do modernizacji prawdziwych aplikacji z prawdziwymi danymi.

6. Podsumowanie i czyszczenie

W tej sekcji podsumowujemy te warsztaty, wdrażając aplikację i sprawdzając, czy działa zgodnie z oczekiwaniami i czy dane wyjściowe są prawidłowe. Po zweryfikowaniu aplikacji wykonaj czynności związane z czyszczeniem i zastanów się, co dalej zrobić.

Wdrażanie i weryfikowanie aplikacji

Zanim ponownie wdrożysz aplikację, uruchom polecenie pip install -t lib -r requirements.txt, aby uzyskać te samodzielnie spakowane biblioteki innych firm w folderze lib. Jeśli chcesz uruchomić rozwiązanie zgodne wstecznie, najpierw zmień nazwę pliku main-migrate.py na main.py. Teraz uruchom gcloud app deploy i sprawdź, czy aplikacja działa tak samo jak aplikacja z modułu 15. Ekran formularza wygląda tak:

f5b5f9f19d8ae978.png

Strona z najnowszymi wizytami wygląda tak:

f5ac6b98ee8a34cb.png

Gratulujemy ukończenia tego ćwiczenia (w Codelabs), w którym zastąpiliśmy App Engine Blobstore usługą Cloud Storage, App Engine NDB usługą Cloud NDB, a webapp2 platformą Flask. Kod powinien teraz odpowiadać temu, który znajduje się w folderze FINISH (Module 16). W tym folderze znajduje się też alternatywna wersja main-migrate.py.

„Migracja” do Pythona 3

Wystarczy, że na początku pliku app.yaml umieścisz dyrektywę Python 3 runtime, która jest zakomentowana, aby przenieść tę aplikację do Pythona 3. Sam kod źródłowy jest już zgodny z Pythonem 3, więc nie trzeba go zmieniać. Aby wdrożyć tę aplikację jako aplikację w Pythonie 3, wykonaj te czynności:

  1. Odkomentuj dyrektywę Pythona 3 runtime u góry pliku app.yaml.
  2. Usuń wszystkie pozostałe wiersze w app.yaml.
  3. Usuń plik appengine_config.py. (nieużywane w środowisku wykonawczym Python 3)
  4. Usuń folder lib, jeśli istnieje. (niepotrzebne w środowisku wykonawczym Pythona 3)

Czyszczenie danych

Ogólne

Jeśli na razie nie chcesz już korzystać z usługi, zalecamy wyłączenie aplikacji App Engine, aby uniknąć naliczania opłat. Jeśli jednak chcesz przeprowadzić więcej testów lub eksperymentów, platforma App Engine ma bezpłatny limit, więc dopóki nie przekroczysz tego poziomu wykorzystania, nie powinny być naliczane żadne opłaty. Dotyczy to obliczeń, ale mogą też wystąpić opłaty za odpowiednie usługi App Engine, więc więcej informacji znajdziesz na stronie z cennikiem. Jeśli migracja obejmuje inne usługi w chmurze, są one rozliczane oddzielnie. W każdym przypadku, jeśli to konieczne, zapoznaj się z sekcją „Specyficzne dla tego laboratorium” poniżej.

Wdrożenie na bezserwerowej platformie obliczeniowej Google Cloud, takiej jak App Engine, wiąże się z niewielkimi kosztami kompilacji i przechowywania. Cloud Build ma własny bezpłatny limit, podobnie jak Cloud Storage. Przechowywanie tego obrazu wykorzystuje część tego limitu. Możesz jednak mieszkać w regionie, w którym nie ma takiego bezpłatnego pakietu, więc kontroluj wykorzystanie miejsca na dane, aby zminimalizować potencjalne koszty. Sprawdź te „foldery” Cloud Storage:

  • 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
  • Linki do pamięci masowej powyżej zależą od PROJECT_ID i *LOC*acji, np. „us”, jeśli Twoja aplikacja jest hostowana w Stanach Zjednoczonych.

Jeśli nie zamierzasz kontynuować pracy z tą aplikacją ani innymi powiązanymi z nią samouczkami dotyczącymi migracji i chcesz wszystko całkowicie usunąć, wyłącz projekt.

Dotyczy tych ćwiczeń z programowania

Usługi wymienione poniżej są dostępne tylko w tym laboratorium. Więcej informacji znajdziesz w dokumentacji poszczególnych usług:

Pamiętaj, że jeśli nastąpiła migracja z modułu 15 do 16, nadal będziesz mieć dane w Blobstore, dlatego powyżej podajemy informacje o cenach tej usługi.

Dalsze kroki

Oprócz tego samouczka warto zapoznać się z innymi modułami migracji, które koncentrują się na przejściu ze starszych pakietów usług:

App Engine nie jest już jedyną platformą bezserwerową w Google Cloud. Jeśli masz małą aplikację App Engine lub aplikację o ograniczonej funkcjonalności i chcesz przekształcić ją w samodzielny mikroserwis albo podzielić aplikację monolityczną na wiele komponentów wielokrotnego użytku, warto rozważyć przejście na Cloud Functions. Jeśli konteneryzacja stała się częścią procesu tworzenia aplikacji, zwłaszcza jeśli obejmuje potok CI/CD (tryb ciągłej integracji/tryb ciągłego dostarczania lub wdrażanie), rozważ migrację do Cloud Run. Te scenariusze są omówione w tych modułach:

  • Migracja z App Engine do Cloud Functions: patrz moduł 11
  • Migracja z App Engine do Cloud Run: w module 4 dowiesz się, jak skonteneryzować aplikację za pomocą Dockera, a w module 5 – jak to zrobić bez kontenerów, wiedzy o Dockerze ani Dockerfiles

Przejście na inną platformę bezserwerową jest opcjonalne. Zanim wprowadzisz jakiekolwiek zmiany, zalecamy rozważenie najlepszych opcji dla Twoich aplikacji i przypadków użycia.

Niezależnie od tego, który moduł migracji wybierzesz, wszystkie materiały dotyczące Serverless Migration Station (ćwiczenia z programowania, filmy, kod źródłowy [jeśli jest dostępny]) znajdziesz w repozytorium open source. W repozytorium README znajdziesz też wskazówki dotyczące migracji, które warto rozważyć, oraz odpowiednią „kolejność” modułów migracji.

7. Dodatkowe materiały

Problemy z ćwiczeniami z programowania i opinie na ich temat

Jeśli zauważysz jakieś problemy z tym kursem, najpierw poszukaj rozwiązania, a dopiero potem zgłoś problem. Linki do wyszukiwania i tworzenia nowych problemów:

Materiały dotyczące migracji

Linki do folderów repozytorium dla modułu 15 (START) i modułu 16 (FINISH) znajdziesz w tabeli poniżej. Możesz też uzyskać do nich dostęp w repozytorium wszystkich migracji codelabów App Engine, które możesz sklonować lub pobrać jako plik ZIP.

Ćwiczenia z programowania

Python 2

Python 3

Moduł 15

kod

Nie dotyczy

Moduł 16 (to ćwiczenie)

kod

(tak samo jak w przypadku Pythona 2)

Zasoby online

Poniżej znajdziesz zasoby online, które mogą być przydatne w tym samouczku:

App Engine Blobstore i Cloud Storage

Platforma App Engine

Inne informacje o chmurze

Python

Filmy

Licencja

To zadanie jest licencjonowane na podstawie ogólnej licencji Creative Commons Attribution 2.0.