কম্পিউটার

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

সার্ভারলেস ফাংশন ক্লাউড পরিষেবাগুলি বিকাশ এবং স্থাপনের একটি নতুন প্রোগ্রামিং দৃষ্টান্ত। একটি সার্ভারহীন বিশ্বে, আমরা ক্লাউড প্রদানকারীর কাছে আমাদের ব্যাকএন্ড পরিষেবাগুলির বিধান, রক্ষণাবেক্ষণ এবং স্কেলিংকে বিমূর্ত করি। এটি ডেভেলপারদের একটি নির্দিষ্ট সমস্যা সমাধানে ফোকাস করার অনুমতি দিয়ে ডেভেলপারের উৎপাদনশীলতাকে উল্লেখযোগ্যভাবে উন্নত করে। যদিও সার্ভারহীন ফাংশন তৈরির অনেক সুবিধা এবং অসুবিধা রয়েছে, সেগুলি তৈরি করার ক্ষেত্রে একটি বিষয় বিবেচনা করতে হবে তা হল ভাষা সমর্থন। সম্প্রতি, Google Google ক্লাউড ফাংশনগুলির জন্য রুবি 2.7-এর সমর্থন ঘোষণা করেছে, এবং এই নিবন্ধে, আমি Google ক্লাউড ফাংশনে রুবিতে একটি সার্ভারবিহীন ফাংশন তৈরি, পরীক্ষা এবং স্থাপন এবং সার্ভারহীন ফাংশনগুলির সুবিধা এবং অসুবিধাগুলিতে ফোকাস করব৷ পি>

একটি সার্ভারহীন OTP সিস্টেম তৈরি করা

ওয়ান-টাইম পাসওয়ার্ড (OTP) হল সংক্ষিপ্ত সংখ্যাসূচক কোড যা প্রমাণীকরণের উদ্দেশ্যে ব্যবহৃত হয়, যেমন আপনার ব্যাঙ্ক যখন আপনার পরিচয় যাচাই করার জন্য পাঠ্যের মাধ্যমে একটি OPT পাঠায়।

এই নিবন্ধে, আমরা একটি OTP ফাংশন তৈরি করব যা তিনটি মূল দায়িত্ব পরিচালনা করে:

POST /otp :প্রদত্ত phone_number-এ একটি OTP বার্তা তৈরি করে এবং পাঠায় .

# Request
{
  "phone_number": "+2347012345678"
}

# Response
{
  "status": true,
  "message": "OTP sent successfully",
  "data": {
    "phone_number": "+2347012345678",
    "otp": 6872,
    "expires_at": "2021-02-09 07:15:25 +0100"
  }
}

PUT /otp/verify :ব্যবহারকারীর প্রদত্ত ওটিপির বিপরীতে একটি ওটিপি যাচাই করে৷

# Request
{
  "phone_number": "+2347012345678",
  "otp": 7116
}

# Response
{
  "status": true,
  "message": "OTP verified",
  "data": {}
}

PUT /otp/resend :প্রদত্ত phone_number-এ একটি OTP তৈরি ও পুনরায় পাঠানোর চেষ্টা করে .

# Request
{
  "phone_number": "+2347012345678"
}

# Response
{
  "status": true,
  "message": "OTP sent successfully",
  "data": {
    "phone_number": "+2347012345678",
    "otp": 8533,
    "expires_at": "2021-02-09 08:59:16 +0100"
  }
}

সরলতার খাতিরে, আমাদের ক্লাউড ফাংশনটি সম্পূর্ণ SQL বা NoSQL ডেটাবেসের পরিবর্তে একটি ক্লাউড মেমরিস্টোর (জিসিপিতে রেডিস বা মেমক্যাশ) দ্বারা সমর্থিত হবে। এটি আমাদের রাষ্ট্রহীন পরিবেশে ভাগ করা রাজ্যগুলি সম্পর্কে জানতে সক্ষম করবে৷

রুবি দিয়ে একটি Google ক্লাউড ফাংশন লেখা

