طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (Cellular Architecture) چگونه است؟

نکات کلیدی

  • معماری سلولی می‌تواند مزایای قابل‌توجهی برای مشتریان و کسب‌وکارها فراهم کند، از جمله افزایش دسترس‌پذیری، افزونگی (Resilience) و افزایش سرعت تیم‌های مهندسی.

  • خودکارسازی زیرساخت سلولی مستلزم حل چند مسئله کلیدی است: ایزولیشن، ایجاد سلول جدید، استقرار (Deployment)، مجوزها و مانیتورینگ.

  • معماری‌های سلولی برای دستیابی به اهداف دسترس‌پذیری بالا، به مسیریابی مناسب درخواست‌ها و مانیتورینگ مؤثر متکی هستند.

  • خودکارسازی را می‌توان از طریق زیرساخت به‌عنوان کد (Infrastructure as Code یا IaC) و پایپلاین‌های Build پیاده‌سازی کرد و با استانداردسازی اجزای کاربردی (Application Components) ساده‌تر نمود.

  • هیچ راه‌حل واحد و جهان‌شمولی وجود ندارد؛ هر تیم می‌تواند ابزارهای متناسب با نیاز خود را انتخاب کرده و سطح مطلوب اتوماسیون را تعیین کند.

معماری سلولی چیست؟

معماری سلولی یک الگوی طراحی است که کمک می‌کند در اپلیکیشن‌های چندمستاجره (Multi-tenant) به دسترس‌پذیری بالا برسیم. هدف این است که اپلیکیشن به‌گونه‌ای طراحی شود که بتوانید تمام اجزای آن را در یک «سلول» ایزوله مستقر کنید؛ سلولی که کاملاً خودکفا و خودبسنده است. سپس این سلول را در قالب استقرارهای متعدد و مجزا پیاده می‌کنید، بدون اینکه وابستگی‌ای بین سلول‌ها وجود داشته باشد.

هر سلول یک نمونه کاملاً عملیاتی و خودمختار از اپلیکیشن شماست که آماده سرویس‌دهی است و هیچ وابستگی یا تعاملی با سایر سلول‌ها ندارد.

ترافیک کاربران شما می‌تواند بین این سلول‌ها توزیع شود و اگر یک قطعی یا اختلال در یکی از سلول‌ها رخ دهد، فقط همان کاربران آن سلول تحت تأثیر قرار می‌گیرند، در حالی که سایر سلول‌ها کاملاً عملیاتی باقی می‌مانند. این کار «محدوده اثر» (blast radius) هر اختلال را به حداقل می‌رساند و کمک می‌کند اختلال روی SLA شما برای اکثر کاربران تأثیر نگذارد.

استراتژی‌های مختلفی برای سازمان‌دهی سلول‌ها و تصمیم‌گیری درباره اینکه کدام ترافیک باید به کدام سلول هدایت شود وجود دارد. برای اطلاعات بیشتر درباره مزایای معماری سلولی و نمونه‌هایی از این استراتژی‌ها، سخنرانی Peter Voshall در رویداد re:Invent 2018 با عنوان «How AWS Minimizes the Blast Radius of Failures» شدیداً توصیه می‌شود.

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

خودکارسازی معماری سلولی شما

در مسیر خودکارسازی زیرساخت سلولی، پنج مسئله کلیدی وجود دارد که باید حل شوند:

  1. ایزولیشن (Isolation): چطور مرزهای مشخص و جداگانه بین سلول‌ها تضمین کنیم؟

  2. سلول‌های جدید (New cells): چطور سلول‌های جدید را به صورت سازگار و کارآمد آنلاین کنیم؟

  3. استقرار (Deployment): چطور آخرین تغییرات کد را به هر سلول منتقل کنیم؟

  4. مجوزها (Permissions): چطور امنیت سلول را تضمین کرده و مجوزهای ورودی و خروجی آن را به‌خوبی مدیریت کنیم؟

  5. مانیتورینگ (Monitoring): اپراتور چطور در یک نگاه بتواند وضعیت سلامت همه سلول‌ها را ببیند و سریع تشخیص دهد کدام سلول‌ها تحت تأثیر یک اختلال هستند؟

