مهاجرت از App Engine Blobstore به Cloud Storage (ماژول 16)

۱. مرور کلی

مجموعه آزمایشگاه‌های کد Serverless Migration Station (آموزش‌های عملی و خودآموز) و ویدیوهای مرتبط با آن ، با هدف کمک به توسعه‌دهندگان Google Cloud serverless برای مدرن‌سازی برنامه‌هایشان، با راهنمایی آنها در طول یک یا چند مهاجرت، و در درجه اول دور شدن از سرویس‌های قدیمی، ارائه می‌شوند. انجام این کار، برنامه‌های شما را قابل حمل‌تر می‌کند و گزینه‌ها و انعطاف‌پذیری بیشتری به شما می‌دهد و شما را قادر می‌سازد تا با طیف وسیع‌تری از محصولات Cloud ادغام شده و به آنها دسترسی داشته باشید و به راحتی به نسخه‌های جدیدتر زبان ارتقا دهید. در حالی که در ابتدا بر روی اولین کاربران Cloud، در درجه اول توسعه‌دهندگان App Engine (محیط استاندارد)، تمرکز دارد، این مجموعه به اندازه کافی گسترده است که شامل سایر پلتفرم‌های serverless مانند Cloud Functions و Cloud Run یا در صورت لزوم، هر جای دیگری نیز می‌شود.

این آزمایشگاه کد به شما آموزش می‌دهد که چگونه از App Engine Blobstore به Cloud Storage مهاجرت کنید. همچنین مهاجرت‌های ضمنی از موارد زیر نیز وجود دارد:

برای اطلاعات گام به گام بیشتر، به هر یک از ماژول‌های مهاجرت مرتبط مراجعه کنید.

یاد خواهید گرفت که چگونه

  • استفاده از API/کتابخانه‌ی App Engine Blobstore را اضافه کنید
  • ذخیره آپلودهای کاربر در سرویس Blobstore
  • برای مرحله بعدی مهاجرت به فضای ذخیره‌سازی ابری آماده شوید

آنچه نیاز دارید

نظرسنجی

چگونه از این آموزش استفاده خواهید کرد؟

فقط تا انتها بخوانید آن را بخوانید و تمرین‌ها را انجام دهید

تجربه خود را با پایتون چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

تجربه خود را در استفاده از خدمات ابری گوگل چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

۲. پیشینه

این آزمایشگاه کد با برنامه نمونه از ماژول ۱۵ شروع می‌شود و نحوه مهاجرت از Blobstore (و NDB) به Cloud Storage (و Cloud NDB) را نشان می‌دهد. فرآیند مهاجرت شامل جایگزینی وابستگی‌ها در سرویس‌های همراه قدیمی App Engine است که به شما امکان می‌دهد برنامه‌های خود را در صورت تمایل به یک پلتفرم بدون سرور Cloud یا پلتفرم میزبانی دیگر منتقل کنید.

این مهاجرت در مقایسه با سایر مهاجرت‌های این مجموعه به تلاش بیشتری نیاز دارد. Blobstore به چارچوب اصلی webapp وابستگی دارد و به همین دلیل است که برنامه نمونه به جای Flask از چارچوب webapp2 استفاده می‌کند. این آموزش شامل مهاجرت به Cloud Storage، Cloud NDB، Flask و Python 3 است.

این برنامه هنوز «بازدیدهای» کاربر نهایی را ثبت می‌کند و ده مورد آخر را نمایش می‌دهد، اما codelab قبلی (ماژول ۱۵) قابلیت جدیدی را برای تطبیق با استفاده از Blobstore اضافه کرده است: برنامه از کاربران نهایی می‌خواهد که یک مصنوع (یک فایل) مربوط به «بازدید» خود را آپلود کنند. کاربران می‌توانند این کار را انجام دهند یا برای انصراف، «رد شدن» را انتخاب کنند. صرف نظر از تصمیم کاربر، صفحه بعد همان خروجی نسخه‌های قبلی این برنامه را ارائه می‌دهد و جدیدترین بازدیدها را نمایش می‌دهد. یک تغییر دیگر این است که بازدیدهایی با مصنوعات مربوطه، دارای یک لینک «مشاهده» برای نمایش مصنوع بازدید هستند. این codelab مهاجرت‌های ذکر شده قبلی را پیاده‌سازی می‌کند و در عین حال عملکرد شرح داده شده را حفظ می‌کند.

۳. تنظیمات/پیش‌پردازش