GCF-এ ফাংশন লিখতে, আমরা Functions Framework-এর উপর নির্ভর করব GCF ফাংশন তৈরির জন্য Google ক্লাউড টিম দ্বারা সরবরাহ করা হয়েছে (এটি সম্পর্কে আরও পরে)।

প্রথমে অ্যাপ ডিরেক্টরি তৈরি করুন এবং ডিরেক্টরিতে প্রবেশ করুন।

mkdir otp-cloud-function && cd otp-cloud-function

এরপর, আপনার জেমফাইল তৈরি করুন এবং ইনস্টল করুন।

বেশিরভাগ সাধারণ রুবি অ্যাপ্লিকেশনের মতো, আমরা bundler ব্যবহার করব আমাদের ফাংশনের নির্ভরতা পরিচালনা করতে

source "https://rubygems.org"

# Core
gem "functions_framework", "~> 0.7"

# Twilio for Sms
gem 'twilio-ruby', '~> 5.43.0'

# Database
gem 'redis'

# Connection Pooling
gem 'connection_pool'

# Time management
gem 'activesupport'

# API Serialization
gem 'active_model_serializers', '~> 0.10.0'

group :development, :test do
  gem 'pry'
  gem 'rspec'
  gem 'rspec_junit_formatter'
  gem 'faker', '~> 2.11.0'
end
bundle install

ফাংশন তৈরি করা

সাধারণত, বিভিন্ন হোস্টিং পরিবেশ আপনাকে একটি ভিন্ন ফাইল নির্দিষ্ট করার অনুমতি দেয় যেখানে আপনার ফাংশনগুলি লেখা হয়। Google ক্লাউড ফাংশন, তবে, এটি app.rb হতে হবে আপনার প্রকল্প ডিরেক্টরির রুটে। এখন, আমরা আমাদের ফাংশন লিখতে প্রস্তুত।

app.rb খুলুন এবং ফাংশন তৈরি করুন:

# Cloud Functions Entrypoint

require 'functions_framework'
require 'connection_pool'
require 'active_model_serializers'
require './lib/store'
require './lib/send_sms_notification'
require './lib/response'
require './lib/serializers/models/base_model'
require './lib/serializers/models/otp_response'
require './lib/serializers/application_serializer'
require './lib/serializers/base_model_serializer'
require './lib/serializers/otp_response_serializer'

FunctionsFramework.on_startup do |function|
  # Setup Shared Redis Client
  require 'redis'
  set_global :redis_client, ConnectionPool.new(size: 5, timeout: 5) { Redis.new }
end

# Define HTTP Function
FunctionsFramework.http "otp" do |request|

  store = Store.new(global(:redis_client))
  data = JSON.parse(request.body.read)

  if  request.post? && request.path == '/otp'
    phone_number = data['phone_number']
    record = store.get(phone_number)
    unless record.nil? || record.expired?
      data = Models::OtpResponse.new(phone_number: phone_number,
                                      otp: record['otp'],
                                      expires_at: record['expires_at'])
      json = Response.generate_json(status: true,
                            message: 'OTP previously sent',
                            data: data)

      return json
    end

    otp = rand(1111..9999)
    record = store.set(phone_number, otp)
    SendSmsNotification.new(phone_number, otp).call

    data = Models::OtpResponse.new(phone_number: phone_number,
                                    otp: record['otp'],
                                    expires_at: record['expires_at'])

    Response.generate_json(status: true,
                          message: 'OTP sent successfully',
                          data: data)

  elsif request.put? && request.path == '/otp/verify'
    phone_number = data['phone_number']
    record = store.get(phone_number)

    if record.nil?
      return Response.generate_json(status: false, message: "OTP not sent to number")
    elsif record.expired?
      return Response.generate_json(status: false,  message: 'OTP code expired')
    end

    is_verified = data['otp'] == record['otp']

    if is_verified
      return Response.generate_json(status: true, message: 'OTP verified')
    else
      return Response.generate_json(status: false, message: 'OTP does not match')
    end

  elsif request.put? && request.path == '/otp/resend'
    phone_number = data['phone_number']
    store.del(phone_number)

    otp = rand(1111..9999)
    record = store.set(phone_number, otp)
    SendSmsNotification.new(phone_number, otp).call

    data = Models::OtpResponse.new(phone_number: phone_number,
                                    otp: record['otp'],
                                    expires_at: record['expires_at'])

    json = Response.generate_json(status: true,
                          message: 'OTP sent successfully',
                          data: data)
  else
    Response.generate_json(status: false,
                            message: 'Request method and path did not match')
  end
