App Engine Blobstore থেকে Cloud Storage-এ মাইগ্রেট করা (মডিউল ১৬)

১. এক নজরে

সার্ভারলেস মাইগ্রেশন স্টেশন সিরিজের কোডল্যাব (নিজের গতিতে, হাতে-কলমে টিউটোরিয়াল) এবং সম্পর্কিত ভিডিও Google Cloud সার্ভারলেস ডেভেলপারদের এক বা একাধিক মাইগ্রেশনের মাধ্যমে তাদের অ্যাপ্লিকেশন আধুনিকীকরণে সাহায্য করার উদ্দেশ্যে তৈরি করা হয়েছে, প্রাথমিকভাবে লিগ্যাসি পরিষেবা থেকে সরে আসার জন্য। এটি করলে আপনার অ্যাপ আরও পোর্টেবল হয়ে যায় এবং আপনি আরও বিকল্প ও সুবিধা পান, যার ফলে আপনি আরও বেশি রেঞ্জের Cloud প্রোডাক্টের সাথে ইন্টিগ্রেট ও অ্যাক্সেস করতে পারেন এবং আরও সহজে নতুন ভাষার রিলিজে আপগ্রেড করতে পারেন। প্রাথমিকভাবে Cloud-এর প্রথম দিকের ব্যবহারকারীদের উপর ফোকাস করা হলেও, মূলত App Engine (স্ট্যান্ডার্ড এনভায়রনমেন্ট) ডেভেলপারদের জন্য এই সিরিজটি যথেষ্ট বিস্তৃত যাতে Cloud Functions এবং Cloud Run-এর মতো অন্যান্য সার্ভারলেস প্ল্যাটফর্ম বা প্রযোজ্য হলে অন্য কোথাও অন্তর্ভুক্ত করা যায়।

এই কোডল্যাব থেকে আপনি শিখতে পারবেন কীভাবে App Engine Blobstore থেকে Cloud Storage-এ মাইগ্রেট করতে হয়। এছাড়াও, এখান থেকে ইমপ্লিসিট মাইগ্রেশন করা হয়:

ধাপে ধাপে আরও তথ্য পেতে, মাইগ্রেশন সম্পর্কিত যেকোনও মডিউল দেখুন।

কীভাবে করতে হয় আপনি তা শিখবেন

  • App Engine Blobstore API/লাইব্রেরির ব্যবহার যোগ করা
  • Blobstore পরিষেবায় ব্যবহারকারীর আপলোড করা ফাইল স্টোর করুন
  • Cloud Storage-এ মাইগ্রেট করার জন্য পরবর্তী ধাপের প্রস্তুতি নিন

আপনার যা প্রয়োজন হবে

সমীক্ষা

আপনি এই টিউটোরিয়াল কীভাবে ব্যবহার করবেন?

শুধু এটি পড়ুন এটি পড়ুন এবং অনুশীলন সম্পূর্ণ করুন

Python-এর সাথে আপনার অভিজ্ঞতাকে আপনি কত রেটিং দেবেন?

শিক্ষানবিস ইন্টারমিডিয়েট দক্ষ

Google Cloud পরিষেবা ব্যবহার করার অভিজ্ঞতাকে আপনি কত রেটিং দেবেন?

শিক্ষানবিস ইন্টারমিডিয়েট দক্ষ

২. ব্যাকগ্রাউন্ড

এই কোডল্যাব মডিউল ১৫-এর স্যাম্পেল অ্যাপ থেকে শুরু হয় এবং Blobstore (ও NDB) থেকে Cloud Storage (ও Cloud NDB)-এ কীভাবে মাইগ্রেট করতে হয় তা দেখায়। মাইগ্রেশন প্রসেসে App Engine-এর পুরনো বান্ডেল করা পরিষেবার উপর নির্ভরতা প্রতিস্থাপন করা হয়, যা আপনাকে নিজের অ্যাপ অন্য Cloud সার্ভারলেস প্ল্যাটফর্ম বা অন্য হোস্টিং প্ল্যাটফর্মে সরাতে দেয়, যদি আপনি চান।

এই সিরিজের অন্যান্য মাইগ্রেশনের তুলনায় এই মাইগ্রেশনের জন্য একটু বেশি পরিশ্রম করতে হবে। Blobstore-এর আসল webapp ফ্রেমওয়ার্কের উপর নির্ভরতা আছে এবং সেই কারণেই স্যাম্পেল অ্যাপ Flask-এর পরিবর্তে webapp2 ফ্রেমওয়ার্ক ব্যবহার করে। এই টিউটোরিয়ালে Cloud Storage, Cloud NDB, Flask ও Python 3-তে মাইগ্রেশন দেখানো হয়েছে।