قبل از اینکه به بخش اصلی آموزش برسیم، بیایید پروژه خود را راه‌اندازی کنیم، کد را دریافت کنیم، سپس برنامه پایه را مستقر کنیم تا بدانیم که با کد کار را شروع کرده‌ایم.

۱. پروژه راه‌اندازی

اگر قبلاً برنامه ماژول ۱۵ را مستقر کرده‌اید، توصیه می‌کنیم از همان پروژه (و کد) دوباره استفاده کنید. همچنین می‌توانید یک پروژه کاملاً جدید ایجاد کنید یا از یک پروژه موجود دیگر دوباره استفاده کنید. مطمئن شوید که پروژه دارای یک حساب صورتحساب فعال است و App Engine فعال است.

۲. نمونه برنامه پایه را دریافت کنید

یکی از پیش‌نیازهای این آزمایشگاه کد، داشتن یک برنامه نمونه ماژول ۱۵ است که کار کند. اگر آن را ندارید، می‌توانید آن را از پوشه "START" ماژول ۱۵ (لینک زیر) دریافت کنید. این آزمایشگاه کد، شما را در هر مرحله راهنمایی می‌کند و در پایان، کدی مشابه آنچه در پوشه "FINISH" ماژول ۱۶ وجود دارد، ارائه می‌دهد.

دایرکتوری فایل‌های STARTING ماژول ۱۵ باید به شکل زیر باشد:

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

فایل main-gcs.py یک نسخه جایگزین از main.py از ماژول ۱۵ است که امکان انتخاب یک باکت ذخیره‌سازی ابری متفاوت از پیش‌فرض URL اختصاص داده شده به برنامه را بر اساس شناسه پروژه فراهم می‌کند: PROJECT_ID .appspot.com . این فایل هیچ نقشی در این codelab (ماژول ۱۶) ندارد، به جز اینکه در صورت تمایل می‌توان تکنیک‌های مهاجرت مشابه را روی آن فایل اعمال کرد.

۳. (دوباره) استقرار برنامه پایه

مراحل مقدماتی باقی مانده برای اجرا:

  1. با ابزار خط فرمان gcloud دوباره آشنا شوید
  2. برنامه نمونه را با استفاده از gcloud app deploy دوباره مستقر کنید
  3. تأیید کنید که برنامه بدون مشکل در App Engine اجرا می‌شود.

وقتی این مراحل را با موفقیت انجام دادید و از کارکرد برنامه ماژول ۱۵ خود اطمینان حاصل کردید، صفحه اولیه با فرمی به کاربران خوشامد می‌گوید که از آنها می‌خواهد فایل مصنوع بازدید را به همراه یک گزینه، یعنی دکمه «رد شدن»، آپلود کنند تا از این فرآیند صرف نظر کنند:

f5b5f9f19d8ae978.png

به محض اینکه کاربران فایلی را آپلود کنند یا از آن صرف نظر کنند، برنامه صفحه آشنای «آخرین بازدیدها» را نمایش می‌دهد:

f5ac6b98ee8a34c.png

بازدیدهایی که شامل یک مصنوع (artifact) هستند، یک لینک «مشاهده» در سمت راست مهر زمانی بازدید برای نمایش (یا دانلود) مصنوع خواهند داشت. پس از تأیید عملکرد برنامه، آماده مهاجرت از سرویس‌های قدیمی App Engine (webapp2، NDB، Blobstore) به جایگزین‌های معاصر (Flask، Cloud NDB، Cloud Storage) خواهید بود.

۴. به‌روزرسانی فایل‌های پیکربندی

سه فایل پیکربندی برای نسخه به‌روز شده برنامه ما اجرا می‌شوند. وظایف مورد نیاز عبارتند از:

  1. کتابخانه‌های شخص ثالث داخلی مورد نیاز را در app.yaml به‌روزرسانی کنید و همچنین در را برای مهاجرت به پایتون ۳ باز بگذارید.
  2. یک requirements.txt اضافه کنید که تمام کتابخانه‌های مورد نیاز که داخلی نیستند را مشخص می‌کند.
  3. appengine_config.py را اضافه کنید تا برنامه از کتابخانه‌های شخص ثالث داخلی و خارجی پشتیبانی کند.

برنامه.yaml