end

এটি অনেক কোড, তাই আমরা এটি ভেঙে দেব:

  • Functions_Framework.on_startup ফাংশন অনুরোধ প্রক্রিয়াকরণ শুরু করার আগে রুবি উদাহরণ প্রতি রান কোড একটি ব্লক. আমাদের ফাংশনগুলিকে কল করার আগে যেকোনো ধরনের ইনিশিয়ালাইজেশন চালানো আদর্শ। এই ক্ষেত্রে, আমি আমাদের রেডিস সার্ভারে একটি সংযোগ পুল তৈরি এবং ভাগ করতে এটি ব্যবহার করছি:

    set_global :redis_client, ConnectionPool.new(size: 5, timeout: 5) { Redis.new }
    

    এটি আমাদেরকে ভয় ছাড়াই একাধিক সমসাময়িক ফাংশন আহ্বান জুড়ে Redis সংযোগ বস্তুর একটি পুল ভাগ করতে সক্ষম করে। একাধিক স্টার্টআপ সংজ্ঞায়িত করা যেতে পারে। তারা সংজ্ঞায়িত করা হয় যে ক্রমে চালানো হবে. এটাও মনে রাখা গুরুত্বপূর্ণ যে Functions Framework ফাংশন সমাপ্তির পরে চালানোর জন্য কোন বিশেষ হুক প্রদান করে না।

  • Functions_Framework.http 'otp' do |request| আমাদের ফাংশনগুলির অনুরোধ এবং প্রতিক্রিয়া প্রক্রিয়াকরণ পরিচালনা করে। এই ফাংশন তিনটি ভিন্ন রুট প্যাটার্ন সমর্থন করে। অন্যান্য ধরনের ফাংশন সংজ্ঞায়িত করা সম্ভব (যেমন, Functions_Framework.cloud_event 'otp' do |event| ) যা অন্যান্য Google পরিষেবার ইভেন্টগুলি পরিচালনা করে৷ একই ফাইলে একাধিক ফাংশন সংজ্ঞায়িত করাও সম্ভব কিন্তু স্বাধীনভাবে স্থাপন করা হয়।

  • store = Store.new(global(:redis_client))-এ , global মেথডটি গ্লোবাল শেয়ার্ড স্টেটে সংরক্ষিত যেকোনো বস্তু পুনরুদ্ধার করতে ব্যবহৃত হয়। উপরে যেমন ব্যবহার করা হয়েছে, আমরা আমাদের startup-এ গ্লোবাল সেটআপে সংজ্ঞায়িত সংযোগ পুল থেকে Redis ক্লায়েন্ট পুনরুদ্ধার করি। ব্লক।

  • Response এবং Models::OtpResponse active_model_serializers এর সাথে প্রতিক্রিয়া সিরিয়ালাইজেশন পরিচালনা করে সঠিকভাবে ফরম্যাট করা JSON প্রতিক্রিয়া দিতে।

আমাদের ফাংশন স্থানীয়ভাবে পরীক্ষা করা

Functions Framework লাইব্রেরি আমাদের ফাংশনগুলিকে ক্লাউডে স্থাপন করার আগে স্থানীয়ভাবে সহজেই পরীক্ষা করতে সক্ষম করে। স্থানীয়ভাবে পরীক্ষা করার জন্য, আমরা চালাই

bundle exec functions-framework-ruby --target=otp --port=3000

--target মোতায়েন করার জন্য ফাংশন নির্বাচন করতে ব্যবহৃত হয়।

ম্যানুয়াল টেস্টিং দুর্দান্ত হলেও, স্বয়ংক্রিয় পরীক্ষা এবং স্ব-পরীক্ষা সফ্টওয়্যার হল পরীক্ষার ক্ষেত্রে পবিত্র গ্রিল। Functions Framework Minitest উভয়ের জন্য সহায়ক পদ্ধতি প্রদান করে এবং RSpec http উভয়ের জন্য আমাদের ফাংশন পরীক্ষা করতে সাহায্য করতে এবং cloudevents হ্যান্ডলার এখানে একটি পরীক্ষার একটি উদাহরণ:

require './spec/spec_helper.rb'
require 'functions_framework/testing'

describe  'OTP Functions' do
  include FunctionsFramework::Testing

  describe 'Send OTP', redis: true do
    let(:phone_number) { "+2347012345678" }
    let(:body) { { phone_number: phone_number }.to_json }
    let(:headers) { ["Content-Type: application/json"] }

    it 'should send OTP successfully' do
      load_temporary "app.rb" do
        request = make_post_request "/otp", body, headers

        response = call_http "otp", request
        expect(response.status).to eq 200
        expect(response.content_type).to eq("application/json")

        parsed_response = JSON.parse(response.body.join)
        expect(parsed_response['status']).to eq true
        expect(parsed_response['message']).to eq 'OTP sent successfully'
      end
    end
  end
end

আমাদের ফাংশন স্থাপন করা হচ্ছে

প্রথমে, আমাদের Google Cloud Memorystore ব্যবহার করে একটি Redis সার্ভার স্থাপন করতে হবে , যার উপর আমাদের ফাংশন নির্ভরশীল। GCP-তে রেডিস সার্ভার কীভাবে স্থাপন করা যায় সে সম্পর্কে আমি এখানে আরও বিশদে যাব না, কারণ এটি এই নিবন্ধের সুযোগের বাইরে।

Google ক্লাউড ফাংশনের পরিবেশে আমাদের ফাংশন স্থাপন করার একাধিক উপায় রয়েছে:আপনার মেশিন থেকে স্থাপন করা, GCP কনসোল থেকে স্থাপন করা, এবং আমাদের কোড সংগ্রহস্থল থেকে স্থাপন করা। আধুনিক সফ্টওয়্যার ইঞ্জিনিয়ারিং আমাদের বেশিরভাগ বিকাশের জন্য CI/CD প্রক্রিয়াগুলিকে উত্সাহিত করে এবং এই নিবন্ধের উদ্দেশ্যে, আমি ডিপ্লোয়-ক্লাউড-ফাংশনগুলি ব্যবহার করে গিথুব অ্যাকশনগুলির সাথে গিথুব থেকে আমাদের ক্লাউড ফাংশন স্থাপনের উপর ফোকাস করব৷

আসুন আমাদের স্থাপনার ফাইল (.github/workflows/deploy.yml) সেট আপ করি।

name: Deployment
on:
  push:
    branches:
      - main
jobs:
  deploy:
    name: Function Deployment
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - id: deploy
        uses: google-github-actions/deploy-cloud-functions@main
        with:
          name: otp-cloud-function
          runtime: ruby26
          credentials: ${{ secrets.gcp_credentials }}
          env_vars: "TWILIO_ACCOUNT_SID=${{ secrets.TWILIO_ACCOUNT_SID }},TWILIO_AUTH_TOKEN=${{ secrets.TWILIO_AUTH_TOKEN }},TWILIO_PHONE_NUMBER=${{ secrets.TWILIO_PHONE_NUMBER }},REDIS_URL=${{ secrets.REDIS_URL }}"

পরিবেশ ভেরিয়েবল

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

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

প্রমাণিকরণ

একটি Service Account তৈরি করুন Cloud Functions Admin সহ এবং Service Account User ভূমিকা।

মেশিন-টু-মেশিন IAM-এর জন্য একটি পরিষেবা অ্যাকাউন্ট ব্যবহার করা হয়। এইভাবে, যখন একটি সিস্টেম, Google ক্লাউডে চলমান কিনা তা নির্বিশেষে, Google ক্লাউডে অন্য একটি সিস্টেমের সাথে কথা বলে, কে আমাদের Google রিসোর্সে অ্যাক্সেসের অনুরোধ করছে তা সনাক্ত করতে সাহায্য করার জন্য একটি পরিষেবা অ্যাকাউন্টের প্রয়োজন হয়৷ ভূমিকা Cloud Functions Admin এবং Service Account User ব্যবহারকারী সংস্থান অ্যাক্সেস করার জন্য অনুমোদিত কিনা তা নির্ধারণ করতে আমাদের সক্ষম করুন। এই পরিস্থিতিতে, একজন গিথুব অ্যাকশন রানার আমাদের ফাংশন স্থাপনের প্রয়োজনীয় অনুমতি সহ একটি পরিষেবা অ্যাকাউন্ট হিসাবে Google ক্লাউড প্রমাণীকরণের সাথে যোগাযোগ করে৷

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