অ্যাপটি এখনও ব্যবহারকারীর "ভিজিট" রেজিস্টার করে এবং সাম্প্রতিক দশটি ভিজিট দেখায়, তবে আগের (মডিউল ১৫) কোডল্যাব Blobstore ব্যবহারের জন্য নতুন কার্যকারিতা যোগ করেছে: অ্যাপটি ব্যবহারকারীদের তাদের "ভিজিট" অনুযায়ী একটি আর্টিফ্যাক্ট (একটি ফাইল) আপলোড করতে প্রম্পট করে। ব্যবহারকারীরা এটি করতে পারেন অথবা অপ্ট-আউট করতে "এড়িয়ে যান" বিকল্প বেছে নিতে পারেন। ব্যবহারকারীর সিদ্ধান্ত যাই হোক না কেন, পরবর্তী পৃষ্ঠায় এই অ্যাপের আগের ভার্সনের মতো একই আউটপুট দেখায়, যেখানে সবচেয়ে সাম্প্রতিক ভিজিট দেখানো হয়। আরেকটি অতিরিক্ত টুইস্ট হল, সংশ্লিষ্ট আর্টিফ্যাক্ট সহ ভিজিটে একটি "ভিউ" লিঙ্ক থাকে যা ভিজিটের আর্টিফ্যাক্ট দেখায়। এই কোডল্যাব, বর্ণিত কার্যকারিতা বজায় রেখে আগে উল্লেখ করা মাইগ্রেশন প্রয়োগ করে।

৩. সেট-আপ/প্রিওয়ার্ক

টিউটোরিয়ালের মূল অংশে যাওয়ার আগে, আমাদের প্রোজেক্ট সেট-আপ করে, কোড নিয়ে, তারপর বেসলাইন অ্যাপ ডিপ্লয় করতে হবে যাতে আমরা জানতে পারি যে আমরা কাজ করছে এমন কোড দিয়ে শুরু করেছি।

১. প্রোজেক্ট সেট-আপ করুন

আপনি যদি ইতিমধ্যেই মডিউল ১৫ অ্যাপ ডিপ্লয় করে থাকেন, তাহলে আমরা সেই একই প্রোজেক্ট (এবং কোড) আবার ব্যবহার করার সাজেশন দিই। এছাড়াও, আপনি একেবারে নতুন প্রোজেক্ট তৈরি করতে অথবা আগে থেকে থাকা অন্য প্রোজেক্ট আবার ব্যবহার করতে পারবেন। প্রোজেক্টে একটি অ্যাক্টিভ বিলিং অ্যাকাউন্ট আছে এবং App Engine চালু আছে কিনা তা নিশ্চিত করুন।

২. বেসলাইন স্যাম্পেল অ্যাপ ডাউনলোড করুন

এই কোডল্যাবের অন্যতম পূর্বশর্ত হল, একটি কার্যকরী মডিউল ১৫ স্যাম্পেল অ্যাপ থাকা। আপনার কাছে এটি না থাকলে, আপনি মডিউল ১৫ "START" ফোল্ডার (নিচে লিঙ্ক দেওয়া আছে) থেকে এটি পেতে পারেন। এই কোডল্যাব আপনাকে প্রতিটি ধাপের মাধ্যমে নিয়ে যায়, মডিউল ১৬ "শেষ" ফোল্ডারে যা আছে তার মতো কোড দিয়ে শেষ হয়।

মডিউল ১৫-এর STARTing ফাইলের ডিরেক্টরি দেখতে এইরকম হওয়া উচিত:

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

main-gcs.py ফাইলটি হল মডিউল ১৫ থেকে main.py-এর একটি বিকল্প ভার্সন যা প্রোজেক্টের আইডির উপর ভিত্তি করে অ্যাপের অ্যাসাইন করা URL-এর ডিফল্ট থেকে আলাদা একটি Cloud Storage বাকেট বেছে নেওয়ার অনুমতি দেয়: PROJECT_ID.appspot.com। এই (মডিউল ১৬) কোডল্যাবে এই ফাইলের কোনও ভূমিকা নেই, তবে আপনি চাইলে একই ধরনের মাইগ্রেশন টেকনিক এই ফাইলের ক্ষেত্রেও প্রয়োগ করতে পারেন।