ابزارها و استراتژی‌های متعددی برای حل این مسائل وجود دارد. در این مقاله، ابزارها و راه‌حل‌هایی که برای ما در Momento به‌خوبی کار کرده‌اند مطرح می‌شوند.

قبل از ورود به این مسائل خاص، ابتدا کمی درباره استانداردسازی صحبت می‌کنیم.

استانداردسازی

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

نکته مهم این است که درباره «استانداردسازی» صحبت می‌کنیم، نه «یکنواخت‌سازی کامل (Homogenization)». اغلب اپلیکیشن‌های ابری مدرن همگن نیستند. اپلیکیشن شما ممکن است شامل چند میکروسرویس مختلف باشد که روی ترکیبی از پلتفرم‌هایی مثل Kubernetes، AWS Lambda و EC2 اجرا می‌شوند. برای ساخت اتوماسیون عمومی برای همه این اجزای مختلف، فقط کافی است چند بخش مشخص از چرخه عمرشان را استاندارد کنیم.

استانداردسازی – قالب‌های استقرار (Deployment Templates)

سؤال اینجاست: چه چیزهایی را باید استاندارد کنیم؟

به مراحل معمولی که در انتشار یک تغییر کد در محیط تولید دخیل هستند فکر کنید. این مراحل معمولاً چیزی شبیه به این‌اند:

  1. یک توسعه‌دهنده، تغییرات کد را در ریپازیتوری کنترل نسخه (Version Control) کامیت می‌کند.

  2. یک آرتیفکت باینری با آخرین تغییرات ساخته می‌شود؛ این آرتیفکت می‌تواند یک Docker image، یک فایل JAR، یک فایل ZIP یا هر آرتیفکت دیگری باشد.

  3. آرتیفکت منتشر یا ریلیز می‌شود: Docker image به یک ریپازیتوری Docker، فایل JAR به یک ریپازیتوری Maven، فایل ZIP به یک فضای ذخیره‌سازی ابری و غیره Push می‌شود.

  4. آرتیفکت به محیط(های) تولید شما دیپلوی می‌شود. در معماری سلولی، این معمولاً یعنی استقرار سریالی روی تک‌تک سلول‌ها.

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

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۱: قالب حداقلی استقرار (Minimal deployment template)

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

برای این کار، اضافه کردن یک سلول «Staging» که ابتدا به آن دیپلوی کنیم و همچنین یک دوره «پخت» (Bake) بین استقرار در سلول‌های بعدی، ایده خوبی است. در طول این دوره Bake می‌توانیم متریک‌ها و آلارم‌ها را مانیتور کنیم و اگر مشکلی مشاهده شد، استقرار را متوقف کنیم. بنابراین قالب استقرار برای هر جزء اپلیکیشن می‌تواند به شکل زیر تکامل پیدا کند:

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۲: قالب استقرار با مراحل Bake

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

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

در Momento، اکثر زیرساخت ما روی AWS مستقر است، بنابراین از ابزارهای AWS استفاده کرده‌ایم. برای یک جزء از اپلیکیشن که روی EC2 اجرا می‌شود و از CloudFormation برای استقرار آن استفاده می‌کنیم، ابزارهای زیر را به کار می‌بریم:

  • AWS CodePipeline برای تعریف و اجرای Stageها

  • AWS CodeBuild برای اجرای مراحل Build

  • AWS Elastic Container Registry (ECR) برای انتشار Docker image جدید آن جزء

  • AWS CloudFormation برای استقرار نسخه‌های جدید در هر سلول

  • AWS Step Functions برای مانیتور کردن آلارم‌ها در مرحله Bake و تعیین اینکه آیا استقرار در سلول بعدی امن است یا خیر

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۳: پیاده‌سازی مراحل استقرار – نسخه مبتنی بر CloudFormation

برای یک جزء مبتنی بر Kubernetes، می‌توانیم همین مجموعه مراحل را با یک تغییر کوچک پیاده کنیم: به جای CloudFormation، از AWS Lambda استفاده می‌کنیم تا با APIهای Kubernetes تماس بگیرد و image جدید را در سلول‌ها دیپلوی کند.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۴: پیاده‌سازی مراحل استقرار – نسخه مبتنی بر Kubernetes