فایل app.yaml خود را با به‌روزرسانی بخش libraries ویرایش کنید. jinja2 را حذف کرده و grpcio ، setuptools و ssl را اضافه کنید. آخرین نسخه موجود برای هر سه کتابخانه را انتخاب کنید. همچنین دستورالعمل runtime پایتون ۳ را اضافه کنید، اما به صورت کامنت. وقتی کارتان تمام شد، باید به این شکل باشد (اگر پایتون ۳.۹ را انتخاب کرده‌اید):

قبل از:

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

این تغییرات عمدتاً مربوط به کتابخانه‌های داخلی پایتون ۲ موجود در سرورهای App Engine است (بنابراین لازم نیست خودتان آنها را دسته بندی کنید). ما Jinja2 را حذف کردیم زیرا با Flask همراه است که قرار است آن را به reqs.txt اضافه کنیم. هر زمان که از کتابخانه‌های کلاینت Google Cloud، مانند کتابخانه‌های Cloud NDB و Cloud Storage، استفاده می‌شود، grpcio و setuptools مورد نیاز هستند. در نهایت، خود Cloud Storage به کتابخانه ssl نیاز دارد. دستورالعمل زمان اجرا که در بالا کامنت گذاری شده است، برای زمانی است که شما آماده انتقال این برنامه به پایتون ۳ هستید. ما این موضوع را در پایان این آموزش پوشش خواهیم داد.

الزامات.txt

یک فایل requirements.txt اضافه کنید که به چارچوب Flask و کتابخانه‌های کلاینت Cloud NDB و Cloud Storage نیاز دارد، که هیچ‌کدام از آن‌ها داخلی نیستند. فایل را با این محتوا ایجاد کنید:

flask
google-cloud-ndb
google-cloud-storage

موتور اجرای پایتون ۲ (Python 2 App Engine) نیاز به بسته‌بندی خودکار کتابخانه‌های شخص ثالث غیر داخلی دارد، بنابراین دستور زیر را برای نصب این کتابخانه‌ها در پوشه lib اجرا کنید:

pip install -t lib -r requirements.txt

اگر هم پایتون ۲ و هم پایتون ۳ را روی دستگاه توسعه خود دارید، ممکن است مجبور شوید از دستور pip2 برای اطمینان از دریافت نسخه‌های پایتون ۲ این کتابخانه‌ها استفاده کنید. پس از ارتقا به پایتون ۳، دیگر نیازی به بسته‌بندی خودکار ندارید.

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)

مراحلی که به تازگی انجام شده‌اند باید مشابه یا یکسان با مراحل ذکر شده در بخش «نصب کتابخانه‌ها برای برنامه‌های پایتون ۲» در مستندات App Engine باشند، و به طور خاص‌تر، محتویات appengine_config.py باید با آنچه در مرحله ۵ آمده است، مطابقت داشته باشد.

کار روی فایل‌های پیکربندی تمام شد، پس بیایید به سراغ برنامه برویم.

۵. فایل‌های برنامه را تغییر دهید

واردات

اولین مجموعه تغییرات برای main.py شامل تعویض تمام موارد جایگزین شده است. در اینجا مواردی که تغییر می‌کنند را مشاهده می‌کنید:

  1. webapp2 با Flask جایگزین شده است.
  2. به جای استفاده از Jinja2 از webapp2_extras ، از Jinja2 که همراه با Flask ارائه می‌شود استفاده کنید.
  3. موتور برنامه، Blobstore و NDB با Cloud NDB و Cloud Storage جایگزین شده‌اند.
  4. هندلرهای Blobstore در webapp با ترکیبی از ماژول کتابخانه استاندارد io ، ابزارهای Flask و werkzeug جایگزین شده‌اند.
  5. به طور پیش‌فرض، Blobstore در یک مخزن ذخیره‌سازی ابری که نام آن از URL برنامه شما ( PROJECT_ID.appspot.com ) گرفته شده است، می‌نویسد. از آنجا که ما در حال انتقال به کتابخانه کلاینت ذخیره‌سازی ابری هستیم، google.auth برای دریافت شناسه پروژه و مشخص کردن دقیقاً همان نام مخزن استفاده می‌شود. (از آنجایی که دیگر کدنویسی نشده است، می‌توانید نام مخزن را تغییر دهید.)

قبل از:

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

تغییرات لیست بالا را با جایگزینی بخش import فعلی در 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 ارائه می‌شود و موتور قالب‌بندی پیش‌فرض آن است، بنابراین آن را حذف کنید.