৩. বেসলাইন অ্যাপ (আবার) ডিপ্লয় করা

এখন কার্যকর করার জন্য আপনার বাকি থাকা প্রি-ওয়ার্ক ধাপ:

  1. gcloud কমান্ড-লাইন টুল সম্পর্কে আবার জেনে নিন
  2. gcloud app deploy-এর সাথে স্যাম্পেল অ্যাপ আবার ডিপ্লয় করুন
  3. অ্যাপটি App Engine-এ কোনও সমস্যা ছাড়াই চলছে কিনা তা কনফার্ম করুন

আপনি এইসব ধাপ সম্পূর্ণ করার পরে, আপনার Module 15 অ্যাপ কাজ করছে কিনা তা কনফার্ম করুন। প্রাথমিক পৃষ্ঠায় ব্যবহারকারীদের একটি ফর্মের মাধ্যমে স্বাগত জানানো হয়, যেখানে একটি বিকল্প সহ ভিজিট আর্টিফ্যাক্ট ফাইল আপলোড করার জন্য প্রম্পট করা হয়, অপ্ট-আউট করার জন্য একটি "এড়িয়ে যান" বোতাম থাকে:

f5b5f9f19d8ae978.png

ব্যবহারকারী ফাইল আপলোড করলে বা এড়িয়ে গেলে, অ্যাপটি পরিচিত "সাম্প্রতিক ভিজিট" পৃষ্ঠাটি দেখায়:

f5ac6b98ee8a34cb.png

আর্টিফ্যাক্ট সহ ভিজিটের ক্ষেত্রে, ভিজিট টাইমস্ট্যাম্পের ডানদিকে একটি "দেখুন" লিঙ্ক থাকবে, যা আর্টিফ্যাক্টটি দেখাবে (বা ডাউনলোড করবে)। অ্যাপের কার্যকারিতা কনফার্ম করার পরে, আপনি App Engine-এর পুরনো পরিষেবা (webapp2, NDB, Blobstore) থেকে সমসাময়িক বিকল্পে (Flask, Cloud NDB, Cloud Storage) মাইগ্রেট করার জন্য প্রস্তুত।

৪. কনফিগারেশন ফাইল আপডেট করুন

আমাদের অ্যাপের আপডেট করা ভার্সনের জন্য তিনটি কনফিগারেশন ফাইল ব্যবহার করা হয়। প্রয়োজনীয় টাস্কগুলি হল:

  1. app.yaml-এ প্রয়োজনীয় বিল্ট-ইন থার্ড-পার্টি লাইব্রেরি আপডেট করা এবং Python 3-এ মাইগ্রেট করার সুযোগ খোলা রাখা
  2. একটি requirements.txt যোগ করুন, যা বিল্ট-ইন নয় এমন সমস্ত প্রয়োজনীয় লাইব্রেরি নির্দিষ্ট করে
  3. appengine_config.py যোগ করুন যাতে অ্যাপটি বিল্ট-ইন এবং নন-বিল্ট-ইন থার্ড-পার্টি লাইব্রেরি উভয়কেই সাপোর্ট করে

app.yaml