ایده‌ای که اینجا به دنبالش هستیم این است: با وجود تفاوت در Stack تکنولوژی اجزا، یک قالب عمومی برای مراحل انتشار تغییر جدید تعریف کنیم. سپس عمده این مراحل را با یک زنجیره ابزار تقریباً مشترک پیاده کنیم و فقط در چند مرحله خاص، تغییرات جزئی بدهیم. استانداردسازی چند بخش از چرخه Build برای همه اجزا، به ما اجازه می‌دهد اتوماسیون این مراحل را به شکل عمومی بسازیم؛ یعنی بتوانیم کد زیرساخت را به‌طور گسترده بازاستفاده کنیم و استقرارها در همه اجزا، سازگار و قابل تشخیص باشند.

استانداردسازی – اهداف Build

چطور مراحل موردنیاز را در میان اجزای مختلف استاندارد کنیم؟

یک راهبرد مفید این است که یک‌سری Build Target استاندارد تعریف کنیم و آن‌ها را در همه اجزا دوباره استفاده کنیم. در Momento، برای این کار از یک تکنولوژی کلاسیک و امتحان‌پس‌داده استفاده می‌کنیم: Makefileها.

Makefileها بسیار ساده‌اند و سال‌هاست وجود دارند؛ برای این منظور کاملاً کفایت می‌کنند.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۵: اهداف Build استاندارد با استفاده از Makefileها

در سمت چپ، بخشی از یک Makefile برای یکی از میکروسرویس‌های Kotlin ما را می‌بینید. در سمت راست، بخشی از Makefile یک سرویس Rust را می‌بینید. فرمان‌های Build کاملاً متفاوت‌اند، اما نکته مهم این است که لیست Targetها در هر دو دقیقاً یکی است.

برای مثال، یک Target به نام pipeline-build داریم که تعیین می‌کند در مرحله Build پایپلاین استقرار برای آن سرویس چه اتفاقی می‌افتد. همچنین Targetهایی برای cell-bootstrap و gcp-cell-bootstrap داریم، چون در Momento می‌توانیم روی سلول‌های AWS یا GCP دیپلوی کنیم. نام Targetها در Makefile یکسان است؛ یعنی سایر بخش‌های زیرساخت که خارج از این سرویس‌های منفرد کار می‌کنند، می‌دانند که در هر جزء اپلیکیشن، همین چرخه عمر مشترک وجود دارد و می‌توانند هنگام استقرار، روی آن حساب کنند.

استانداردسازی – رجیستری سلول

یکی دیگر از بلوک‌های سازنده‌ای که به استانداردسازی اتوماسیون کمک می‌کند، چیزی است که ما آن را «Cell Registry» می‌نامیم. این در اصل یک مکانیسم برای داشتن لیستی از تمام سلول‌هایی است که ساخته‌ایم و متادیتای ضروری مربوط به آنها.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۶: مدل TypeScript برای رجیستری سلول

در Momento، رجیستری سلول را با TypeScript ساخته‌ایم. حدود ۱۰۰ خط TypeScript داریم که چند اینترفیس ساده برای نمایش داده‌های سلول‌ها تعریف می‌کند. مهم‌ترین اینترفیس، CellConfiguration است؛ این اینترفیس تمام اطلاعات حیاتی یک سلول را نگه می‌دارد:

آیا این سلول، سلول Production است یا سلول توسعه‌دهنده (Developer Cell)؟
در کدام Region قرار دارد؟
نام‌های DNS مربوط به Endpointهای این سلول چیست؟
سلول روی AWS است یا روی GCP؟

همچنین یک اینترفیس MomentoOrg داریم که آرایه‌ای از CellConfigurationها را در خود دارد. در واقع Org راهی برای نگه‌داری همه سلول‌های موجود است.

با استفاده از مدلی که این اینترفیس‌ها فراهم می‌کنند، تنها با کمی کدنویسی بیشتر در TypeScript می‌توانیم این اینترفیس‌ها را نمونه‌سازی کنیم و تمام داده واقعی سلول‌ها را بسازیم. در ادامه، بخشی از این کد را می‌بینید:

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۷: داده رجیستری سلول برای سلول «alpha»

