1. סקירה כללית
סדרת ה-codelabs של Serverless Migration Station (מדריכים מעשיים בקצב אישי) והסרטונים הקשורים נועדו לעזור למפתחים של Google Cloud ללא שרת (serverless) לשדרג את האפליקציות שלהם, על ידי הדרכה בתהליך של העברה אחת או יותר, בעיקר משירותים מדור קודם. כך האפליקציות שלכם יהיו ניידות יותר, ויהיו לכם יותר אפשרויות וגמישות. תוכלו לשלב מגוון רחב יותר של מוצרי Cloud ולגשת אליהם, ולשדרג בקלות רבה יותר לגרסאות חדשות יותר של שפות. הסדרה מתמקדת בהתחלה במשתמשים הראשונים של Cloud, בעיקר במפתחים של App Engine (סביבת ברירת המחדל), אבל היא מספיק רחבה כדי לכלול פלטפורמות אחרות ללא שרת (serverless) כמו Cloud Functions ו-Cloud Run, או פלטפורמות אחרות אם רלוונטי.
ב-Codelab הזה תלמדו איך לעבור מ-App Engine Blobstore ל-Cloud Storage. יש גם העברות משתמעות מ:
- מסגרת האינטרנט
webapp2ל-Flask (מפורט ביחידת לימוד 1) - App Engine NDB ל-Cloud NDB לגישה ל-Datastore (מפורט ביחידת לימוד 2)
- Python 2 ל-Python 3 (האפליקציה שהועברה תואמת גם ל-Python 2 וגם ל-Python 3)
כדי לקבל מידע נוסף, אפשר לעיין במודולים הרלוונטיים להעברת נתונים.
כאן אפשר להבין איך
- הוספת השימוש ב-API או בספרייה של App Engine Blobstore
- אחסון העלאות של משתמשים בשירות Blobstore
- הכנה לשלב הבא במעבר ל-Cloud Storage
מה תצטרכו
- פרויקט ב-Google Cloud Platform עם חשבון לחיוב פעיל ב-GCP
- מיומנויות בסיסיות ב-Python
- ידע מעשי בפקודות נפוצות של Linux
- ידע בסיסי בפיתוח ופריסה של אפליקציות App Engine
- אפליקציה פעילה של App Engine מיחידת לימוד 15: משלימים את ה-Codelab של יחידת לימוד 15 (מומלץ) או מעתיקים את האפליקציה של יחידת לימוד 15 מהמאגר
סקר
איך תשתמשו במדריך הזה?
איך היית מדרג את הניסיון שלך עם Python?
איך היית מדרג/ת את חוויית השימוש שלך בשירותי Google Cloud?
2. רקע
ה-Codelab הזה מתחיל באפליקציה לדוגמה מיחידת לימוד 15, ומדגים איך לעבור מ-Blobstore (ו-NDB) ל-Cloud Storage (ו-Cloud NDB). תהליך ההעברה כולל החלפה של יחסי תלות בחבילות השירות מדור קודם של App Engine, שמאפשרות להעביר את האפליקציות לפלטפורמה אחרת ללא שרת (serverless) ב-Cloud או לפלטפורמת אירוח אחרת, אם רוצים.
ההעברה הזו דורשת קצת יותר מאמץ בהשוואה להעברות האחרות בסדרה הזו. ל-Blobstore יש יחסי תלות ב-webapp framework המקורי, ולכן האפליקציה לדוגמה משתמשת ב-webapp2 framework במקום ב-Flask. במדריך הזה נתמקד בהעברות ל-Cloud Storage, ל-Cloud NDB, ל-Flask ול-Python 3.
האפליקציה עדיין רושמת "ביקורים" של משתמשי קצה ומציגה את עשרת הביקורים האחרונים, אבל ב-Codelab הקודם (מודול 15) נוספה פונקציונליות חדשה כדי להתאים לשימוש ב-Blobstore: האפליקציה מבקשת ממשתמשי הקצה להעלות פריט מידע שנוצר בתהליך הפיתוח (קובץ) שתואם ל "ביקור" שלהם. המשתמשים יכולים לעשות את זה או לבחור באפשרות "דילוג" כדי לבטל את ההצטרפות. לא משנה מה המשתמש יבחר, בדף הבא יוצג אותו פלט כמו בגרסאות הקודמות של האפליקציה, עם הביקורים האחרונים. עוד דבר אחד: בביקורים עם פריטי מידע שקשורים אליהם יש קישור "הצגה" שמאפשר להציג את פריט המידע של הביקור. ב-Codelab הזה נטמיע את ההעברות שצוינו קודם, תוך שמירה על הפונקציונליות המתוארת.
3. הגדרה/הכנה
לפני שנגיע לחלק העיקרי של המדריך, נגדיר את הפרויקט, נקבל את הקוד ונפרוס את האפליקציה הבסיסית כדי שנדע שהתחלנו עם קוד תקין.
1. הגדרת הפרויקט
אם כבר פרסתם את האפליקציה של מודול 15, מומלץ להשתמש שוב באותו פרויקט (וקוד). אפשר גם ליצור פרויקט חדש או להשתמש בפרויקט קיים אחר. מוודאים שלפרויקט יש חשבון לחיוב פעיל ושהאפליקציה App Engine מופעלת.
2. הורדת אפליקציה לדוגמה בסיסית
אחד מהתנאים המוקדמים ל-Codelab הזה הוא אפליקציה לדוגמה של מודול 15. אם אין לכם אותה, אתם יכולים להוריד אותה מהתיקייה START של מודול 15 (קישור בהמשך). ב-Codelab הזה נסביר על כל שלב, ובסוף נציג קוד שדומה למה שמופיע בתיקייה FINISH של מודול 16.
- התחלה: התיקייה של יחידת לימוד 15 (Python 2)
- סיום: התיקייה של מודול 16 (Python 2)
- המאגר כולו (כדי לשכפל או להוריד קובץ ZIP)
הספרייה של קובצי START של מודול 15 אמורה להיראות כך:
$ ls README.md app.yaml main-gcs.py main.py templates
הקובץ main-gcs.py הוא גרסה חלופית של main.py ממודול 15, שמאפשרת לבחור קטגוריה של Cloud Storage ששונה מכתובת ה-URL שמוקצית לאפליקציה כברירת מחדל על סמך מזהה הפרויקט: PROJECT_ID.appspot.com. הקובץ הזה לא משתתף ב-Codelab הזה (מודול 16), אבל אפשר להחיל עליו טכניקות העברה דומות אם רוצים.
3. פריסה (מחדש) של אפליקציית הבסיס
השלבים שנותרו לביצוע לפני ההעברה:
- היכרות מחודשת עם כלי שורת הפקודה
gcloud - פורסים מחדש את האפליקציה לדוגמה באמצעות
gcloud app deploy - מוודאים שהאפליקציה פועלת ב-App Engine בלי בעיות
אחרי שתבצעו את השלבים האלה ותוודאו שהאפליקציה של מודול 15 פועלת, בדף הראשון מוצג למשתמשים טופס שבו הם מתבקשים להעלות קובץ של פריט מידע שנוצר במהלך הביקור, וגם כפתור "דילוג" שמאפשר להם לבטל את ההסכמה:

אחרי שהמשתמשים מעלים קובץ או מדלגים על השלב הזה, האפליקציה מציגה את הדף המוכר "הביקורים האחרונים":

בביקורים שכוללים פריט מידע, יופיע קישור "צפייה" משמאל לחותמת הזמן של הביקור, כדי להציג (או להוריד) את פריט המידע. אחרי שמוודאים שהאפליקציה פועלת, אפשר לעבור מהשירותים הקודמים של App Engine (webapp2, NDB, Blobstore) לחלופות העדכניות (Flask, Cloud NDB, Cloud Storage).
4. עדכון קובצי תצורה
בגרסה המעודכנת של האפליקציה שלנו יש שלושה קובצי תצורה. המשימות הנדרשות הן:
- עדכון ספריות צד שלישי מובנות שנדרשות ב-
app.yaml, וגם השארת האפשרות למעבר ל-Python 3 - מוסיפים
requirements.txtשמציין את כל הספריות הנדרשות שלא מובנות - מוסיפים את
appengine_config.pyכדי שהאפליקציה תתמוך גם בספריות מובנות וגם בספריות לא מובנות של צד שלישי
app.yaml
עורכים את הקובץ app.yaml על ידי עדכון הקטע libraries. מסירים את jinja2 ומוסיפים את grpcio, setuptools ו-ssl. בוחרים בגרסה העדכנית ביותר שזמינה לכל שלוש הספריות. מוסיפים גם את ההנחיה runtime של Python 3, אבל מסמנים אותה כהערה. בסיום התהליך, זה אמור להיראות כך (אם בחרתם ב-Python 3.9):
לפני:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
אחרי:
#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
השינויים קשורים בעיקר לספריות המובנות של Python 2 שזמינות בשרתי App Engine (כדי שלא תצטרכו לארוז אותן בעצמכם). הסרנו את Jinja2 כי היא מגיעה עם Flask, שאותה נוסיף ל-reqs.txt. בכל פעם שמשתמשים בספריות לקוח של Google Cloud, כמו אלה של Cloud NDB ו-Cloud Storage, צריך את grpcio ו-setuptools. לבסוף, Cloud Storage עצמו דורש את ספריית ה-SSL. הוראת זמן הריצה שסומנה כהערה בחלק העליון מיועדת למקרים שבהם אתם מוכנים להעביר את האפליקציה הזו ל-Python 3. נעסוק בנושא הזה בסוף המדריך.
requirements.txt
מוסיפים קובץ requirements.txt, שדורש את Flask framework ואת ספריות הלקוח של Cloud NDB ו-Cloud Storage, שאף אחת מהן לא מובנית. יוצרים את הקובץ עם התוכן הבא:
flask
google-cloud-ndb
google-cloud-storage
סביבת זמן הריצה של Python 2 ב-App Engine דורשת אריזה עצמית של ספריות צד שלישי לא מובנות, לכן צריך להריץ את הפקודה הבאה כדי להתקין את הספריות האלה בתיקיית lib:
pip install -t lib -r requirements.txt
אם במחשב הפיתוח מותקנות גם גרסה 2 וגם גרסה 3 של Python, יכול להיות שתצטרכו להשתמש בפקודה pip2 כדי לוודא שאתם מקבלים את גרסאות Python 2 של הספריות האלה. אחרי השדרוג ל-Python 3, לא צריך יותר לארוז את החבילות בעצמכם.
appengine_config.py
הוספת קובץ 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)
השלבים שביצעתם אמורים להיות דומים או זהים לשלבים שמפורטים בקטע התקנת ספריות לאפליקציות של Python 2 במסמכי התיעוד של App Engine. באופן ספציפי יותר, התוכן של appengine_config.py אמור להתאים למה שמופיע בשלב 5 שם.
סיימנו לעבוד על קובצי התצורה, אז בואו נעבור לאפליקציה.
5. שינוי קובצי אפליקציה
יבוא
הקבוצה הראשונה של השינויים ב-main.py כוללת החלפה של כל הדברים שמוחלפים. מה ישתנה?
-
webapp2מוחלף ב-Flask - במקום להשתמש ב-Jinja2 מ-
webapp2_extras, משתמשים ב-Jinja2 שמגיע עם Flask - Blobstore ו-NDB של App Engine מוחלפים ב-Cloud NDB וב-Cloud Storage
- רכיבי ה-handler של Blobstore ב-
webappמוחלפים בשילוב של המודולioשל הספרייה הרגילה, Flask וכלי העזר שלwerkzeug - כברירת מחדל, Blobstore כותב לקטגוריה של Cloud Storage שנקראת על שם כתובת ה-URL של האפליקציה (
PROJECT_ID.appspot.com). מכיוון שאנחנו מעבירים לספריית הלקוח של Cloud Storage, משתמשים ב-google.authכדי לקבל את מזהה הפרויקט ולציין את אותו שם קטגוריה בדיוק. (אפשר לשנות את שם הקטגוריה כי הוא כבר לא מקודד באופן קשיח).
לפני:
import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers
כדי להטמיע את השינויים שברשימה שלמעלה, מחליפים את קטע הייבוא הנוכחי בקובץ main.py בקטע הקוד שבהמשך.
אחרי:
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
אתחול ותמיכה מיותרת ב-Jinja2
בלוק הקוד הבא שצריך להחליף הוא BaseHandler, שמציין את השימוש ב-Jinja2 מ-webapp2_extras. זה מיותר כי Jinja2 מגיע עם Flask והוא מנוע התבניות שמוגדר כברירת מחדל, אז צריך להסיר אותו.
ביחידת לימוד 16, יוצרים אובייקטים שלא היו באפליקציה הישנה. זה כולל אתחול של אפליקציית Flask ויצירת לקוחות API ל-Cloud NDB ול-Cloud Storage. לבסוף, אנחנו מרכיבים את שם הקטגוריה של Cloud Storage כמו שמתואר למעלה בקטע "ייבוא". ככה זה נראה לפני ואחרי הטמעת העדכונים:
לפני:
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))
אחרי:
app = Flask(__name__)
ds_client = ndb.Client()
gcs_client = storage.Client()
_, PROJECT_ID = google.auth.default()
BUCKET = '%s.appspot.com' % PROJECT_ID
עדכון הגישה ל-Datastore
Cloud NDB תואם ברובו ל-App Engine NDB. הבדל אחד שכבר ציינו הוא הצורך בלקוח API. הבדל נוסף הוא שב-NDB, הגישה ל-Datastore צריכה להיות מנוהלת על ידי מנהל ההקשר של Python של לקוח ה-API. בעיקרון, זה אומר שכל הקריאות לגישה ל-Datastore באמצעות ספריית הלקוח Cloud NDB יכולות להתרחש רק בתוך בלוקים של Python with.
זה שינוי אחד. השינוי השני הוא ש-Blobstore והאובייקטים שלו, למשל BlobKey, לא נתמכים ב-Cloud Storage, ולכן צריך לשנות את file_blob ל-ndb.StringProperty. בהמשך מוצגים המחלקה של מודל הנתונים והפונקציות המעודכנות store_visit() ו-fetch_visits() שמשקפות את השינויים האלה:
לפני:
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)
אחרי:
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)
הנה ייצוג חזותי של השינויים שבוצעו עד עכשיו:

עדכון ה-handlers
רכיב לטיפול בהעלאות
רכיבי handler ב-webapp2 הם מחלקות, ואילו ב-Flask הם פונקציות. במקום שיטה של פועל HTTP, ב-Flask משתמשים בפועל כדי לקשט את הפונקציה. Blobstore ו-webapp handlers שלו מוחלפים בפונקציונליות מ-Cloud Storage, וגם מ-Flask ומהכלים שלו:
לפני:
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.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)
כמה הערות לגבי העדכון הזה:
- במקום
blob_id, פריטי קבצים מזוהים עכשיו לפי שם הקובץ (fname) אם הוא קיים, ולפיNoneאם לא (המשתמש בחר לא להעלות קובץ). - ה-handlers של Blobstore הפכו את תהליך ההעלאה למופשט עבור המשתמשים, אבל Cloud Storage לא עושה את זה, כך שאתם יכולים לראות את הקוד החדש שנוסף שמגדיר את אובייקט ה-blob ואת המיקום (קטגוריה) של הקובץ, וגם את הקריאה שמבצעת את ההעלאה בפועל. (
upload_from_file()). -
webapp2משתמש בטבלת ניתוב בתחתית קובץ האפליקציה, ואילו נתיבי Flask נמצאים בכל handler מעוטר. - שני ה-handlers מסיימים את הפונקציונליות שלהם על ידי הפניה אוטומטית לדף הבית (
/) תוך שמירה על הבקשהPOSTעם קוד החזרה HTTP 307.
רכיב ה-handler לעיבוד הורדות
העדכון של ה-handler להורדה דומה לזה של ה-handler להעלאה, אבל יש הרבה פחות קוד שצריך לבדוק. מחליפים את הפונקציונליות של Blobstore ו-webapp בפונקציונליות המקבילה של Cloud Storage ו-Flask:
לפני:
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.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)
הערות לגבי העדכון הזה:
- שוב, Flask מקשט פונקציות handler עם הנתיב שלהן, בעוד ש-
webappעושה זאת בטבלת ניתוב בתחתית, אז זהה את תחביר התאמת התבניות של האחרון('/view/([^/]+)?') לעומת זה של Flask ('/view/<path:fname>'). - כמו ב-upload handler, נדרשת קצת יותר עבודה בצד של Cloud Storage כדי להשיג את הפונקציונליות שמופשטת על ידי ה-handlers של Blobstore, כלומר זיהוי הקובץ (blob) המדובר והורדה מפורשת של הקובץ הבינארי, לעומת קריאה אחת ל-method
send_blob()של ה-handler של Blobstore. - בשני המקרים, אם פריט המידע לא נמצא, המשתמש מקבל שגיאת HTTP 404.
ה-handler הראשי
השינויים הסופיים באפליקציה הראשית מתבצעים ב-handler הראשי. ה-methods של הפועל webapp2 ב-HTTP הוחלפו בפונקציה אחת שמשלבת את הפונקציונליות שלהן. מחליפים את המחלקה MainHandler בפונקציה root() ומסירים את טבלת הניתוב webapp2, כמו שמוצג כאן:
לפני:
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)
אחרי:
@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)
במקום methods נפרדים מסוג get() ו-post(), הם בעצם הצהרת if-else ב-root(). בנוסף, מכיוון ש-root() היא פונקציה אחת, יש רק קריאה אחת לעיבוד התבנית גם ל-GET וגם ל-POST, בעוד שזה לא ממש אפשרי ב-webapp2.
כאן אפשר לראות ייצוג חזותי של קבוצת השינויים השנייה והאחרונה ב-main.py:

(אופציונלי) "שיפור" התאימות לדורות קודמים
אז הפתרון שיצרנו למעלה עובד בצורה מושלמת... אבל רק אם אתם מתחילים מאפס ואין לכם קבצים שנוצרו על ידי Blobstore. עדכנו את האפליקציה כדי לזהות קבצים לפי שם הקובץ במקום לפי BlobKey, ולכן האפליקציה שהושלמה במודול 16 לא תוכל להציג קובצי Blobstore כמו שהם. במילים אחרות, ביצענו שינוי שאינו תואם לאחור במהלך ההעברה הזו. עכשיו אנחנו מציגים גרסה חלופית של main.py שנקראת main-migrate.py (נמצאת במאגר) שמנסה לגשר על הפער הזה.
"ההרחבה" הראשונה שתומכת בקבצים שנוצרו ב-Blobstore היא מודל נתונים שיש לו BlobKeyProperty (בנוסף ל-StringProperty לקבצים שנוצרו ב-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()
המאפיין file_blob ישמש לזיהוי קבצים שנוצרו על ידי Blobstore, והמאפיין file_gcs ישמש לזיהוי קבצים של Cloud Storage. עכשיו, כשיוצרים ביקורים חדשים, מאחסנים ערך באופן מפורש ב-file_gcs במקום ב-file_blob, כך שהאירוע store_visit נראה קצת אחרת:
לפני:
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 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()
כשמאחזרים את הביקורים האחרונים, "מנרמלים" את הנתונים לפני ששולחים אותם לתבנית:
לפני:
@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)
אחרי:
@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)
בשלב הבא, צריך לאשר את קיומו של file_blob או file_gcs (או אף אחד מהם). אם יש קובץ זמין, בוחרים את הקובץ שקיים ומשתמשים במזהה הזה (BlobKey לקבצים שנוצרו ב-Blobstore או שם הקובץ לקבצים שנוצרו ב-Cloud Storage). כשאנחנו אומרים "קבצים שנוצרו ב-Cloud Storage", אנחנו מתכוונים לקבצים שנוצרו באמצעות ספריית הלקוח של Cloud Storage. גם Blobstore כותב ב-Cloud Storage, אבל במקרה הזה, אלה קבצים שנוצרו על ידי Blobstore.
עכשיו, מה שחשוב יותר, מהי הפונקציה etl_visits() שמשמשת לנרמול או ל-ETL (חילוץ, טרנספורמציה וטעינה) של הנתונים עבור משתמש הקצה? כך הוא נראה:
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]
הוא כנראה נראה כמו שציפיתם: הקוד עובר בלולאה על כל הביקורים, ובכל ביקור, לוקח את נתוני המבקר וחותמת הזמן כפי שהם, ואז בודק אם file_gcs או file_blob קיימים, ואם כן, בוחר אחד מהם (או None אם אף אחד מהם לא קיים).
הנה איור שממחיש את ההבדלים בין main.py לבין main-migrate.py:

אם אתם מתחילים מאפס בלי קבצים שנוצרו על ידי Blobstore, השתמשו ב-main.py, אבל אם אתם עוברים ורוצים לתמוך בקבצים שנוצרו על ידי Blobstore וגם Cloud Storage, עיינו ב-main-migrate.py כדוגמה להתמודדות עם תרחיש כזה כדי לעזור לכם לתכנן העברות לאפליקציות שלכם. כשמבצעים העברות מורכבות, סביר להניח שיתעוררו מקרים מיוחדים, ולכן הדוגמה הזו נועדה להראות זיקה גדולה יותר למודרניזציה של אפליקציות אמיתיות עם נתונים אמיתיים.
6. סיכום/ניקוי
בקטע הזה נסיים את ה-Codelab: נפרוס את האפליקציה, נוודא שהיא פועלת כמו שצריך ונבדוק את הפלט. אחרי אימות האפליקציה, מבצעים את כל שלבי הניקוי וחושבים על השלבים הבאים.
פריסה ואימות של האפליקציה
לפני שפורסים מחדש את האפליקציה, חשוב להריץ את pip install -t lib -r requirements.txt כדי להעביר את הספריות של הצד השלישי שכלולות בחבילה לתיקיית lib. אם רוצים להריץ את הפתרון שתואם לדורות קודמים, צריך קודם לשנות את השם של main-migrate.py ל-main.py. עכשיו מריצים את gcloud app deploy ומוודאים שהאפליקציה פועלת באופן זהה לאפליקציה של מודול 15. מסך הטופס נראה כך:

כך נראה הדף "הביקורים האחרונים":

כל הכבוד, סיימתם את ה-Codelab הזה שבו החלפתם את Blobstore של App Engine ב-Cloud Storage, את NDB של App Engine ב-Cloud NDB ואת webapp2 ב-Flask. הקוד שלכם אמור להיות זהה לקוד שבתיקייה FINISH (Module 16). החלופה main-migrate.py נמצאת גם היא בתיקייה הזו.
"העברה" ל-Python 3
ההנחיה runtime של Python 3 שמסומנת כהערה בחלק העליון של app.yaml היא כל מה שצריך כדי להעביר את האפליקציה הזו ל-Python 3. קוד המקור עצמו כבר תואם ל-Python 3, כך שלא צריך לבצע בו שינויים. כדי לפרוס את האפליקציה כאפליקציית Python 3, מבצעים את השלבים הבאים:
- מבטלים את ההערה של ההוראה
runtimeשל Python 3 בחלק העליון שלapp.yaml. - מוחקים את כל השורות האחרות בקובץ
app.yaml. - מוחקים את הקובץ
appengine_config.py. (לא בשימוש בסביבת זמן הריצה של Python 3) - מוחקים את התיקייה
libאם היא קיימת. (לא נדרש בסביבת זמן ריצה של Python 3)
הסרת המשאבים
כללי
אם סיימתם לעת עתה, מומלץ להשבית את אפליקציית App Engine כדי להימנע מחיוב. עם זאת, אם אתם רוצים לבדוק או להתנסות עוד קצת, לפלטפורמת App Engine יש מכסה בחינם, וכל עוד לא תחרוג מרמת השימוש הזו, לא תחויבו. זה לגבי המחשוב, אבל יכול להיות שיהיו גם חיובים על שירותים רלוונטיים של App Engine, אז כדאי לבדוק את דף התמחור שלו כדי לקבל מידע נוסף. אם ההעברה הזו כוללת שירותי Cloud אחרים, הם מחויבים בנפרד. בכל מקרה, אם רלוונטי, עיינו בקטע "ספציפי ל-Codelab הזה" שבהמשך.
חשוב לדעת: פריסה בפלטפורמת מחשוב בלי שרת (serverless computing) של Google Cloud, כמו App Engine, כרוכה בעלויות קטנות של פיתוח ואחסון. ל-Cloud Build יש מכסה משלו לשימוש בחינם, וכך גם ל-Cloud Storage. האחסון של התמונה הזו מנצל חלק מהמכסה הזו. עם זאת, יכול להיות שאתם גרים באזור שאין בו תוכנית בחינם, אז כדאי לשים לב לנפח אחסון נדרש שאתם משתמשים בו כדי לצמצם עלויות פוטנציאליות. "תיקיות" ספציפיות ב-Cloud Storage שכדאי לבדוק:
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- הקישורים לאחסון שלמעלה תלויים ב
PROJECT_IDוב *LOC*ation, לדוגמה, "us" אם האפליקציה מתארחת בארה"ב.
מצד שני, אם אתם לא מתכוונים להמשיך עם האפליקציה הזו או עם שיעורי Lab אחרים שקשורים להעברה, ואתם רוצים למחוק הכול, השביתו את הפרויקט.
ספציפי ל-Codelab הזה
השירותים שמפורטים בהמשך ייחודיים ל-Codelab הזה. מידע נוסף זמין במסמכי התיעוד של כל מוצר:
- שירות Blobstore של App Engine כפוף למכסות ולמגבלות של נתונים מאוחסנים, לכן כדאי לעיין בהן וגם בדף התמחור של חבילות שירות מדור קודם.
- ל-Cloud Storage יש תוכנית בחינם לאזורים ספציפיים. מידע נוסף זמין בדף התמחור הכללי.
- שירות App Engine Datastore מסופק על ידי Cloud Datastore (Cloud Firestore במצב Datastore), שגם לו יש תוכנית בחינם. מידע נוסף זמין בדף התמחור שלו."
שימו לב שאם עברתם ממודול 15 למודול 16, עדיין יהיו לכם נתונים ב-Blobstore, ולכן כללנו למעלה את פרטי התמחור שלו.
השלבים הבאים
בנוסף למדריך הזה, יש עוד מודולים של העברה שמתמקדים במעבר מחבילות השירותים הקודמות, למשל:
- מודול 2: מעבר מ-
ndbב-App Engine ל-Cloud NDB - יחידות לימוד 7-9: מעבר ממשימות של דחיפת הודעות בתור המשימות של App Engine ל-Cloud Tasks
- יחידות לימוד 12-13: מעבר מ-App Engine Memcache ל-Cloud Memorystore
- מודולים 18-19: מעבר מתור המשימות של App Engine (שליפת הודעות) ל-Cloud Pub/Sub
App Engine היא כבר לא הפלטפורמה היחידה בלי שרת (serverless) ב-Google Cloud. אם יש לכם אפליקציה קטנה ב-App Engine או אפליקציה עם פונקציונליות מוגבלת, ואתם רוצים להפוך אותה למיקרו-שירות עצמאי, או אם אתם רוצים לפצל אפליקציה מונוליתית למספר רכיבים שניתן לעשות בהם שימוש חוזר, אלה סיבות טובות לשקול מעבר ל-Cloud Functions. אם הכללת קונטיינרים הפכה לחלק מתהליך הפיתוח של האפליקציה, במיוחד אם הוא כולל צינור עיבוד נתונים של CI/CD (אינטגרציה רציפה/פריסה או פיתוח רציף), כדאי לעבור ל-Cloud Run. התרחישים האלה מוסברים במודולים הבאים:
- מעבר מ-App Engine ל-Cloud Functions: ראו מודול 11
- מעבר מ-App Engine ל-Cloud Run: ביחידת לימוד 4 מוסבר איך להוסיף את האפליקציה לקונטיינר באמצעות Docker, וביחידת לימוד 5 מוסבר איך לעשות את זה בלי קונטיינרים, בלי ידע ב-Docker ובלי
Dockerfile
המעבר לפלטפורמה אחרת בלי שרת (serverless) הוא אופציונלי, ומומלץ לשקול את האפשרויות הטובות ביותר לאפליקציות ולתרחישי השימוש שלכם לפני שתבצעו שינויים.
לא משנה באיזה מודול העברה תבחרו, תוכלו לגשת לכל התוכן של Serverless Migration Station (שיעורי Lab ב-Codelabs, סרטונים, קוד מקור [אם זמין]) במאגר הקוד הפתוח שלו. בREADME של המאגר יש גם הנחיות לגבי ההעברות שכדאי לשקול ו"סדר" רלוונטי של מודולי העברה.
7. מקורות מידע נוספים
בעיות או משוב בנוגע ל-Codelab
אם נתקלתם בבעיות ב-Codelab הזה, חפשו את הבעיה לפני שתדווחו עליה. קישורים לחיפוש וליצירת דיווחים או בקשות חדשים:
משאבים להעברת נתונים
בטבלה הבאה מופיעים קישורים לתיקיות המאגר של מודול 15 (START) ומודול 16 (FINISH). אפשר גם לגשת אליהם מהמאגר של כל ההעברות ב-App Engine codelab, שאותו אפשר לשכפל או להוריד כקובץ ZIP.
Codelab | Python 2 | Python 3 |
יחידת לימוד 15 | לא רלוונטי | |
מודול 16 (ה-Codelab הזה) | (כמו ב-Python 2) |
משאבים באינטרנט
בהמשך מופיעים מקורות מידע באינטרנט שעשויים להיות רלוונטיים למדריך הזה:
App Engine Blobstore ו-Cloud Storage
- שירות App Engine Blobstore
- מעבר לספריית הלקוח של Cloud Storage
- דף הבית של Cloud Storage
- מאמרי העזרה של Cloud Storage
פלטפורמת App Engine
- מסמכי התיעוד של App Engine
- סביבת זמן הריצה של Python 2 App Engine (סביבת ברירת המחדל)
- שימוש בספריות המובנות של App Engine ב-Python 2 App Engine
- סביבת זמן הריצה של Python 3 ב-App Engine (סביבה רגילה)
- ההבדלים בין סביבות זמן הריצה של Python 2 ו-Python 3 ב-App Engine (סביבת ברירת המחדל)
- מדריך להעברת נתונים (מיגרציה) של App Engine (סביבת ברירת המחדל) מ-Python 2 ל-Python 3
- מידע על התמחור והמכסות של App Engine
- השקת פלטפורמת App Engine מהדור השני (2018)
- השוואה בין פלטפורמות מהדור הראשון והשני
- תמיכה לטווח ארוך בסביבות זמן ריצה מדור קודם
- מאגר דוגמאות להעברת מסמכים
- מאגר דוגמאות להעברה שנוצרו על ידי הקהילה
מידע נוסף על Cloud
- Python ב-Google Cloud Platform
- ספריות לקוח של Google Cloud Python
- מסלול Always Free של Google Cloud
- Google Cloud SDK (כלי שורת הפקודה
gcloud) - כל מסמכי התיעוד של Google Cloud
Python
- מערכות התבניות Django ו-Jinja2
- framework לאינטרנט של
webapp2 - מסמכי התיעוד של
webapp2 webapp2_extrasקישוריםwebapp2_extrasמסמכי התיעוד של Jinja2- Flask web framework
סרטונים
- Serverless Migration Station
- Serverless Expeditions
- הרשמה לערוץ Google Cloud Tech
- הרשמה ל-Google Developers
רישיון
היצירה הזו מוגנת ברישיון Creative Commons Attribution 2.0 Generic.