libraries বিভাগ আপডেট করে আপনার app.yaml ফাইল এডিট করুন। jinja2 সরিয়ে দিন এবং grpcio, setuptools ও ssl যোগ করুন। তিনটি লাইব্রেরির জন্যই উপলভ্য লেটেস্ট ভার্সন বেছে নিন। এছাড়াও, Python 3 runtime ডিরেক্টিভ যোগ করুন, তবে কমেন্ট হিসেবে চিহ্নিত করুন। আপনার কাজ হয়ে গেলে, এটি দেখতে এইরকম হওয়া উচিত (আপনি 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

পরিবর্তনগুলি মূলত App Engine সার্ভারে উপলভ্য Python 2 বিল্ট-ইন লাইব্রেরির সাথে সম্পর্কিত (তাই আপনাকে সেগুলি সেল্ফ-বান্ডেল করতে হবে না)। আমরা Jinja2 সরিয়ে দিয়েছি কারণ এটি Flask-এর সাথে আসে, যা আমরা reqs.txt-এ যোগ করতে চলেছি। Cloud NDB ও Cloud Storage-এর মতো Google Cloud ক্লায়েন্ট লাইব্রেরি ব্যবহার করা হলে, grpcio ও setuptools প্রয়োজন হয়। অবশেষে, Cloud Storage-এর জন্য ssl লাইব্রেরি প্রয়োজন। উপরে কমেন্ট হিসেবে চিহ্নিত করা রানটাইম নির্দেশিকাটি হল আপনি যখন এই অ্যাপটি Python 3-এ পোর্ট করার জন্য প্রস্তুত হন। এই টিউটোরিয়ালের শেষে আমরা এই বিষয়ে আলোচনা করব।

requirements.txt

Flask ফ্রেমওয়ার্ক, Cloud NDB ও Cloud Storage ক্লায়েন্ট লাইব্রেরি প্রয়োজন এমন একটি requirements.txt ফাইল যোগ করুন, যার কোনওটিই বিল্ট-ইন নয়। এই কন্টেন্ট দিয়ে ফাইল তৈরি করুন:

flask
google-cloud-ndb
google-cloud-storage

Python 2 App Engine রানটাইমে নন-বিল্ট-ইন থার্ড-পার্টি লাইব্রেরির সেল্ফ-বান্ডলিং প্রয়োজন, তাই lib ফোল্ডারে এইসব লাইব্রেরি ইনস্টল করতে নিম্নলিখিত কমান্ড এক্সিকিউট করুন:

pip install -t lib -r requirements.txt

আপনার ডেভেলপমেন্ট মেশিনে Python 2 এবং 3 দুটিই থাকলে, এই লাইব্রেরিগুলির Python 2 ভার্সন পেতে আপনাকে pip2 কমান্ড ব্যবহার করতে হতে পারে। 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)

এইমাত্র সম্পূর্ণ করা ধাপগুলি App Engine ডকুমেন্টের Python 2 অ্যাপের জন্য লাইব্রেরি ইনস্টল করা বিভাগে তালিকাভুক্ত ধাপগুলির মতো বা অভিন্ন হওয়া উচিত এবং আরও নির্দিষ্টভাবে, appengine_config.py-এর কন্টেন্ট সেখানে ৫ নম্বর ধাপে যা আছে তার সাথে মিলতে হবে।

কনফিগারেশন ফাইলের কাজ সম্পূর্ণ হয়েছে, তাই এবার অ্যাপ্লিকেশন নিয়ে কাজ করা যাক।

৫. অ্যাপ্লিকেশন ফাইল পরিবর্তন করুন

ইমপোর্ট

main.py-এর জন্য প্রথম সেট পরিবর্তনের মধ্যে রয়েছে, যেসব জিনিস পরিবর্তন করা হচ্ছে সেগুলি অদলবদল করা। কী কী পরিবর্তন করা হচ্ছে তা এখানে দেওয়া হল:

  1. webapp2-এর পরিবর্তে Flask ব্যবহার করা হয়
  2. webapp2_extras থেকে Jinja2 ব্যবহার করার পরিবর্তে, Flask-এর সাথে আসা Jinja2 ব্যবহার করুন
  3. App Engine Blobstore ও NDB-এর পরিবর্তে Cloud NDB ও Cloud Storage ব্যবহার করা হয়
  4. webapp-এ Blobstore হ্যান্ডলারের পরিবর্তে io স্ট্যান্ডার্ড লাইব্রেরি মডিউল, Flask এবং werkzeug ইউটিলিটির কম্বো ব্যবহার করা হয়
  5. ডিফল্ট হিসেবে, Blobstore আপনার অ্যাপের URL (PROJECT_ID.appspot.com) অনুযায়ী নাম দেওয়া Cloud Storage বাকেটে লেখে। যেহেতু আমরা 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 যা webapp2_extras থেকে Jinja2-এর ব্যবহার নির্দিষ্ট করে। This is unnecessary because Jinja2 comes with Flask and is its default templating engine, so remove it.

মডিউল ১৬-এর ক্ষেত্রে, পুরনো অ্যাপে ছিল না এমন অবজেক্ট ইনস্ট্যানশিয়েট করুন। এর মধ্যে Flask অ্যাপ ইনিশিয়ালাইজ করা এবং Cloud NDB ও Cloud Storage-এর জন্য API ক্লায়েন্ট তৈরি করা অন্তর্ভুক্ত। অবশেষে, আমরা ইমপোর্ট বিভাগে উপরে বর্ণিত 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 অ্যাক্সেস API ক্লায়েন্টের Python কনটেক্সট ম্যানেজার দ্বারা কন্ট্রোল করা প্রয়োজন। মূলত, এর অর্থ হল Cloud NDB ক্লায়েন্ট লাইব্রেরি ব্যবহার করে সব Datastore অ্যাক্সেস কল শুধুমাত্র 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)

