سِروِرلِس

سرورلس (یا بی‌سرور) یک سبک جدید و متفاوت برای راه‌اندازی سرویس‌های اینترنتی است. برنامه‌های سازگار با سرورلس ارزانتر و پایدارتر هستند و در مقیاس بسیار بالا قابل اجرا.

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

چه خوب می‌شد اگر برنامه‌نویس فقط برنامه‌اش را می‌نوشت و بقیه کارها به خودی خود انجام می‌شد.

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

به همین خاطر ما سیستم‌عامل و سرور را از معادله حذف می‌کنیم.

ولی مگر می‌شود یک برنامه را بدون سیستم‌عامل و سرور به صورت دائمی اجرا کرد؟ جواب کوتاه اینست که خیر نمی‌شود. ما فقط نگهداری از سیستم‌عامل را به پلتفرم و برنامه‌ی دیگری واگذار می‌کنیم. برای اینکه تفاوت یک برنامه‌ی سرورلس را با یک برنامه‌ی سنتی نشان بدهیم مثل همیشه به سراغ «ساقی‌بات» می‌رویم. من این برنامه را در دو نسخه‌ی ‏معمولی و سرورلس نوشته‌ام. بگذارید نگاهی بکنیم به نحوه‌ی اجرای این دو برنامه.

نسخه‌ی معمولی ساقی‌بات یک برنامه‌ی جاوااسکریپ است که باید مستقیما توسط نود اجرایش کنیم. یعنی باید روی یک کامپیوتر وابستگی‌های لازم از جمله خود نود را نصب کنیم و متغیرها را در محیط اجرا اضافه کنیم و بعد برنامه را اجرا کنیم. اگر برنامه کرش بکند باز این خودمان هستیم که باید آن را دوباره اجرا کنیم یا از یک برنامه‌ی دیگر مثل pm2 استفاده کنیم تا همواره آن را در حال اجرا نگاه دارد. یکی از دلایلی که بسیاری از سرویس‌های اینترنتی و بات‌ها بعد از مدتی از کار می‌افتند و می‌میرند همین است. برنامه کرش می‌کند یا سرور خراب می‌شود یا دیگر پولی و حوصله‌ای برای نگهداری از آن نیست. ساقی‌بات معمولی هم همینطور بود.

نسخه‌ی سرورلس ساقی‌بات اما به کلی متفاوت است. از سویی آن هم یک برنامه‌ی جاوااسکریپت ساده است که به نود و همه‌ی نیازمندی‌های ساقی‌بات معمولی محتاج است (آن را دوباره با فریم‌ورک بهتری نوشته‌ام). ولی نحوه اجرای برنامه کاملا فرق می‌کند. مهمترین تفاوت این است که «واحد اجرای برنامه» یک «تابع» یا «فانکشن» است نه یک برنامه‌ی کامل یا همان پراسس. تفاوت‌های مهم:

  • دیپلوی یک فانکشن بجای کل برنامه
  • دیپلوی روی یک سرویس‌دهنده‌ی ابری (کامپیوتر دیگران یا هر یک کلاستر با پشتیبانی از سرورلس) بجای نصب مستقیم روی سرور
  • شکستن برنامه به بخش‌های مختلف و دیپلوی جداگانه آنها

دیپلوی کردن همان عملیات نصب نرم‌افزار روی محیط هدف است. مثلا روی یک سرور اینترنتی. معمولا تیم‌های خوب اینکار را به صورت اتوماتیک توسط راه‌حل‌های مختلف CI انجام می‌دهند.

اگر به سورس‌کد ساقی‌بات سرورلس نگاه کنید به جز فایلهای خود برنامه یک فایل بنام serverless.yml می‌بینید. در این فایل متاداده‌های ساقی‌بات و نیز توابع آن را تعریف کرده‌ام. ساختار این فایل اختیاری نیست بلکه طبق دستورالعمل‌های فریم‌ورک سرورلس ساخته شده است. این فریم‌ورک یکی از روشهای ایجاد برنامه‌های سرورلس است که از انواع مختلفی از سرویس‌دهنده‌ها پشتیبانی می‌کند. در حال حاضر برای مخاطبین داخل ایران سرویس‌دهنده‌های معروف خارج از دسترس هستند ولی نکته‌ی خوب ماجرا اینست که می‌توان مشابه این سرویس را با کیفیت قابل قبولی شخصا ساخت که در آینده به آن خواهم پرداخت. اگر مشتاق هستید Kubeless و Knative و fn و OpenFaaS را ببینید. بگذارید نگاهی به این فایل بکنیم:

service: saaghibot

provider:
  name: aws
  region: eu-central-1
  runtime: nodejs10.x
  memorySize: 128
  environment:
    SAAGHIBOT_TOKEN: ${ssm:/saaghibot_token}

functions:
  saaghia:
    handler: handler.saaghia
    events:
      - http:
          path: saaghia
          method: post
          cors: true
      - schedule: rate(5 minutes)

این فایل سه بخش اصلی دارد. نام سرویس و شرح سرویس‌دهنده و لیست توابع. البته می‌تواند شاخ و برگ فراوانی داشته باشد ولی ساقی به همین راضی است! بخش سرویس‌دهنده برای اجرا روی سرویس‌های ابری آمازون نوشته شده است. براحتی می‌توان آن را به منظور انتقال به یک سرور شخصی تغییر داد که قصد دارم در آینده به آن بپردازم. لیست توابع شامل تنها یک تابع است به نام saaghia. اگر با دقت بیشتری به آن نگاه کنیم دو مدخل مهم دارد:

  saaghia:
    handler: handler.saaghia
    events:
      - http:
          path: saaghia
          method: post
          cors: true
      - schedule: rate(5 minutes)

اولی handler است که به کد واقعی تابعی اشاره می‌کند که داریم تعریف می‌کنیم. دیگری events است که می‌گوید چه رویدادهایی منجر به اجرای این تابع می‌شوند. رویداد اول http است و دومی schedule. اولی باعث می‌شود که یک آدرس وب توسط سرویس‌دهنده‌ی ابری برای این تابع خلق بشود و دومی هم هر پنج دقیقه این تابع را اجرا می‌کند. با داشتن این فایل و کد پروژه (که با نود نوشته شده) می‌توانیم این تابع را بسته‌بندی و در سرویس‌دهنده ابری آپلود کنیم. برای همه این موارد از دستور sls که توسط فریم‌ورکی که استفاده کردیم فراهم شده استفاده می‌کنیم. خلاصه دستورات را در ادامه می‌آورم. و همانطور که پیشتر اشاره کردم از شرح جزئیات خاص آمازون صرف نظر می‌کنم چون در ایران کاربردی ندارد و البته می‌تواند جایگزین بشود.

$ git clone https://github.com/mehdisadeghi/saaghibot-serverless && cd saaghibot-serverless
$ npm install  # install saaghibot dependencies
$ npm insgall -g serverless  # install serverless framework
$ sls deploy  # aws cli should be configured

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

$ sls info
Service Information
service: saaghibot
stage: dev
region: eu-central-1
stack: saaghibot-dev
resources: 13
api keys:
  None
endpoints:
  POST - https://xxxxxxxxxx.execute-api.eu-central-1.amazonaws.com/dev/saaghia
functions:
  saaghia: saaghibot-dev-saaghia
layers:
  None        

می‌بینید که تابع ما آدرس وب خاص خودش را دارد که می‌توان درخواست وب به آن POST کرد. من برای این تابع هیچگونه کنترلی ننوشته‌ام. یعنی هرکس با داشتن لینک می‌تواند آن را صدا بزند. به همین خاطر آدرس را تغییر داده‌ام.

به کمک همین دستور کارهای بیشتری می‌شود کرد. مثل اجرای تابع از راه دور، دیدن لاگ تابع، لیست توابع دیپلوی شده و مانند اینها. فقط کافیست تایپ کنید sls و مستندات را ببینید. در ضمن ممکن است برایتان سوال پیش آمده باشد که چگونه ساقی‌بات سرورلس را به بات تلگرام ساقی وصل کرده‌ام. برای اینکار لینک بالا را خورانده‌ام به API تلگرام. هربار پیام جدیدی برای ساقی به تلگرام می‌رسد او فورا یک درخواست HTTP به لینک بالا POST می‌کند و تابع هم جوابش را می‌دهد. دستور را در ادامه می‌آورم.

➜  saaghi git:(master) ✗  2051  curl --request POST --url https://api.telegram.org/bot<bot_token>/setWebhook --header 'content-type: application/json' --data '{"url":"https://xxxxxxxxxx.execute-api.eu-central-1.amazonaws.com/dev/saaghia"}'

تا مطلب بعدی خوش و خرم باشید!