ایده‌‌ای جسورانه برای بالا نگهداشتن سرویس - قسمت دوم

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

چرا نسخه‌ی برنامه‌ها را ثابت کنیم!؟

فرض کنید بخواهیم بر روی کلاستر، تغییراتی اعمال کنیم. جهت اعمال این تغییرات، از یک سری اسکرپیت‌ها استفاده می‌شود. این اسکریپت‌ها وظیفه‌ی تنظیم (config) و نصب سرویس‌ها را به عهده دارند. ثابت نگه‌داشتن نسخه‌ی برنامه و وابستگی‌هایش در زمان اجرای این اسکریپت‌ها، مزایای متعددی دارد که از جمله‌ی آن‌ها می‌توان به موارد بعدی اشاره نمود:

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

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

نسخه‌سازی معنایی (Semantic Versioning)

جهت شماره‌گذاری نسخه‌ی برنامه، از نسخه‌سازی معنایی استفاده می‌کنیم؛ که به صورت Major.Minor.Patch (مانند ورژن ۲.۳.۵) بیان می‌شود.

به بیان ساده‌تر هر تغییر، در بردارنده‌ی یکی از سه حالت کلی زیر است:

  • تغییراتی در کد داده شده است که باعث اختلال در سرویس‌دهی می‌شوند -تغییر با نسخه قبل سازگار نیست- در این صورت، مقدار Major یک عدد بالاتر می‌رود. مانند ۳.۰.۰
  • بهبودی در کد داده شده است. در این صورت، مقدار Minor یک عدد بالاتر می‌رود. مانند ۲.۴
  • یک باگ ساده رفع شده است. در این صورت، مقدار Patch یک عدد بالاتر می‌رود. مانند ۲.۳.۶

لازم به ذکر است اگر مقدار Major یا Minor افزایش پیدا کند؛ اعداد پائین‌تر صفر می‌شود. برای نمونه، با بالا رفتن مقدار Major در نسخه ۲.۳.۵ شماره نسخه به صورت ۳.۳.۵ بالا نرفت، بلکه به صورت ۳.۰.۰ تغییر یافت.

نسخه‌سازی معنایی، جهت سازگار بودن نسخه‌ها بیان شده است و بالاتر رفتن عددها معنای دیگری ندارد.

چطور نسخه‌ی برنامه‌ها را ثابت نگه‌داریم؟

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

ثابت کردن پایتون

از آنجایی‌که نسخه‌ی پایتون از یک سیستم به سیستم دیگر، متفاوت است؛ پس باید ابتدا نسخه‌ی پایتون را ثابت نگه داریم. برای تعیین نسخه‌ی پایتون از ابزار Pyenv استفاده می‌کنیم.

به عنوان نمونه، فرض کنید می‌خواهیم برای یکی از پروژه‌هایمان از نسخه‌ی ۳.۶.۶ پایتون استفاده کنیم. ابزار Pyenv گام‌های ذیل را انجام می‌دهد:

  • نسخه‌ی پایتون را از فایل python-version می‌خواند.
  • سورس کد پایتون را دانلود می‌کند.
  • پایتون مورد نظر را می‌سازد (build می‌کند).
  • متغیرهای محیطی را تنظیم می‌کند تا کد‌های آن پروژه، با نسخه‌ی پایتونی که تعیین شده است؛ اجرا شوند.

ثابت کردن وابستگی‌ها (Dependencies)

برای ثابت کردن نسخه‌ی ماژول‌ها هم از ابزار Pipenv استفاده می‌کنیم. ابزار Pipenv با استفاده از دو فایل Pipfile و Pipfile.lock‌ نسخه‌ی ماژول‌های مورد نیاز پروژه را ثابت (fix) می‌کند.

ثابت کردن برنامه در حال توسعه

جهت تعیین نسخه برنامه‌ی در حال توسعه‌، از نسخه‌سازی معنایی استفاده می‌شود.

بعد از ثابت کردن سه مولفه‌ی پایتون، وابستگی‌ها و کد، مزایای گفته شده در مورد ثابت کردن نسخه‌ها، حاصل خواهد شد.

مدیریت تغییرات (Change Management)

در قسمت قبل توضیح داده شد که چگونه مولفه‌های مورد نظرمان را ثابت نگه داریم. اطلاعات مربوط به ثابت نگه‌داشتن نسخه‌ها، باید در جایی ثبت شوند. بدین‌ منظور از سیستم‌های Version Control مانند Git استفاده می‌کنیم.

به عنوان نمونه، فایل‌های Pipfile ، Pipfile.lock و python-version را در Git ذخیره می‌کنیم.

استفاده از ابزارهایی مانند Git مزایایی دارد:

  • به ما این امکان را می دهد که ببینیم چه کسی و به چه علتی تغییر را اعمال کرده است.
  • تغییرات، توسط شخص یا گروه، بازبینی (review) می‌شود و از زاویه دیدهای مختلفی به تغییرات نگاه می‌شود و افراد دیگر هم از این تغییر مطلع می‌شوند.
  • علت خطاهایی که بعد از اجرای اسکریپت‌ها در کلاستر روی می‌دهد؛ راحت‌تر پیدا می‌شوند. فرض کنید سرویسی که در حال اجرا در کلاستر بود؛ هم اکنون دچار اختلال شده است. با توجه به تاریخچه‌ی تغییرات، علت خرابی راحت‌تر پیدا می‌شود.

مهاجرت (Migration)

برای تغییر یک سرویس از یک وضعیت به وضعیت دیگر از اسکریپت های Migration استفاده می‌کنیم.

در این‌صورت، سرویس به نسخه جدید مهاجرت کرده و اجرای متوالی اسکرپیت‌ها، تغییری در وضعیت سرویس ایجاد نمی‌کنند.

آیا باید برنامه‌نویس را در استفاده از ابزار محدود کنیم!؟‌

قطعا خیر. توسعه‌دهنده‌ها باید در استفاده از سیستم‌های نرم‌افزاری آزاد باشند. مثلا توسعه دهنده باید آزادانه سیستم‌عامل خود را اعم از Ubuntu یا CentOS یا Manjaro یا هر سیستم‌عامل دیگری -البته به جزWindows- انتخاب کند.

جمع بندی و نکته‌ی پایانی

دراین مقاله توضیح داده شد که برای این‌که ناخواسته یک سرویس از حالت سرویس‌دهی خارج نگردد نیاز داریم:

  • نسخه‌ی برنامه‌ها به صورت Semantic Versioning مدیریت شود.
  • تغییرات در Version Control قرار گرفته، بازبینی و تست شوند.
  • با مکانیزم درست مانند فرآیند Migration این تغییرات در کلاستر اعمال شود.
  • از ابزارهایی استفاده کنیم که تا وقتی ورودی تغییر نکرده، سرویس‌ها تغییر نکنند.

سعی بر این بود که با توجه به آنچه در این مطلب و در مطلب پیشین ارائه شد؛ بتوانیم از کار افتادن ناخواسته ی سرویس جلوگیری کنیم.