এখনও পর্যন্ত যেসব পরিবর্তন করা হয়েছে তার একটি সচিত্র উপস্থাপনা এখানে দেওয়া হল:

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 তা করে না, তাই আপনি নতুন যোগ করা কোড দেখতে পাবেন যা ফাইলের ব্লব অবজেক্ট ও লোকেশন (বাকেট) সেট করে এবং সেইসাথে কল যা প্রকৃত আপলোড করে। (upload_from_file())।
  • webapp2 অ্যাপ্লিকেশন ফাইলের নিচে একটি রাউটিং টেবিল ব্যবহার করে, যেখানে প্রতিটি ডেকোরেটেড হ্যান্ডলারে Flask রুট পাওয়া যায়।
  • দুটি হ্যান্ডলারই HTTP 307 রিটার্ন কোডের মাধ্যমে POST অনুরোধ সংরক্ষণ করার সময় হোমে ( / ) রিডাইরেক্ট করে তাদের কার্যকারিতা শেষ করে।

ডাউনলোড হ্যান্ডলার

ডাউনলোড হ্যান্ডলার আপডেট করার পদ্ধতি আপলোড হ্যান্ডলারের মতোই, শুধু এখানে অনেক কম কোড দেখতে হয়। 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 হ্যান্ডলার ফাংশনকে তাদের রুটের সাথে সাজায়, যেখানে webapp এটি নিচে একটি রাউটিং টেবিলে করে, তাই পরেরটির প্যাটার্ন ম্যাচিং সিনট্যাক্স ('/view/([^/]+)?') বনাম Flask-এর ('/view/<path:fname>') চিনুন।
  • আপলোড হ্যান্ডলারের মতো, ব্লবস্টোর হ্যান্ডলারের মাধ্যমে অ্যাবস্ট্র্যাক্ট করা কার্যকারিতার জন্য Cloud Storage-এর দিকে আরও কিছুটা কাজ করতে হবে, যেমন, ফাইল (ব্লব) শনাক্ত করা এবং ব্লবস্টোর হ্যান্ডলারের একক send_blob() মেথড কলের পরিবর্তে স্পষ্টভাবে বাইনারি ডাউনলোড করা।
  • দুটি ক্ষেত্রেই, কোনও আর্টিফ্যাক্ট খুঁজে পাওয়া না গেলে, ব্যবহারকারীকে HTTP 404 সংক্রান্ত সমস্যা দেখানো হয়।

মূল হ্যান্ডলার

মূল অ্যাপ্লিকেশনে চূড়ান্ত পরিবর্তন মূল হ্যান্ডলারে করা হয়। 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)

আলাদা আলাদা get() এবং post() পদ্ধতি ব্যবহার করার পরিবর্তে, এগুলি আসলে root()-এ if-else স্টেটমেন্ট। এছাড়াও, root() একটি সিঙ্গল ফাংশন হওয়ার কারণে, GET এবং POST দুটির জন্যই টেমপ্লেট রেন্ডার করার জন্য শুধুমাত্র একটি কল আছে, যদিও এটি webapp2-এ সত্যিই সম্ভব নয়।

main.py-এ দ্বিতীয় ও চূড়ান্ত সেট পরিবর্তনের সচিত্র উপস্থাপনা এখানে দেওয়া হল:

5ec38818c32fec2.png

(ঐচ্ছিক) ব্যাকওয়ার্ড কম্প্যাটিবিলিটি "উন্নত করা"

তাই উপরে তৈরি করা সমাধানটি নিখুঁতভাবে কাজ করে... তবে শুধুমাত্র আপনি যদি স্ক্র্যাচ থেকে শুরু করেন এবং Blobstore-এর মাধ্যমে তৈরি করা ফাইল আপনার কাছে না থাকে। আমরা অ্যাপটি আপডেট করেছি যাতে BlobKey-এর পরিবর্তে ফাইলের নাম দিয়ে ফাইল শনাক্ত করা যায়, তাই সম্পূর্ণ হওয়া মডিউল ১৬ অ্যাপটি Blobstore ফাইল দেখতে পারবে না। অন্যভাবে বলতে গেলে, এই মাইগ্রেশন করার সময় আমরা ব্যাকওয়ার্ড ইনকমপ্যাটিবল পরিবর্তন করেছি। আমরা এখন main.py-এর একটি বিকল্প ভার্সন উপস্থাপন করছি যাকে main-migrate.py (রেপোতে পাওয়া গেছে) বলা হয়, যা এই ব্যবধান পূরণ করার চেষ্টা করে।