در سمت ماژول ۱۶، اشیاء را که در برنامه قدیمی‌تر نداشتیم، نمونه‌سازی می‌کنیم. این شامل مقداردهی اولیه برنامه Flask و ایجاد کلاینت‌های API برای Cloud NDB و Cloud Storage می‌شود. در نهایت، نام باکت Cloud Storage را همانطور که در بخش importها در بالا توضیح داده شد، کنار هم قرار می‌دهیم. در اینجا قبل و بعد از پیاده‌سازی این به‌روزرسانی‌ها آمده است:

قبل از:

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

به‌روزرسانی دسترسی به پایگاه داده

Cloud NDB عمدتاً با App Engine NDB سازگار است. یکی از تفاوت‌هایی که قبلاً به آن پرداخته شد، نیاز به یک کلاینت API است. تفاوت دیگر این است که مورد دوم نیاز به کنترل دسترسی به Datastore توسط مدیر زمینه پایتون کلاینت API دارد. اساساً، این بدان معناست که تمام فراخوانی‌های دسترسی به Datastore با استفاده از کتابخانه کلاینت Cloud NDB فقط می‌توانند در پایتون 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)

در اینجا تصویری از تغییراتی که تاکنون ایجاد شده است را مشاهده می‌کنید:

a8f74ca392275822.png

به‌روزرسانی هندلرها

کنترل کننده آپلود

هندلرها در webapp2 کلاس هستند در حالی که در Flask تابع هستند. به جای یک متد فعل HTTP، Flask از فعل برای تزئین تابع استفاده می‌کند. Blobstore و هندلرهای 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 (هیچکدام) شناسایی می‌شوند (کاربر از آپلود فایل انصراف داده است).
  • کنترل‌کننده‌های Blobstore فرآیند آپلود را از کاربران خود جدا می‌کردند، اما Cloud Storage این کار را نمی‌کند، بنابراین می‌توانید کد تازه اضافه شده را که شیء blob فایل و مکان (bucket) و همچنین فراخوانی که آپلود واقعی را انجام می‌دهد ( upload_from_file() ) را تنظیم می‌کند، مشاهده کنید.
  • webapp2 از یک جدول مسیریابی در پایین فایل برنامه استفاده می‌کند در حالی که مسیرهای Flask در هر handler تزئین شده یافت می‌شوند.
  • هر دو کنترل‌کننده با هدایت به home ( / ) و حفظ درخواست POST با کد بازگشتی HTTP 307، عملکرد خود را به پایان می‌رسانند.

دانلود هندلر

به‌روزرسانی کنترل‌کننده‌ی دانلود از الگویی مشابه کنترل‌کننده‌ی آپلود پیروی می‌کند، فقط کد بسیار کمتری برای بررسی وجود دارد. قابلیت‌های 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)

نکاتی در مورد این به‌روزرسانی:

  • باز هم، فلسک توابع هندلر را با مسیرشان تزئین می‌کند در حالی که webapp این کار را در یک جدول مسیریابی در پایین انجام می‌دهد، بنابراین سینتکس تطبیق الگوی دومی ('/view/([^/]+)?' ) را در مقابل سینتکس فلسک ( '/view/<path:fname>' ) تشخیص دهید.
  • همانند کنترل‌کننده آپلود، در سمت ذخیره‌سازی ابری برای عملکردهایی که توسط کنترل‌کننده‌های Blobstore خلاصه شده‌اند، کار بیشتری لازم است، یعنی شناسایی فایل (blob) مورد نظر و دانلود صریح فایل باینری در مقابل فراخوانی تکی متد send_blob() کنترل‌کننده Blobstore.
  • در هر دو مورد، اگر مصنوع یافت نشود، خطای HTTP 404 به کاربر بازگردانده می‌شود.

متصدی اصلی