این همان داده‌ای است که ممکن است برای سلول “alpha” داشته باشیم. نام سلول، Account ID، Region، پیکربندی DNS و غیره. هر زمان بخواهیم سلول جدید اضافه کنیم، کافی است به کد رجیستری سلول مراجعه کرده و یک ورودی جدید به این آرایه اضافه کنیم.

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

آخرین جزء رجیستری سلول، یک کتابخانه کوچک TypeScript است که می‌داند این داده‌ها را از S3 بخواند و دوباره به یک شیء TypeScript تبدیل کند. این کتابخانه را در یک ریپازیتوری خصوصی npm منتشر می‌کنیم و هر جای دیگری در کد زیرساخت که نیاز باشد، آن را مصرف می‌کنیم. این کار به ما اجازه می‌دهد الگوهای عمومی در اتوماسیون زیرساخت بسازیم؛ مثلاً روی تمام سلول‌ها Loop بزنیم و برای هر یک، اتوماسیون مشابهی تعریف کنیم.

استانداردسازی – اسکریپت Bootstrap سلول

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

اگر فرض کنیم سورس‌کد هر جزء اپلیکیشن در یک ریپازیتوری Git قرار دارد، با توجه به بلوک‌های سازنده‌ای که تاکنون تعریف کردیم، منطق Bootstrap کردن یک سلول جدید به سادگی این است:

  1. با استفاده از رجیستری سلول، تمام متادیتای موردنیاز آن سلول (مثل AWS Account ID، پیکربندی DNS و غیره) را پیدا کنید.

  2. برای هر جزء اپلیکیشن:

    • ریپازیتوری Git آن جزء را Clone کنید.

    • Target استاندارد cell-bootstrap را در Makefile اجرا کنید.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۸: اسکریپت Bootstrap سلول

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

کنار هم قرار دادن همه چیز

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

ایزولیشن (Isolation)

ساده‌ترین روش برای تضمین ایزولیشن بین سلول‌ها در یک محیط AWS این است که برای هر سلول، یک AWS Account جداگانه ایجاد کنید. در ابتدا، اگر به کار با چندین Account عادت نداشته باشید، این کار دلهره‌آور به نظر می‌رسد؛ اما ابزارهای AWS امروز بسیار بالغ شده‌اند و این کار را بسیار ساده‌تر از چیزی می‌کنند که فکر می‌کنید.

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

مزیت دیگر استفاده از چند Account این است که می‌توانید این Accountها را با AWS Organizations به هم لینک کرده و سپس با استفاده از AWS Cost Explorer هزینه‌ها را بر اساس هر سلول مشاهده و تحلیل کنید. اگر به جای این، چند سلول را در یک Account مستقر کنید، برای دیدن هزینه‌ها به تفکیک سلول باید به‌دقت منابع مرتبط با هر سلول را Tag کنید. استفاده از چند Account این مزیت را «رایگان» در اختیارتان می‌گذارد.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟ طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۹: نمای Cost Explorer برای هزینه هر Account مربوط به سلول‌ها

یکی از چالش‌هایی که به‌طور طبیعی همراه معماری سلولی می‌آید، مسیریابی (Routing) است. زمانی که چند سلول ایزوله دارید و در هر یک نسخه‌ای از اپلیکیشن شما اجرا می‌شود، باید راهبردی برای ارسال ترافیک کاربران به سلول مناسب انتخاب کنید.

اگر کاربران از طریق یک SDK یا کلاینت دیگری که خودتان ارائه می‌دهید با سرویس شما تعامل کنند، یک راه ساده برای مسیریابی ترافیک به سلول‌های مختلف این است که برای هر سلول، نام‌های DNS منحصربه‌فرد تعریف کنید. ما در Momento از همین رویکرد استفاده می‌کنیم؛ هنگام ایجاد توکن‌های احراز هویت برای کاربران، نام DNS سلول مناسب آن کاربر را به‌عنوان یک Claim داخل توکن قرار می‌دهیم و سپس کتابخانه‌های کلاینت ما می‌توانند بر اساس آن اطلاعات، ترافیک را مسیریابی کنند.

اما این روش برای همه Use Caseها مناسب نیست. اگر کاربران از طریق مرورگر وب به سرویس شما دسترسی دارند، معمولاً می‌خواهید یک نام DNS واحد به آنها بدهید تا مجبور نباشند از وجود سلول‌ها آگاه باشند. در این سناریو، لازم است یک لایه مسیریابی نازک ایجاد کنید تا ترافیک را هدایت کند.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۱۰: لایه مسیریابی برای ایزولیشن سلول‌ها