Blobstore-এর তৈরি করা ফাইলকে সাপোর্ট করার জন্য প্রথম "এক্সটেনশন" হল একটি ডেটা মডেল যাতে BlobKeyProperty থাকে (Cloud Storage-এর তৈরি করা ফাইলের জন্য StringProperty-এর পাশাপাশি):

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 (অথবা কোনওটিই নয়) আছে কিনা তা কনফার্ম করুন। কোনও ফাইল উপলভ্য থাকলে, যেটি আছে সেটি বেছে নিন এবং সেই শনাক্তকারী ব্যবহার করুন (Blobstore-এর তৈরি ফাইলের জন্য BlobKey বা ক্লাউড স্টোরেজের তৈরি ফাইলের জন্য ফাইলের নাম)। "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 দেখুন। জটিল মাইগ্রেশন করার সময়, বিশেষ পরিস্থিতি তৈরি হতে পারে, তাই এই উদাহরণটি আসল ডেটা সহ আসল অ্যাপগুলিকে আধুনিকীকরণের জন্য আরও বেশি সখ্যতা দেখানোর জন্য তৈরি করা হয়েছে।

৬. সারসংক্ষেপ/ক্লিন-আপ

এই বিভাগটি অ্যাপ ডিপ্লয় করে, এটি উদ্দেশ্য অনুযায়ী কাজ করে কিনা তা যাচাই করে এবং যেকোনও প্রতিফলিত আউটপুটে এই কোডল্যাবটি শেষ করে। অ্যাপ যাচাইকরণের পরে, কোনও ক্লিন-আপ ধাপ থাকলে তা সম্পূর্ণ করুন এবং পরবর্তী ধাপগুলি বিবেচনা করুন।

অ্যাপ্লিকেশন ডিপ্লয় ও যাচাই করুন

আপনার অ্যাপ আবার ডিপ্লয় করার আগে, lib ফোল্ডারে সেল্ফ-বান্ডেল করা থার্ড-পার্টি লাইব্রেরি পেতে pip install -t lib -r requirements.txt রান করতে ভুলবেন না। আপনি ব্যাকওয়ার্ড কম্প্যাটিবল সমাধান চালাতে চাইলে, প্রথমে main-migrate.py-এর নাম পরিবর্তন করে main.py করুন। এখন gcloud app deploy রান করুন এবং কনফার্ম করুন যে অ্যাপটি মডিউল ১৫ অ্যাপের মতোই কাজ করে। ফর্ম স্ক্রিনটি এইরকম দেখায়:

f5b5f9f19d8ae978.png

সবচেয়ে সাম্প্রতিক ভিজিট পৃষ্ঠাটি দেখতে এইরকম:

f5ac6b98ee8a34cb.png

App Engine Blobstore-এর পরিবর্তে Cloud Storage, App Engine NDB-এর পরিবর্তে Cloud NDB এবং webapp2-এর পরিবর্তে Flask ব্যবহার করে এই কোডল্যাব সম্পূর্ণ করার জন্য অভিনন্দন। আপনার কোড এখন FINISH (মডিউল ১৬) ফোল্ডারে যা আছে তার সাথে মিলতে হবে। বিকল্প main-migrate.py-ও সেই ফোল্ডারে আছে।

Python 3 "মাইগ্রেশন"

এই অ্যাপটিকে Python 3-এ পোর্ট করার জন্য app.yaml-এর উপরে কমেন্ট হিসেবে চিহ্নিত করা Python 3 runtime নির্দেশিকাই যথেষ্ট। সোর্স কোডটি আগে থেকেই Python 3-এর সাথে মানানসই, তাই সেখানে কোনও পরিবর্তন করার প্রয়োজন নেই। Python 3 অ্যাপ হিসেবে এটি ডিপ্লয় করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  1. app.yaml-এর সবচেয়ে উপরে Python 3 runtime নির্দেশিকা আনকমেন্ট করুন।
  2. app.yaml-এ অন্য সব লাইন মুছে দিন।
  3. appengine_config.py ফাইলটি মুছে দিন। (Python 3 রানটাইমে ব্যবহার করা হয়নি)
  4. lib ফোল্ডার থাকলে সেটি মুছে দিন। (Python 3 রানটাইমের ক্ষেত্রে অপ্রয়োজনীয়)