একটি পরিষেবা অ্যাকাউন্ট কী তৈরি করুন, JSON ডাউনলোড করুন এবং এটি গিটহাব সিক্রেটসে যোগ করুন।

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

ভয়লা ! 🎉 আমাদের ক্লাউড ফাংশন সফলভাবে স্থাপন করা হয়েছে।

ক্লাউড ফাংশন সীমা বনাম AWS সীমা

নীচে দুটি বৃহত্তম সার্ভারহীন ফাংশন প্রদানকারীর একটি বিশদ তুলনা রয়েছে:

রুবি দিয়ে Google ক্লাউড ফাংশন তৈরি করা, পরীক্ষা করা এবং স্থাপন করা

ফাংশন ফ্রেমওয়ার্ক চুক্তি বনাম সার্ভারহীন ফ্রেমওয়ার্ক

এই নিবন্ধে, আমরা Google ক্লাউড ফাংশনগুলির জন্য ক্লাউড ফাংশন তৈরির উপর ফোকাস করেছি। এই সেগমেন্টে, আমি ফাংশন ফ্রেমওয়ার্ক বনাম সার্ভারলেস ফ্রেমওয়ার্কের সাথে বিল্ডিং তুলনা করতে চাই।

  • Serverless Framework serverless.yml এর উপর ভিত্তি করে , যখন ফাংশন ফ্রেমওয়ার্ক Functions Framework Contract , যা Google ক্লাউড অবকাঠামো জুড়ে সার্ভারহীন ফাংশন স্থাপন করতে ব্যবহৃত হয়।
  • Serverless Framework সহ , শুধুমাত্র কয়েকটি উদাহরণ রয়েছে, এবং রুবির সাথে বিভিন্ন Google সার্ভারহীন পরিবেশে (ক্লাউড ফাংশন, ক্লাউড রান, এবং নেটিভ এনভায়রনমেন্ট) কীভাবে সার্ভারহীন ফাংশন তৈরি এবং স্থাপন করা যায় তা পুরোপুরি পরিষ্কার নয়। Functions Framework Contract সহ ফাংশন, এই বিভিন্ন Google পণ্য জুড়ে রুবি দিয়ে তৈরি করা সহজ।
    • আগের পয়েন্ট থেকে অনুসরণ করে, Functions Framework Contract আপনার মোতায়েন প্রক্রিয়ায় অগত্যা পরিবর্তন না করেই আপনার ফাংশনের পিছনে ব্যাকিং ভাষা পরিবর্তন করা খুব সহজ করে তোলে৷
  • এই লেখা থেকে, Functions Framework শুধুমাত্র Google ক্লাউড সার্ভারহীন পরিবেশ এবং Knative পরিবেশ জুড়ে আন্তঃকার্যযোগ্যতা সমর্থন করে। Serverless Framework তবে, একাধিক প্রদানকারী জুড়ে একাধিক প্ল্যাটফর্ম সমর্থন করে।

রেফারেন্সের জন্য, সম্পূর্ণ কোড এখানে উপলব্ধ।


  1. রুবিতে একটি খেলনা প্রোগ্রামিং ভাষা তৈরি করা

  2. Vue, Vuex এবং Rails সহ একটি সম্পূর্ণ-স্ট্যাক অ্যাপ্লিকেশন তৈরি করা

  3. Fluentd এবং ObjectRocket দিয়ে একটি হাইব্রিড ক্লাউড লগ ইন করা

  4. Google ক্লাউড প্রিন্ট কি এবং এটি কিভাবে কাজ করে?