این لایه مسیریابی باید تا حد ممکن کوچک و ساده باشد. این لایه باید حداقل منطق لازم برای شناسایی کاربر (بر اساس بخشی از اطلاعات درخواست)، تعیین سلول مقصد و سپس Proxy یا Redirect کردن آن درخواست را در خود داشته باشد.

این لایه مسیریابی تجربه کاربری ساده‌تری فراهم می‌کند (کاربران لازم نیست درباره سلول‌ها چیزی بدانند)، اما در عوض یک مؤلفه جهانی جدید به معماری اضافه می‌کند که باید آن را نگهداری و مانیتور کنید. این لایه همچنین به یک «نقطه شکست واحد» (Single Point of Failure) تبدیل می‌شود؛ چیزی که معماری سلولی در بقیه نقاط تا حد زیادی از آن اجتناب می‌کند. به همین دلیل باید تلاش کنید این لایه تا حد امکان کوچک و ساده باقی بماند.

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

سلول‌های جدید

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

  1. یک حساب AWS جدید در Organization خود ایجاد کنید.

  2. آن Account را به رجیستری سلول اضافه کنید.

  3. اسکریپت Bootstrap سلول را اجرا کنید تا تمام اجزای اپلیکیشن ساخته و دیپلوی شوند.

تمام شد! یک سلول جدید داریم. از آنجا که مراحل چرخه Build را در Makefileهای هر جزء استاندارد کرده‌ایم، منطق استقرار بسیار عمومی شده و بالا آوردن سلول جدید به تلاش کمی نیاز دارد.

استقرارها (Deployments)

استقرار احتمالاً سخت‌ترین مسئله‌ای است که در هر معماری اپلیکیشن باید حل شود، و این موضوع در معماری سلولی حتی حساس‌تر است. خوشبختانه در سال‌های اخیر، پیشرفت‌های قابل‌توجهی در ابزارهای زیرساخت به‌عنوان کد (IaC) انجام شده که بخش‌هایی از این چالش‌ها را قابل‌مدیریت‌تر کرده است.

در گذشته، اغلب ابزارهای IaC از سینتکس پیکربندی اعلانی (Declarative) مثل YAML یا JSON برای تعریف منابع استفاده می‌کردند. اما روندهای جدید، این امکان را فراهم کرده‌اند که تعریف زیرساخت را با استفاده از زبان‌های برنامه‌نویسی واقعی انجام دهید. به جای دست‌وپنجه نرم کردن با فایل‌های پیکربندی پیچیده و طولانی، توسعه‌دهندگان می‌توانند از قدرت و بیان‌پذیری زبان‌های آشنا برای تعریف اجزای زیرساخت استفاده کنند. نمونه‌هایی از این ابزارها عبارت‌اند از:

  • AWS CDK (Cloud Development Kit) برای استقرار زیرساخت مبتنی بر CloudFormation

  • AWS cdk8s برای استقرار زیرساخت Kubernetes

  • CDKTF (CDK for Terraform) برای استقرار زیرساخت با Terraform شرکت HashiCorp

این ابزارها به ما اجازه می‌دهند با استفاده از ساختارهایی مانند حلقه‌های for، صدها خط YAML/JSON تکراری را حذف کنیم.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟
شکل ۱۱: مقایسه CloudFormation JSON با CDK TypeScript

نکته قدرتمند دیگر درباره بیان زیرساخت با زبانی مثل TypeScript این است که می‌توانیم از کتابخانه‌های npm به‌عنوان Dependency استفاده کنیم. این یعنی پروژه‌های IaC ما می‌توانند کتابخانه رجیستری سلول را به‌عنوان Dependency اضافه کنند و به این ترتیب به آرایه شامل متادیتای همه سلول‌ها دسترسی داشته باشند. سپس می‌توانیم روی این آرایه Loop بزنیم و مراحل زیرساختی موردنیاز برای هر سلول را تعریف کنیم. هر زمان سلول جدید اضافه و رجیستری به‌روزرسانی شود، زیرساخت نیز به‌طور خودکار به‌روزرسانی خواهد شد.