অপ্রয়োজনীয়গুলি মুছুন

সাধারণ

আপনার কাজ হয়ে গেলে, বিলিং এড়াতে আমরা আপনাকে App Engine অ্যাপ বন্ধ করে দিতে সাজেস্ট করি। তবে, আপনি যদি আরও পরীক্ষা করতে চান, তাহলে App Engine প্ল্যাটফর্মে ফ্রি কোটা আছে এবং আপনি ব্যবহারের সীমা অতিক্রম না করা পর্যন্ত আপনাকে চার্জ করা হবে না। এটি কম্পিউটের জন্য, তবে প্রাসঙ্গিক App Engine পরিষেবার জন্যও চার্জ প্রযোজ্য হতে পারে, তাই আরও তথ্যের জন্য এর দামের পৃষ্ঠা দেখুন। এই মাইগ্রেশনে অন্যান্য Cloud পরিষেবা অন্তর্ভুক্ত থাকলে, সেগুলির জন্য আলাদাভাবে বিল করা হয়। যেকোনও ক্ষেত্রে, প্রযোজ্য হলে, নিচে "এই কোডল্যাবের জন্য নির্দিষ্ট" বিভাগটি দেখুন।

সম্পূর্ণ তথ্য প্রকাশ করার জন্য, App Engine-এর মতো Google Cloud সার্ভারলেস কম্পিউট প্ল্যাটফর্মে ডিপ্লয় করার জন্য সামান্য বিল্ড ও স্টোরেজ খরচ হয়। Cloud Storage-এর মতো Cloud Build-এরও নিজস্ব ফ্রি কোটা আছে। সেই ছবি স্টোর করার জন্য কোটার কিছুটা অংশ ব্যবহার করা হয়। তবে, আপনি এমন কোনও অঞ্চলে বসবাস করতে পারেন যেখানে এই ধরনের ফ্রি টিয়ার নেই, তাই সম্ভাব্য খরচ কমাতে আপনার স্টোরেজ ব্যবহারের ব্যাপারে সচেতন থাকুন। নির্দিষ্ট 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"।

অন্যদিকে, আপনি যদি এই অ্যাপ্লিকেশন বা অন্যান্য সম্পর্কিত মাইগ্রেশন কোডল্যাব চালিয়ে যেতে না চান এবং সবকিছু সম্পূর্ণরূপে মুছে ফেলতে চান, তাহলে আপনার প্রোজেক্ট বন্ধ করুন।

এই কোডল্যাবের জন্য নির্দিষ্ট

নিচে তালিকাভুক্ত পরিষেবাগুলি এই কোডল্যাবের জন্য অনন্য। আরও তথ্যের জন্য প্রতিটি প্রোডাক্টের ডকুমেন্টেশন দেখুন:

মনে রাখবেন, আপনি মডিউল ১৫ থেকে ১৬-এ মাইগ্রেট করলেও, Blobstore-এ আপনার ডেটা থাকবে, তাই আমরা উপরে এর দাম সংক্রান্ত তথ্য অন্তর্ভুক্ত করেছি।

পরবর্তী ধাপ

এই টিউটোরিয়াল ছাড়াও, অন্যান্য মাইগ্রেশন মডিউল যা পুরনো বান্ডেল করা পরিষেবা থেকে সরে আসার উপর ফোকাস করে, তার মধ্যে রয়েছে:

  • মডিউল ২: App Engine ndb থেকে Cloud NDB-তে মাইগ্রেট করা
  • মডিউল ৭-৯: App Engine টাস্ক কিউ পুশ টাস্ক থেকে Cloud Tasks-এ মাইগ্রেট করা
  • মডিউল ১২-১৩: App Engine Memcache থেকে Cloud Memorystore-এ মাইগ্রেট করা
  • মডিউল ১৮-১৯: App Engine টাস্ক কিউ (টাস্ক পুল করা) থেকে Cloud Pub/Sub-এ মাইগ্রেট করা

