1. סקירה כללית
סדרת הServerless Migration Station של Codelabs (מדריכים מעשיים בקצב עצמי) וסרטונים קשורים נועדו לעזור להעביר מפתחים ללא שרת (serverless) של Google Cloud, באמצעות העברת אפליקציות מדור קודם באמצעות שירותי Google Cloud. כך האפליקציות שלכם יהיו יותר ניידות ויהיו לכם יותר אפשרויות וגמישות, כך שתוכלו להשתלב עם מגוון רחב יותר של מוצרי 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 עד 3 (האפליקציה שהועברה תואמת גם ל-Python 2 וגם ל-3)
להוראות מפורטות, אפשר לעיין במודולים קשורים של העברה.
כאן אפשר להבין איך
- הוספת שימוש ב-API/הספרייה של App Engine Blobstore
- אחסון העלאות של משתמשים לשירות Blobstore
- הכנה לשלב הבא של ההעברה ל-Cloud Storage
מה צריך להכין
- פרויקט ב-Google Cloud Platform עם חשבון פעיל לחיוב ב-GCP
- מיומנויות בסיסיות ב-Python
- ידע בעבודה עם פקודות Linux נפוצות
- ידע בסיסי פיתוח ופריסה של אפליקציות App Engine
- אפליקציית App Engine של מודול פעיל 15: משלימים את מודול 15 Codelab (מומלץ) או מעתיקים את אפליקציית מודול 15 מהמאגר
סקר
איך תשתמשו במדריך הזה?
איזה דירוג מגיע לדעתך לחוויה שלך עם Python?
איזה דירוג מגיע לדעתך לחוויית השימוש שלך בשירותי Google Cloud?
2. רקע
ה-Codelab הזה מתחיל באפליקציה לדוגמה ממודול 15 ומדגים איך לעבור מ-Bobstore (ו-NDB) ל-Cloud Storage (ו-Cloud NDB). תהליך ההעברה כולל החלפת יחסי תלות בשירותים בחבילה מדור קודם של App Engine, שמאפשרים להעביר את האפליקציות לפלטפורמה אחרת ללא שרת Cloud או לפלטפורמת אירוח אחרת לפי הצורך.
ההעברה הזו דורשת קצת יותר מאמץ בהשוואה להעברות אחרות בסדרה הזו. ל-blobstore יש תלות ב-framework המקורי של אפליקציית האינטרנט, ולכן האפליקציה לדוגמה משתמשת ב-framework של webapp2 במקום ב-Flask. המדריך הזה כולל העברות אל Cloud Storage, Cloud NDB, Flask ו-Python 3.
האפליקציה עדיין רושמת 'ביקורים' של משתמשי קצה ומציג את עשרת התוכנות האחרונות, אבל ב-Codelab הקודם (מודול 15) נוסף פונקציונליות חדשה כדי לאפשר שימוש ב-Bluobstore: האפליקציה מבקשת ממשתמשי הקצה להעלות פריט מידע שנוצר בתהליך הפיתוח (Artifact) (קובץ) שתואם ל'ביקור' שלהם. המשתמשים יכולים לעשות זאת או לבחור באפשרות 'דילוג' כדי לבטל את ההסכמה. ללא קשר להחלטת המשתמש, הדף הבא מעבד את אותו פלט כמו בגרסאות הקודמות של האפליקציה הזו, ומציג את הביקורים האחרונים. שינוי אחד נוסף הוא שבביקורים עם פריטי מידע שנוצרו בתהליך הפיתוח (Artifact) התואמים מקבלים 'תצוגה' קישור להצגת פריט המידע שנוצר בפגישה. ה-Codelab הזה מטמיע את ההעברות שצוינו קודם תוך שמירה על הפונקציונליות שתוארה.
3. הגדרה/עבודה מוקדמת
לפני שנעבור לחלק המרכזי של המדריך, בואו נגדיר את הפרויקט, נקבל את הקוד ואז נפרוס את אפליקציית הבסיס כדי שנדע שהתחלנו לעבוד עם קוד.
1. הגדרת הפרויקט
אם כבר פרסתם את אפליקציית מודול 15, מומלץ להשתמש שוב באותו פרויקט (ובקוד). לחלופין, אפשר ליצור פרויקט חדש לגמרי או להשתמש שוב בפרויקט קיים. צריך לוודא שלפרויקט יש חשבון פעיל לחיוב וש-App Engine מופעל.
2. אחזור של אפליקציה בסיסית לדוגמה
אחת הדרישות המוקדמות ל-Codelab הזה היא להיות אפליקציה לדוגמה של מודול 15 פעיל. אם לא התקנת אותו, אפשר לקבל אותו מהמודול 15 'START' תיקייה (קישור למטה). ה-Codelab הזה ינחה אותך לאורך כל שלב, ויסתיים בקוד שדומה לזה שבמודול 16 'FINISH' .
- START: תיקיית מודול 15 (Python 2)
- FINISH: תיקיית מודול 16 (Python 2)
- כל המאגר (לשכפול או הורדה של קובץ ZIP)
ספריית הקבצים של מודול 15 STARTing אמורה להיראות כך:
$ 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. (Re) פריסת אפליקציות בסיסיות
שאר השלבים לפני העבודה שצריך לבצע עכשיו:
- כדאי להכיר מחדש את כלי שורת הפקודה
gcloud
- פורסים מחדש את האפליקציה לדוגמה באמצעות
gcloud app deploy
- אישור שהאפליקציה פועלת ב-App Engine ללא בעיות
לאחר שתבצעו את השלבים האלה בהצלחה, ותוודאו שאפליקציית מודול 15 פועלת. הדף ההתחלתי מקבל הודעה למשתמשים באמצעות טופס שמבקש להעלות קובץ Artifact של ביקור שיש להעלות, עם אפשרות - 'דילוג' כדי לבטל את ההסכמה:
אחרי שהמשתמשים מעלים קובץ או מדלגים, האפליקציה מעבדת את הנתונים המוכרים "הביקורים האחרונים" page:
לביקורים שמוצג בהם פריט מידע שנוצר בתהליך הפיתוח (Artifact) יוצגו 'תצוגה' קישור משמאל לחותמת הזמן של הביקור כדי להציג (או להוריד) את פריט המידע שנוצר בתהליך הפיתוח (Artifact). אחרי שתאשרו את הפונקציונליות של האפליקציה, תוכלו לעבור מהשירותים הקודמים של 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
עם framework של Flask וספריות הלקוח של Cloud NDB ו-Cloud Storage. כולן לא מובנות. יוצרים את הקובץ עם התוכן הבא:
flask
google-cloud-ndb
google-cloud-storage
זמן הריצה של Python 2 App Engine דורש קיבוץ עצמי של ספריות צד שלישי לא מובנות, לכן צריך לבצע את הפקודה הבאה כדי להתקין את הספריות האלו בתיקייה lib:
pip install -t lib -r requirements.txt
אם במחשב הפיתוח שלכם מותקנת גרסת Python 2 וגם 3, יכול להיות שתצטרכו להשתמש בפקודה 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. אפשרות אחרת היא שנדרשת שליטה בגישה ל-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)
הנה תרשים של השינויים שבוצעו עד עכשיו:
עדכון רכיבי ה-handler
העלאת handler
handlers ב-webapp2
הם קורסים בזמן שהם פועלים ב-Flask. במקום שיטת פועל של HTTP, Flask משתמש בפועל כדי לקשט את הפונקציה. Blobstore ורכיבי ה-handler של webapp
מוחלפים בפונקציונליות של 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 מקושט. - שני המטפלים מסיימים את הפונקציונליות שלהם על ידי הפניה אוטומטית לדף הבית (
/
) תוך שמירה על בקשת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>'
). - בדומה ל-handler של ההעלאה, נדרשת עוד קצת עבודה בצד Cloud Storage בשביל פונקציונליות שמופשטת על ידי ה-handlers של Blobstore, כלומר זיהוי הקובץ (blob) הרלוונטי והורדה מפורשת של בקשת ה-method היחידה
send_blob()
של ה-handler הבינארי לעומת ה-blobstore. - אם לא נמצא פריט מידע שנוצר בתהליך הפיתוח (Artifact), בשני המקרים, תוחזר שגיאת HTTP 404.
handler עיקרי
השינויים האחרונים באפליקציה הראשית מתבצעים ב-handler הראשי. השיטות של פועל ה-HTTP webapp2
מוחלפות בפונקציה אחת שמשלבת את הפונקציונליות שלהן. מחליפים את המחלקה 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)
במקום להשתמש בשיטות get()
ו-post()
נפרדות, הן למעשה הצהרת if-else
ב-root()
. בנוסף, מכיוון ש-root()
היא פונקציה אחת, יש רק קריאה אחת לעיבוד התבנית גם עבור GET
וגם עבור POST
, אבל ב-webapp2
זה לא אפשרי.
הנה ייצוג גרפי של סדרת השינויים השנייה והסופית ב-main.py
:
(אופציונלי) תאימות לאחור 'שיפור'
הפתרון שנוצר למעלה עובד בצורה מושלמת... אבל רק אם התחלתם מאפס ואין לכם קבצים שנוצרו על ידי Blobstore. עדכנו את האפליקציה כך שהיא תזהה קבצים לפי שם הקובץ במקום BlobKey
, ולכן באפליקציית מודול 16 שהושלם לה אין אפשרות להציג קבצים של Blobstore. במילים אחרות, ביצענו שינוי שאינו תואם לאחור שמבצע את ההעברה הזו. עכשיו מוצגת גרסה חלופית של main.py
בשם main-migrate.py
(שנמצאת במאגר) שמנסה לגשר על הפער הזה.
ה"תוסף" הראשון לתמיכה בקבצים שנוצרו ב-Bluobstore הוא מודל נתונים שיש לו 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
:
אם רק התחלת את התהליך בלי קבצים שנוצרו ב-Blubstore, כדאי להשתמש ב-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 הזה בהחלפת App Engine Blobstore ב-Cloud Storage, App Engine NDB ב-Cloud NDB ו-webapp2
ב-Flask. הקוד אמור עכשיו להתאים למה שמופיע בתיקייה FINISH (מודול 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. למידע נוסף, יש לעיין בדף התמחור של האפליקציה. אם ההעברה הזו כוללת שירותי ענן אחרים, הם מחויבים בנפרד. בכל מקרה, אם רלוונטי, ראו "ספציפי ל-Codelab זה" שבהמשך.
גילוי נאות מלא, פריסה בפלטפורמת מחשוב ללא שרת (serverless) של Google Cloud, כמו App Engine, כרוכה בעלויות נמוכות של build ואחסון. ל-Cloud Build יש מכסה משלה בחינם, כמו גם ל-Cloud Storage. נפח האחסון של התמונה הזו תופס חלק מהמכסה. עם זאת, ייתכן שאתם גרים באזור שאין בו תוכנית ללא תשלום כזה, לכן כדאי שתהיו מודעים לשימוש שלכם בנפח האחסון כדי למזער את העלויות הפוטנציאליות. 'תיקיות' ספציפיות של 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
- הקישורים לנפח האחסון שלמעלה תלויים במאפיין
PROJECT_ID
ובמאפיין *LOC
*שלך, לדוגמה, "us
" אם האפליקציה מתארחת בארה"ב.
מצד שני, אם אתם לא מתכוונים להמשיך להשתמש באפליקציה הזו או ב-Codelabs קשורים אחרים ורוצים למחוק את הכול לחלוטין, כדאי להשבית את הפרויקט.
ספציפי ל-Codelab הזה
השירותים שמפורטים בהמשך הם ייחודיים ל-Codelab הזה. אפשר לקרוא מידע נוסף במסמכי התיעוד של כל מוצר:
- שירות App Engine Blobstore נכלל במכסות ובמגבלות של נתונים מאוחסנים, לכן צריך לבדוק את זה וכן את דף התמחור של שירותים בחבילה מדור קודם.
- ל-Cloud Storage יש רמה ללא תשלום לאזורים ספציפיים; אפשר למצוא מידע נוסף בדף התמחור הכללי.
- שירות App Engine Datastore ניתן על ידי Cloud Datastore (Cloud Firestore במצב Datastore), שגם לו יש תוכנית ללא תשלום; אפשר למצוא מידע נוסף בדף התמחור הזה."
שימו לב: אם עברתם ממודול 15 למודול 16, עדיין יהיו לכם נתונים ב-Bblobstore, ולכן אנחנו כוללים את פרטי התמחור שלו למעלה.
השלבים הבאים
מעבר למדריך הזה, יש מודולים אחרים להעברה שמתמקדים בהפסקת השימוש בשירותים בחבילה מהדור הקודם:
- מודול 2: העברה מ-App Engine
ndb
ל-Cloud NDB - מודולים 7-9: העברה ממשימות דחיפה לתור המשימות של App Engine אל Cloud Tasks
- מודולים 12-13: העברה מ-Memcache של App Engine ל-Cloud Memorystore
- מודולים 18-19: העברה מתור המשימות של App Engine (משימות משיכה) אל Cloud Pub/Sub
App Engine היא כבר לא הפלטפורמה היחידה ללא שרת (serverless) ב-Google Cloud. אם יש לכם אפליקציה קטנה של App Engine או אפליקציה שיש לה פונקציונליות מוגבלת ואתם רוצים להפוך אותה למיקרו-שירות (microservice) עצמאי, או שאתם רוצים לפצל אפליקציה מונוליתית למספר רכיבים לשימוש חוזר, כדאי לשקול לעבור ל-Cloud Functions. אם יצירת קונטיינרים הפכה לחלק מתהליך פיתוח האפליקציות שלכם, במיוחד אם היא מורכבת מצינור עיבוד נתונים של CI/CD (אינטגרציה רציפה (CI/CD)/פיתוח רציף (continuous delivery) או פריסה), מומלץ לעבור ל-Cloud Run. התרחישים האלה מתוארים במודולים הבאים:
- מעבר מ-App Engine ל-Cloud Functions: ראו מודול 11
- מעבר מ-App Engine ל-Cloud Run: אפשר לעיין במודול 4 ליצירת קונטיינרים לאפליקציה באמצעות Docker, או במודול 5 כדי לבצע אותו ללא קונטיינרים, ידע ב-Docker או
Dockerfile
המעבר לפלטפורמה אחרת ללא שרת הוא אופציונלי, ומומלץ לבדוק את האפשרויות הטובות ביותר לאפליקציות ולתרחישים לדוגמה לפני שמבצעים שינויים.
בלי קשר למודול ההעברה הרצוי, כל התוכן של תחנת המיגרציה ללא שרת (serverless) (codelabs, סרטונים, קוד מקור [אם הוא זמין]) יהיה זמין במאגר הקוד הפתוח שלו. README
של המאגר גם מספק הדרכה לגבי ההעברות שכדאי לשקול ו"הזמנה" רלוונטית של מודולי העברה.
7. מקורות מידע נוספים
משוב או בעיות ב-Codelab
אם נתקלתם בבעיות ב-Codelab הזה, צריך קודם לחפש את הבעיה לפני השליחה. קישורים לחיפוש וליצירת בעיות חדשות:
משאבים להעברה
בטבלה למטה מופיעים הקישורים לתיקיות המאגר של מודול 15 (START) ומודול 16 (FINISH). אפשר לגשת אליהן גם דרך המאגר לכל העברות Codelab ב-App Engine, שאותו ניתן לשכפל או להוריד בקובץ ZIP.
Codelab | ֶPython 2 | ֶPython 3 |
יחידת לימוד 15 | לא רלוונטי | |
יחידת לימוד 16 (Codelab זה) | (כמו ב-Python 2) |
מקורות מידע אונליין
בהמשך מופיעים מקורות מידע מקוונים שעשויים להיות רלוונטיים למדריך זה:
Blobstore ו-Cloud Storage של App Engine
- שירות Blobstore של App Engine
- מעבר לספריית הלקוח של Cloud Storage
- דף הבית של Cloud Storage
- מאמרי העזרה של Cloud Storage
פלטפורמת App Engine
- מסמכי התיעוד של App Engine
- זמן ריצה של Python 2 App Engine (סביבה סטנדרטית)
- שימוש בספריות מובנות של App Engine ב-App Engine Python 2
- זמן ריצה של Python 3 App Engine (סביבה סטנדרטית)
- ההבדלים בין Python 2 לבין Python 3 זמני ריצה של App Engine (סביבה רגילה)
- מדריך להעברת אפליקציות מ-Python 2-3 App Engine (סביבה סטנדרטית)
- מידע על תמחור ומכסות של App Engine
- השקת פלטפורמת App Engine מדור שני (2018)
- השוואה ראשונה ל- פלטפורמות דור שני
- תמיכה לטווח ארוך בסביבות זמני ריצה מדור קודם
- מאגר דוגמאות להעברת תיעוד
- מאגר דוגמאות למיגרציה שתורמים לקהילה
מידע אחר בענן
- Python ב-Google Cloud Platform
- ספריות הלקוח של Google Cloud Python
- "חינם תמיד" ב-Google Cloud שכבה
- Google Cloud SDK (כלי שורת הפקודה
gcloud
) - כל משאבי העזרה של Google Cloud
Python
- מערכות יצירה של Django ו-Jinja2
- מסגרת אינטרנט אחת (
webapp2
) - מסמכי תיעוד בנושא
webapp2
webapp2_extras
קישוריםwebapp2_extras
מסמכי תיעוד של Jinja2- Flask Web Framework
סרטונים
- תחנת העברה ללא שרת (serverless)
- קמפיינים ללא שרת (serverless)
- הרשמה למינוי Google Cloud Tech
- הרשמה ל-Google Developers
רישיון
היצירה הזו בשימוש ברישיון Creative Commons Attribution 2.0 גנרי.