ترکیب AWS CDK و AWS CodePipeline به‌طور ویژه قدرتمند است. می‌توانیم یک الگوی عمومی برای تعریف پایپلاین‌های هر جزء اپلیکیشن ایجاد کنیم و مراحل Build و Deploy موردنیاز را تنظیم کنیم، در حالی که عمده کد بین آنها مشترک باقی می‌ماند.

در Momento، برای هر نوع Stage که لازم است به CodePipeline اضافه کنیم (مثل ساخت پروژه، Push کردن Docker image، استقرار یک Stack CloudFormation، استقرار image جدید در یک خوشه Kubernetes و غیره) کمی کد CDK TypeScript داریم. این Stageها را در یک آرایه قرار می‌دهیم و سپس روی آرایه Loop می‌زنیم تا Stageها را به هر پایپلاین اضافه کنیم:

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۱۲: کد CDK برای اضافه کردن Stageها به CodePipeline

ما یک پایپلاین ویژه ساخته‌ایم که آن را «Pipeline of Pipelines» می‌نامیم. این یک پایپلاین «متا» است که مسئول ایجاد پایپلاین‌های منفرد برای هر جزء اپلیکیشن است.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۱۳: Pipeline of Pipelines

این ریپازیتوری به‌عنوان Single Source of Truth برای تمام منطق استقرار ما عمل می‌کند. هر زمان یک توسعه‌دهنده نیاز داشته باشد چیزی در زیرساخت استقرار تغییر دهد، همه این کارها را می‌توان در همین یک نقطه انجام داد. هر تغییری که در لیست مراحل استقرار (مثل تغییر ترتیب سلول‌ها یا پیچیده‌تر کردن مرحله Bake) اعمال کنیم، به‌طور خودکار در همه پایپلاین‌های اجزا منعکس می‌شود. وقتی یک سلول جدید اضافه شود، Pipeline of Pipelines اجرا شده و همه پایپلاین‌های اجزا را به‌روزرسانی می‌کند تا سلول جدید در لیست مراحل استقرار اضافه شود.

برای بهبود داستان دسترس‌پذیری خود، با دقت به ترتیب استقرار در سلول‌های Production فکر می‌کنیم. سلول‌ها بر اساس اندازه، اهمیت و سطح ترافیک به Waveها سازمان‌دهی می‌شوند. در Wave اول، روی سلول‌های Pre-production دیپلوی می‌کنیم که به‌عنوان محیط‌های تست برای تغییرات قبل از ارتقا به Production عمل می‌کنند. اگر استقرار روی این سلول‌ها خوب پیش برود، به‌تدریج روی سلول‌های Production بزرگ‌تر و حساس‌تر استقرار می‌دهیم. این رویکرد مرحله‌ای به انتشار تغییرات، احتمال کشف مشکل قبل از تأثیرگذاری روی تعداد زیادی مشتری را افزایش می‌دهد.

مجوزها

برای مدیریت مجوزهای ورودی و خروجی هر سلول، به‌شدت روی AWS SSO که اکنون با نام IAM Identity Center شناخته می‌شود، تکیه می‌کنیم. این سرویس یک صفحه SSO (ورود یکپارچه) در اختیار ما می‌گذارد که تمام توسعه‌دهندگان می‌توانند با هویت Google خود در آن وارد شوند و سپس به کنسول AWS هر Accountی که اجازه دارند به آن دسترسی داشته باشند، وارد شوند. همچنین دسترسی به Accountهای مشخص را از طریق خط فرمان (CLI) و SDKهای AWS فراهم می‌کند که اتوماسیون وظایف عملیاتی را ساده می‌کند.

این رابط مدیریتی کنترل بسیار دقیق روی دسترسی کاربران در هر Account فراهم می‌کند. برای مثال می‌توان نقش‌هایی مانند “read-only” و “cell-operator” را در یک Account مربوط به سلول تعریف کرد که سطح دسترسی‌های متفاوتی را می‌دهند.

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟
شکل ۱۴: مجوزهای Account در AWS SSO

ترکیب قابلیت‌های نگاشت نقش در AWS SSO با CDK و رجیستری سلول به ما اجازه می‌دهد مجوزهای ورودی و خروجی هر Account سلول را به‌طور کامل خودکار کنیم.