Google Cloud-এ App Engine এখন আর একমাত্র সার্ভারলেস প্ল্যাটফর্ম নয়। আপনার যদি ছোট App Engine অ্যাপ বা সীমিত কার্যকারিতা সহ কোনও অ্যাপ থাকে এবং আপনি এটিকে একটি স্বতন্ত্র মাইক্রোসার্ভিসে পরিণত করতে চান, অথবা আপনি যদি একটি মনোলিথিক অ্যাপকে একাধিক পুনর্ব্যবহারযোগ্য উপাদানে বিভক্ত করতে চান, তাহলে Cloud Functions-এ স্থানান্তরিত করার কথা বিবেচনা করার জন্য এগুলি ভাল কারণ। আপনার অ্যাপ্লিকেশন ডেভেলপমেন্ট ওয়ার্কফ্লোতে কন্টেনারাইজেশন অন্তর্ভুক্ত থাকলে, বিশেষ করে এতে CI/CD (কন্টিনিউয়াস ইন্টিগ্রেশন/কন্টিনিউয়াস ডেলিভারি বা ডিপ্লয়মেন্ট) পাইপলাইন থাকলে, Cloud Run-এ মাইগ্রেট করার কথা বিবেচনা করুন। এইসব পরিস্থিতি নিম্নলিখিত মডিউলে কভার করা হয়েছে:

  • App Engine থেকে Cloud Functions-এ মাইগ্রেট করা: মডিউল ১১ দেখুন
  • App Engine থেকে Cloud Run-এ মাইগ্রেট করা: Docker-এর মাধ্যমে আপনার অ্যাপ কন্টেনারাইজ করতে মডিউল ৪ দেখুন অথবা কন্টেনার, Docker-এর জ্ঞান বা Dockerfile ছাড়াই এটি করতে মডিউল ৫ দেখুন

অন্য সার্ভারলেস প্ল্যাটফর্মে পরিবর্তন করা ঐচ্ছিক এবং আমরা কোনও পরিবর্তন করার আগে আপনার অ্যাপ এবং ব্যবহারের ক্ষেত্রে সেরা বিকল্পগুলি বিবেচনা করার সাজেশন দিই।

আপনি এর পরে যে মাইগ্রেশন মডিউলই বিবেচনা করুন না কেন, সমস্ত সার্ভারলেস মাইগ্রেশন স্টেশন কন্টেন্ট (কোডল্যাব, ভিডিও, সোর্স কোড [উপলভ্য হলে]) এর ওপেন সোর্স রেপো থেকে অ্যাক্সেস করা যেতে পারে। এছাড়াও, রেপোজিটরি README কোন মাইগ্রেশন বিবেচনা করতে হবে এবং মাইগ্রেশন মডিউলের যেকোনও প্রাসঙ্গিক "অর্ডার" সম্পর্কে নির্দেশিকা প্রদান করে।

৭. অতিরিক্ত রিসোর্স

কোডল্যাব সংক্রান্ত সমস্যা/মতামত

এই কোডল্যাবে কোনও সমস্যা দেখতে পেলে, অভিযোগ জানানোর আগে আপনার সমস্যাটি সার্চ করে দেখুন। নতুন সমস্যা সার্চ ও তৈরি করার লিঙ্ক:

মাইগ্রেশন রিসোর্স

মডিউল ১৫ (শুরু) এবং মডিউল ১৬ (শেষ)-এর জন্য রেপো ফোল্ডারের লিঙ্ক নিচের টেবিলে পাওয়া যাবে। এছাড়াও, সব App Engine কোডল্যাব মাইগ্রেশনের জন্য রেপো থেকে এগুলি অ্যাক্সেস করা যেতে পারে, যা আপনি ক্লোন করতে বা ZIP ফাইল ডাউনলোড করতে পারেন।

Codelab

Python 2

Python 3

মডিউল ১৫

কোড

প্রযোজ্য নয়

মডিউল ১৬ (এই কোডল্যাব)

কোড

(Python 2-এর মতোই)

অনলাইন রিসোর্স

এই টিউটোরিয়ালের জন্য প্রাসঙ্গিক হতে পারে এমন অনলাইন রিসোর্স নিচে দেওয়া হল:

App Engine Blobstore ও Cloud Storage

App Engine প্ল্যাটফর্ম

Cloud সম্পর্কিত অন্যান্য তথ্য

Python

ভিডিও

লাইসেন্স

এই কাজটি Creative Commons Attribution 2.0 Generic License-এর অধীনে লাইসেন্সপ্রাপ্ত।