آخرین تغییرات در برنامه اصلی در هندلر اصلی (main 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 آورده شده است:

5ec38818c32fec2.png

(اختیاری) "بهبود" سازگاری با نسخه‌های قبلی

بنابراین راهکار ایجاد شده در بالا کاملاً کار می‌کند... اما فقط در صورتی که از ابتدا شروع کنید و فایل‌هایی که توسط Blobstore ایجاد شده‌اند را نداشته باشید. از آنجا که ما برنامه را به‌روزرسانی کردیم تا فایل‌ها را به جای BlobKey با نام فایل شناسایی کند، برنامه ماژول ۱۶ تکمیل شده به همین صورت قادر به مشاهده فایل‌های 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_blob ، صریحاً مقداری را در file_gcs ذخیره کنید، بنابراین 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 یا filename برای فایل‌های ایجاد شده توسط 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 آورده شده است:

718b05b2adadb2e1.png

اگر از ابتدا و بدون فایل‌های ایجاد شده توسط Blobstore شروع می‌کنید، main.py استفاده کنید، اما اگر در حال گذار هستید و می‌خواهید فایل‌های ایجاد شده توسط Blobstore و Cloud Storage را پشتیبانی کنید، main-migrate.py به عنوان نمونه‌ای از نحوه برخورد با سناریویی مانند کمک به شما در برنامه‌ریزی مهاجرت برای برنامه‌های خود بررسی کنید. هنگام انجام مهاجرت‌های پیچیده، احتمالاً موارد خاصی پیش می‌آید، بنابراین این مثال برای نشان دادن تمایل بیشتر برای مدرن‌سازی برنامه‌های واقعی با داده‌های واقعی است.

۶. خلاصه/پاکسازی

این بخش، این آزمایشگاه کد را با استقرار برنامه، تأیید عملکرد آن طبق برنامه و در هر خروجی منعکس‌شده، به پایان می‌رساند. پس از اعتبارسنجی برنامه، مراحل پاکسازی را انجام داده و مراحل بعدی را در نظر بگیرید.

استقرار و تأیید برنامه

قبل از استقرار مجدد برنامه، حتماً pip install -t lib -r requirements.txt را اجرا کنید تا کتابخانه‌های شخص ثالثِ خود-بسته‌بندی‌شده در پوشه lib قرار گیرند. اگر می‌خواهید راه‌حل سازگار با نسخه‌های قبلی را اجرا کنید، ابتدا main-migrate.py را به main.py تغییر نام دهید. اکنون gcloud app deploy اجرا کنید و تأیید کنید که برنامه دقیقاً مانند برنامه ماژول ۱۵ کار می‌کند. صفحه فرم به این شکل خواهد بود:

f5b5f9f19d8ae978.png

صفحه آخرین بازدیدها به این شکل است:

f5ac6b98ee8a34c.png

تبریک می‌گویم که این کد را با جایگزینی App Engine Blobstore با Cloud Storage، App Engine NDB با Cloud NDB و webapp2 با Flask تکمیل کردید. اکنون کد شما باید با آنچه در پوشه FINISH (ماژول ۱۶) است مطابقت داشته باشد. فایل جایگزین main-migrate.py نیز در آن پوشه موجود است.

پایتون ۳ "مهاجرت"

دستورالعمل runtime پایتون ۳ که در بالای app.yaml کامنت‌گذاری شده است، تمام چیزی است که برای انتقال این برنامه به پایتون ۳ مورد نیاز است. خود کد منبع از قبل با پایتون ۳ سازگار است، بنابراین نیازی به تغییر در آن نیست. برای استقرار این برنامه به عنوان یک برنامه پایتون ۳، مراحل زیر را انجام دهید:

  1. دستور runtime پایتون ۳ را در بالای app.yaml از حالت کامنت خارج کنید.
  2. تمام خطوط دیگر را در app.yaml حذف کنید.
  3. فایل appengine_config.py را حذف کنید. (در زمان اجرای پایتون ۳ استفاده نشده است)
  4. در صورت وجود، پوشه lib را حذف کنید. (با پایتون ۳ در زمان اجرا غیر ضروری است)

تمیز کردن

عمومی

اگر فعلاً کارتان تمام است، توصیه می‌کنیم برنامه App Engine خود را غیرفعال کنید تا از پرداخت هزینه جلوگیری شود. با این حال، اگر می‌خواهید بیشتر آزمایش یا تجربه کنید، پلتفرم App Engine سهمیه رایگان دارد و بنابراین تا زمانی که از آن سطح استفاده تجاوز نکنید، نباید هزینه‌ای از شما دریافت شود. این هزینه برای محاسبات است، اما ممکن است برای سرویس‌های مربوطه App Engine نیز هزینه‌هایی وجود داشته باشد، بنابراین برای اطلاعات بیشتر به صفحه قیمت‌گذاری آن مراجعه کنید. اگر این مهاجرت شامل سایر سرویس‌های ابری باشد، هزینه آنها جداگانه محاسبه می‌شود. در هر صورت، در صورت لزوم، به بخش "ویژه این codelab" در زیر مراجعه کنید.

برای روشن شدن کامل موضوع، استقرار در یک پلتفرم محاسباتی بدون سرور Google Cloud مانند App Engine هزینه‌های ساخت و ذخیره‌سازی کمی را متحمل می‌شود. 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 *ation شما بستگی دارند، برای مثال، اگر برنامه شما در ایالات متحده میزبانی می‌شود، " us " خواهد بود.

از طرف دیگر، اگر قصد ندارید با این برنامه یا سایر آزمایشگاه‌های کد مهاجرت مرتبط ادامه دهید و می‌خواهید همه چیز را به طور کامل حذف کنید، پروژه خود را ببندید .

مخصوص این آزمایشگاه کد

سرویس‌های ذکر شده در زیر مختص این codelab هستند. برای اطلاعات بیشتر به مستندات هر محصول مراجعه کنید:

توجه داشته باشید که اگر از ماژول ۱۵ به ۱۶ مهاجرت کرده‌اید، همچنان داده‌ها را در Blobstore خواهید داشت، به همین دلیل اطلاعات قیمت‌گذاری آن را در بالا آورده‌ایم.

مراحل بعدی

فراتر از این آموزش، ماژول‌های مهاجرت دیگری که بر دور شدن از سرویس‌های همراه قدیمی تمرکز دارند، عبارتند از:

App Engine دیگر تنها پلتفرم بدون سرور در Google Cloud نیست. اگر یک برنامه کوچک App Engine یا برنامه‌ای با قابلیت‌های محدود دارید و می‌خواهید آن را به یک میکروسرویس مستقل تبدیل کنید، یا می‌خواهید یک برنامه یکپارچه را به چندین مؤلفه قابل استفاده مجدد تقسیم کنید، اینها دلایل خوبی برای در نظر گرفتن انتقال به Cloud Functions هستند. اگر کانتینرسازی به بخشی از گردش کار توسعه برنامه شما تبدیل شده است، به خصوص اگر شامل یک خط لوله CI/CD (ادغام مداوم/تحویل مداوم یا استقرار) باشد، مهاجرت به Cloud Run را در نظر بگیرید. این سناریوها توسط ماژول‌های زیر پوشش داده می‌شوند:

  • مهاجرت از موتور برنامه به توابع ابری: به ماژول 11 مراجعه کنید
  • مهاجرت از App Engine به Cloud Run: برای کانتینرایز کردن برنامه خود با Docker به ماژول ۴ و برای انجام این کار بدون کانتینرها، دانش Docker یا Dockerfile ها به ماژول ۵ مراجعه کنید.

تغییر به یک پلتفرم بدون سرور دیگر اختیاری است و توصیه می‌کنیم قبل از ایجاد هرگونه تغییر، بهترین گزینه‌ها را برای برنامه‌ها و موارد استفاده خود در نظر بگیرید.

صرف نظر از اینکه کدام ماژول مهاجرت را در مرحله بعد در نظر بگیرید، تمام محتوای Serverless Migration Station (آزمایشگاه‌های کد، ویدیوها، کد منبع [در صورت وجود]) در مخزن متن‌باز آن قابل دسترسی است. README این مخزن همچنین راهنمایی‌هایی در مورد اینکه کدام مهاجرت‌ها را باید در نظر گرفت و هرگونه «ترتیب» مربوط به ماژول‌های مهاجرت ارائه می‌دهد.

۷. منابع اضافی

مشکلات/بازخوردهای Codelab

اگر در این آزمایشگاه کد مشکلی پیدا کردید، لطفاً قبل از ثبت، ابتدا مشکل خود را جستجو کنید. لینک‌های جستجو و ایجاد مشکلات جدید:

منابع مهاجرت

لینک‌های پوشه‌های مخزن ماژول ۱۵ (START) و ماژول ۱۶ (FINISH) را می‌توانید در جدول زیر پیدا کنید. همچنین می‌توانید از مخزن تمام مهاجرت‌های Codelab مربوط به App Engine که می‌توانید آن‌ها را کلون کنید یا یک فایل ZIP دانلود کنید، به آن‌ها دسترسی داشته باشید.

کدلب

پایتون ۲

پایتون ۳

ماژول ۱۵

کد

ناموجود

ماژول ۱۶ (این آزمایشگاه کد)

کد

(همانند پایتون ۲)

منابع آنلاین

در زیر منابع آنلاینی وجود دارد که ممکن است برای این آموزش مرتبط باشند:

موتور برنامه Blobstore و فضای ذخیره‌سازی ابری

پلتفرم موتور برنامه

سایر اطلاعات ابری

پایتون

ویدیوها

مجوز

این اثر تحت مجوز عمومی Creative Commons Attribution 2.0 منتشر شده است.