برای مجوزهای ورودی، روی تمام توسعه‌دهندگان و Accountهای سلول در رجیستری Loop می‌زنیم و با استفاده از CDK، نقش‌های مناسب را اعطا می‌کنیم. وقتی Account جدیدی به رجیستری سلول اضافه شود، اتوماسیون به‌طور خودکار مجوزهای صحیح را تنظیم می‌کند. برای مجوزهای خروجی، روی هر سلول در رجیستری Loop می‌زنیم و دسترسی به منابعی مانند تصاویر ECR یا VPCهای خصوصی را در صورت نیاز اعطا می‌کنیم.

مانیتورینگ (Monitoring)

مانیتور کردن تعداد زیادی سلول کار ساده‌ای نیست. ضروری است راهکاری برای مانیتورینگ داشته باشید که تضمین کند اپراتورها می‌توانند در یک نما وضعیت سلامت سرویس در تمام سلول‌ها را ببینند. اینکه انتظار داشته باشید اپراتورها به‌صورت دستی متریک‌ها را در هر Account مربوط به سلول بررسی کنند، راهکار مقیاس‌پذیری نیست.

برای حل این مشکل، فقط کافی است یک راهکار متمرکز متریک انتخاب کنید که بتوانید متریک‌ها را از تمام Accountهای سلول به آن صادر کنید. این راهکار باید پشتیبانی از گروه‌بندی متریک‌ها بر اساس یک بُعد (Dimension) را فراهم کند؛ که در اینجا این بُعد همان نام سلول خواهد بود.

راهکارهای متریک زیادی این قابلیت را ارائه می‌دهند؛ می‌توان متریک‌ها را از چند Account جمع‌آوری کرد و در یک Account مانیتورینگ مرکزی به CloudWatch Metrics ارسال کرد. همچنین ابزارهای شخص‌ثالث زیادی وجود دارند، مانند Datadog، New Relic، LightStep و Chronosphere.

در ادامه، تصویری از یک داشبورد LightStep را می‌بینید که متریک‌های Momento را بر اساس بُعد سلول گروه‌بندی کرده است:

طراحی معماری برای دسترس‌پذیری بالا در فضای ابری با معماری سلولی (cellular architecture) چگونه است؟

شکل ۱۵: داشبورد متریک‌ها با گروه‌بندی بر اساس نام سلول

مزایای اضافی

حالا که توضیح دادیم معماری سلولی چگونه به دسترس‌پذیری بالا کمک می‌کند و ابزارهای مدرن IaC و زیرساخت چگونه می‌توانند به خودکارسازی زیرساخت سلولی کمک کنند، بد نیست به چند مزیت جانبی دیگر این اتوماسیون اشاره کنیم.

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

مزیت بزرگ دیگر این است که توسعه‌دهندگان می‌توانند سلول‌های شخصی در Accountهای توسعه‌ای خود ایجاد کنند. گاهی واقعاً راهی برای تست و Debug یک ویژگی پیچیده که به تعامل چند سرویس یا جزء وابسته است وجود ندارد، مگر اینکه یک محیط واقعی کامل در اختیار داشته باشید.

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

هیچ راه‌حل واحدی وجود ندارد

در این مقاله، درباره چندین ابزار و فناوری که ما برای خودکارسازی زیرساخت سلولی انتخاب کرده‌ایم صحبت کردیم. اما مهم است یادآور شویم که برای هر فناوری ذکرشده در این مقاله، گزینه‌های جایگزین زیادی وجود دارد. برای مثال، در حالی که Momento از چندین ابزار AWS استفاده می‌کند، سایر ارائه‌دهندگان بزرگ Cloud مثل GCP و Azure نیز محصولات مشابهی برای رسیدن به همین اهداف دارند.

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

خلاصه

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

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

 

۹ گام اصلی برای ساختن یک معماری چابک (Agile Architecture) کدامند؟
در توسعه نرم‌افزار، کدام رویکرد بهتر جواب می‌دهد: معماری چابک (Agile)، معماری ناب (Lean)، یا ترکیبی از هر دو؟

دیدگاهتان را بنویسید

سبد خرید
علاقه‌مندی‌ها
مشاهدات اخیر
دسته بندی ها