حالت تاریک
فصل ۶ — ساخت محیطها با پشتهها
فصل ۵ پشتهٔ زیرساخت را مجموعهای از منابع زیرساخت توصیف کرد که بهعنوان یک واحد مدیریت میکنید. یک محیط (environment) هم مجموعهای از منابع زیرساخت است. پس آیا پشته همان محیط است؟ این فصل توضیح میدهد که شاید باشد، اما شاید نباشد.
یک محیط مجموعهای از منابع نرمافزار و زیرساخت است که حول یک هدف خاص سازماندهی شده — مثلاً پشتیبانی از فاز آزمون، یا ارائهٔ سرویس در یک منطقهٔ جغرافیایی. یک پشته، یا مجموعهای از پشتهها، وسیلهای برای تعریف و مدیریت مجموعهای از منابع زیرساخت است. پس از یک پشته، یا چند پشته، برای پیادهسازی یک محیط استفاده میکنید. ممکن است یک محیط را بهصورت یک پشته پیادهسازی کنید، یا محیط را از چندین پشته compose کنید. حتی میتوانید چندین محیط در یک پشته بسازید، اگرچه نباید.
محیطها دربارهٔ چه هستند
مفهوم محیط یکی از چیزهایی است که در IT مسلم میگیریم. اما اغلب در contextهای مختلف معنای کمی متفاوت داریم. در این کتاب، محیط مجموعهای از منابع زیرساخت operationally مرتبط است. یعنی منابع یک محیط از یک فعالیت خاص پشتیبانی میکنند، مانند آزمون یا اجرای یک سیستم. اغلب چندین محیط وجود دارد و هر کدام یک نمونه از همان سیستم را اجرا میکنند.
دو use case معمول برای داشتن چندین محیط که نمونههایی از همان سیستم را اجرا میکنند وجود دارد. یکی پشتیبانی از فرایند تحویل است، و دیگری اجرای چندین نمونهٔ production سیستم.
محیطهای تحویل
آشناترین use case برای چندین محیط، پشتیبانی از فرایند release نرمافزار progressive است — گاهی path to production نامیده میشود. یک build مشخص از برنامه بهترتیب به هر محیط deploy میشود تا فعالیتهای مختلف توسعه و آزمون را پشتیبانی کند تا در نهایت به محیط production deploy شود (شکل ۶-۱).
شکل ۶-۱. محیطهای تحویل ShopSpinner
در سراسر این فصل از این مجموعهٔ محیطها برای توضیح الگوهای تعریف محیطها بهصورت کد استفاده میکنم.
چندین محیط production
ممکن است برای کپیهای کامل و مستقل یک سیستم در production از چندین محیط استفاده کنید. دلایل:
تحمل خطا (Fault tolerance)
اگر یک محیط fail شود، دیگران میتوانند سرویس را ادامه دهند. این ممکن است شامل فرایند failover برای shift کردن load از محیط failed باشد. همچنین میتوانید fault tolerance درون یک محیط داشته باشید، با چندین نمونه از برخی زیرساختها، مانند server cluster. اجرای محیط اضافی همهٔ زیرساخت را duplicate میکند و درجهٔ بالاتری از fault tolerance ایجاد میکند، اگرچه با هزینهٔ بالاتر. برای استراتژیهای continuity که Infrastructure as Code را leverage میکنند به «Continuity» در صفحهٔ ۳۷۶ مراجعه کنید.
مقیاسپذیری (Scalability)
میتوانید workload را بین چندین محیط پخش کنید. مردم اغلب این را جغرافیایی انجام میدهند، با محیط جداگانه برای هر region. چندین محیط ممکن است هم برای scalability و هم fault tolerance استفاده شوند. اگر failure در یک region باشد، load به محیط region دیگر shift میشود تا failure اصلاح شود.
جداسازی (Segregation)
ممکن است چندین نمونه از یک برنامه یا سرویس برای user baseهای مختلف اجرا کنید، مانند clientهای مختلف. اجرای این نمونهها در محیطهای مختلف segregation را تقویت میکند. segregation قویتر ممکن است به برآورده کردن الزامات legal یا compliance و اعتماد بیشتر customer کمک کند.
ShopSpinner برای هر customer ecommerce خود یک application server جداگانه اجرا میکند. با گسترش به North America، Europe و South Asia، تصمیم میگیرد برای هر region یک محیط جداگانه بسازد (شکل ۶-۲).
شکل ۶-۲. محیطهای regional ShopSpinner
استفاده از محیطهای کاملاً جدا، بهجای یک محیط واحد spread شده در regionها، به ShopSpinner کمک میکند مطمئن شود با مقررات مختلف ذخیرهٔ دادهٔ customer در regionهای مختلف comply میکند. همچنین اگر نیاز به تغییراتی با downtime باشد، میتواند در هر region در زمان متفاوت انجام دهد. این همراستا کردن downtime با time zoneهای مختلف را آسانتر میکند.
بعداً ShopSpinner قراردادی با زنجیرهٔ داروخانهٔ The Medicine Barn میبندد. The Medicine Barn باید دادهٔ customer خود را جدا از شرکتهای دیگر host کند بهدلایل regulatory. پس تیم ShopSpinner پیشنهاد میدهد محیط کاملاً جداگانهای dedicated به The Medicine Barn با هزینهٔ بالاتر از shared environment اجرا کند.
محیطها، یکنواختی و پیکربندی
چون چندین محیط قرار است نمونههایی از همان سیستم را اجرا کنند، زیرساخت در هر محیط باید consistent باشد. یکنواختی بین محیطها یکی از محرکهای اصلی Infrastructure as Code است.
تفاوت بین محیطها ریسک رفتار ناهمگون ایجاد میکند. آزمون نرمافزار در یک محیط ممکن است مشکلاتی را که در دیگری رخ میدهد uncover نکند. حتی ممکن است نرمافزار در برخی محیطها deploy شود اما در برخی نه.
از سوی دیگر، معمولاً به برخی تفاوتهای خاص بین محیطها نیاز دارید. محیطهای test ممکن است کوچکتر از production باشند. افراد مختلف ممکن است privilege متفاوت در محیطهای مختلف داشته باشند. محیطهای customerهای مختلف ممکن است feature و ویژگی متفاوت داشته باشند. حداقل، name و ID متفاوت است (appserver-test، appserver-stage، appserver-prod). پس باید حداقل برخی جنبههای محیطها را configure کنید.
یک ملاحظهٔ کلیدی برای محیطها، استراتژی آزمون و تحویل شماست. وقتی همان کد زیرساخت روی هر محیط اعمال میشود، آزمون آن در یک محیط معمولاً confidence میدهد که در محیطهای دیگر درست کار میکند. اگر زیرساخت بین نمونهها زیاد vary کند، این confidence را نمیگیرید.
ممکن است با آزمون کد زیرساخت با مقادیر پیکربندی مختلف confidence را بهبود دهید. با این حال، شاید عملی نباشد مقادیر زیادی را test کنید.
در این موقعیتها، ممکن است به validation اضافی نیاز داشته باشید، مانند post-provisioning test یا monitoring محیطهای production. در فصل ۸ عمیقتر دربارهٔ آزمون و تحویل صحبت میکنم.
الگوهای ساخت محیطها
همانطور که گفتم، محیط مجموعهٔ conceptual از عناصر زیرساخت است، و پشته مجموعهٔ concrete از عناصر زیرساخت. پروژهٔ پشته کد منبعی است که برای ساخت یک یا چند نمونهٔ پشته استفاده میکنید. پس چگونه باید از پروژهها و نمونههای پشته برای پیادهسازی محیطها استفاده کنید؟
دو anti-pattern و یک الگو برای پیادهسازی محیطها با پشتههای زیرساخت توضیح میدهم. هر کدام روشی برای تعریف چندین محیط با پشتههای زیرساخت را توصیف میکند. برخی سیستمها از چندین پشته compose شدهاند، همانطور که در «الگوها و anti-patternها برای ساختاردهی پشتهها» در صفحهٔ ۵۶ توضیح دادم. در «ساخت محیطها با چندین پشته» در صفحهٔ ۷۴ توضیح میدهم این برای چندین محیط چگونه به نظر میرسد.
Anti-pattern: پشتهٔ چندمحیطی
یک پشتهٔ چندمحیطی (multiple-environment stack) زیرساخت چندین محیط را بهعنوان یک نمونهٔ پشته تعریف و مدیریت میکند.
مثلاً اگر سه محیط برای آزمون و اجرای یک برنامه وجود دارد، یک پروژهٔ پشته کد هر سه محیط را شامل میشود (شکل ۶-۳).
شکل ۶-۳. یک پشتهٔ چندمحیطی زیرساخت چندین محیط را بهعنوان یک نمونهٔ پشته مدیریت میکند
انگیزهها
بسیاری هنگام یادگیری ابزار stack جدید این ساختار را میسازند چون طبیعی به نظر میرسد محیطهای جدید را به پروژهٔ موجود اضافه کنند.
پیامدها
وقتی ابزاری برای بهروزرسانی نمونهٔ پشته اجرا میکنید، دامنهٔ یک تغییر بالقوه همهٔ چیز در پشته است. اگر اشتباه یا conflict در کد دارید، همهٔ چیز در نمونه آسیبپذیر است.¹
وقتی محیط production در همان نمونهٔ پشته با محیط دیگر است، تغییر محیط دیگر ریسک ایجاد مشکل production دارد. خطای coding، dependency غیرمنتظره، یا حتی bug در ابزار میتواند production را بشکند وقتی فقط میخواستید محیط test را تغییر دهید.
الگوهای مرتبط
میتوانید blast radius تغییرات را با تقسیم محیطها به پشتههای جدا محدود کنید. یک راه واضح copy-paste environment است (نگاه کنید به «Anti-pattern: Copy-Paste Environments» در صفحهٔ ۷۰) که هر محیط یک پروژهٔ پشتهٔ جداگانه است، اگرچه anti-pattern محسوب میشود.
رویکرد بهتر الگوی reusable stack است (نگاه کنید به «الگو: Reusable Stack» در صفحهٔ ۷۲). یک پروژه برای تعریف ساختار generic یک محیط استفاده میشود و سپس برای مدیریت نمونهٔ پشتهٔ جداگانه برای هر محیط. اگرچه یک پروژه استفاده میشود، پروژه فقط یکبار در هر زمان روی یک نمونهٔ محیط اعمال میشود. پس blast radius تغییرات به همان یک محیط محدود است.
Anti-pattern: Copy-Paste Environments
anti-pattern copy-paste environments برای هر نمونهٔ پشتهٔ زیرساخت یک پروژهٔ کد منبع پشتهٔ جداگانه استفاده میکند.
در مثال سه محیط به نام test، staging و production، برای هر محیط یک پروژهٔ زیرساخت جداگانه وجود دارد (شکل ۶-۴). تغییرات با ویرایش کد در یک محیط و سپس copy کردن تغییرات به هر محیط دیگر بهترتیب انجام میشود.
شکل ۶-۴. copy-paste environment برای هر نمونه یک کپی جداگانه از پروژهٔ کد منبع دارد
انگیزه
copy-paste environments روش intuitive برای نگهداری چندین محیط است. مشکل blast radius anti-pattern multiheaded stack را اجتناب میکند. همچنین میتوانید هر نمونهٔ پشته را بهراحتی customize کنید.
قابلاعمال بودن
copy-paste environments ممکن است مناسب باشد اگر میخواهید نمونههای مختلف را نگه دارید و تغییر دهید و نگران duplication کد یا consistency نیستید.
پیامدها
نگهداری چندین copy-paste environment میتواند challenging باشد. وقتی میخواهید تغییر کد دهید، باید آن را به هر پروژه copy کنید. احتمالاً باید هر نمونه را جداگانه test کنید، چون تغییر ممکن است در یکی کار کند اما در دیگری نه.
copy-paste environments اغلب از configuration drift رنج میبرند (نگاه کنید به «Configuration Drift» در صفحهٔ ۱۷). استفاده از copy-paste environments برای محیطهای تحویل reliability فرایند deploy و validity آزمون را بهدلیل inconsistency بین محیطها کاهش میدهد.
copy-paste environment ممکن است در ابتدا consistent باشد، اما variationها با زمان creep میکنند.
پیادهسازی
copy-paste environment را با copy کردن کد پروژه از یک نمونهٔ پشته به پروژهٔ جدید میسازید. سپس کد را برای customize کردن نمونهٔ جدید ویرایش میکنید. وقتی به یک پشته تغییر میدهید، باید آن را copy-paste کنید در همهٔ پروژههای پشتهٔ دیگر، در حالی که customization هر کدام را حفظ میکنید.
الگوهای مرتبط
environment branch (نگاه کنید به «تحویل کد از repository کد منبع» در صفحهٔ ۳۲۵) ممکن است شکلی از copy-paste environments محسوب شود. هر branch کپی از کد دارد و مردم با merge بین branchها کد copy میکنند. اعمال مداوم کد (نگاه کنید به «اعمال مداوم کد» در صفحهٔ ۳۵۰) ممکن است از pitfalls copy-paste اجتناب کند، چون تضمین میکند کد از یک محیط به محیط بعدی modify نشده. ویرایش کد بهعنوان بخشی از merge به environment branch خطرات anti-pattern copy-paste را ایجاد میکند.
الگوی wrapper stack (نگاه کنید به «الگو: Wrapper Stack» در صفحهٔ ۹۰) هم شبیه copy-paste environments است. wrapper stack برای هر محیط یک پروژهٔ پشتهٔ جداگانه دارد تا پارامترهای پیکربندی را set کند. اما کد پشته در stack componentها، مانند کد ماژول reusable، پیادهسازی شده. خود آن کد برای هر محیط copy-paste نمیشود، بلکه مانند reusable stack promote میشود.
با این حال، اگر مردم بیش از پارامترهای پایهٔ نمونهٔ پشته به پروژههای wrapper stack اضافه کنند، میتواند به anti-pattern copy-paste environment devolve کند.
در مواردی که نمونههای پشته قرار است همان پشته را represent کنند، الگوی reusable stack معمولاً مناسبتر است.
الگو: Reusable Stack
یک reusable stack پروژهٔ کد منبع زیرساخت است که برای ساخت چندین نمونه از یک پشته استفاده میشود (شکل ۶-۵).
شکل ۶-۵. چندین نمونهٔ پشته ساختهشده از یک پروژهٔ reusable stack
انگیزه
reusable stack میسازید تا چندین نمونهٔ consistent زیرساخت را نگه دارید. وقتی به کد پشته تغییر میدهید، میتوانید آن را در یک نمونه اعمال و test کنید، و سپس همان نسخهٔ کد را برای ساخت یا بهروزرسانی نمونههای اضافی استفاده کنید. میخواهید نمونههای جدید پشته را با ceremony minimal تأمین کنید، شاید حتی خودکار.
بهعنوان مثال، تیم ShopSpinner کد مشترک را از پروژههای پشتهٔ مختلف که هر کدام application server استفاده میکردند extract کرد. اعضای تیم کد مشترک را در ماژولی گذاشتند که هر پروژهٔ پشته استفاده میکرد. بعداً فهمیدند پروژههای پشته برای applicationهای customer هنوز خیلی شبیه به هم هستند. علاوه بر استفاده از ماژول برای ساخت application server، هر پشته کدی برای ساخت database و سرویسهای logging و reporting dedicated برای هر customer داشت.
تغییر و test تغییرات این کد در customerهای مختلف دارد hassle میشد، و ShopSpinner هر ماه customer جدید sign up میکرد. پس تیم تصمیم گرفت یک پروژهٔ پشتهٔ واحد که customer application stack را تعریف میکند بسازد. این پروژه هنوز از ماژول Java application server مشترک استفاده میکند، همانطور که چند application دیگر (Jira و GoCD). اما پروژه هم کد راهاندازی بقیهٔ زیرساخت اختصاصی هر مشتری را دارد.
حالا وقتی customer جدید sign up میکنند، اعضای تیم از پروژهٔ customer stack مشترک برای ساخت نمونهٔ جدید استفاده میکنند. وقتی چیزی را در codebase پروژه fix یا improve میکنند، روی test instance اعمال میکنند تا مطمئن شوند OK است، و سپس یکییکی به customer instanceها roll out میکنند.
قابلاعمال بودن
میتوانید reusable stack را برای محیطهای تحویل یا چندین محیط production استفاده کنید. این الگو وقتی مفید است که variation زیادی بین محیطها نیاز ندارید. وقتی محیطها نیاز به customize سنگین دارند کمتر applicable است.
پیامدها
توانایی تأمین و بهروزرسانی چندین پشته از یک پروژه scalability، reliability و throughput را تقویت میکند. میتوانید نمونههای بیشتری با effort کمتر مدیریت کنید، تغییرات با ریسک failure پایینتر بدهید، و تغییرات را سریعتر به سیستمهای بیشتر roll out کنید.
معمولاً باید برخی جنبههای پشته را برای نمونههای مختلف configure کنید، حتی اگر فقط name باشد. یک فصل کامل دربارهٔ این صحبت میکنم (فصل ۷).
باید کد پروژهٔ پشته را قبل از اعمال تغییرات روی زیرساخت business-critical test کنید. چند فصل دربارهٔ این صحبت میکنم، از جمله فصلهای ۸ و ۹.
پیادهسازی
reusable stack را بهعنوان پروژهٔ پشتهٔ زیرساخت میسازید، و سپس هر بار که میخواهید نمونهای از پشته بسازید یا بهروزرسانی کنید ابزار مدیریت پشته را اجرا میکنید.
از syntax دستور ابزار پشته بگویید کدام نمونه را میخواهید بسازید یا بهروزرسانی کنید. با Terraform مثلاً state file یا workspace متفاوت برای هر نمونه specify میکنید. با CloudFormation یک stack ID یکتا برای هر نمونه pass میکنید.
دستور نمونهٔ زیر دو نمونهٔ پشته از یک پروژه با دستور خیالی stack تأمین میکند. دستور argument env را میگیرد که نمونههای یکتا را identify میکند:
text
> stack up env=test --source mystack/src
SUCCESS: stack 'test' created
> stack up env=staging --source mystack/src
SUCCESS: stack 'staging' createdبهعنوان قاعده، باید از پارامترهای ساده برای تعریف تفاوت بین نمونههای پشته استفاده کنید — string، number، یا در برخی موارد list. علاوه بر این، زیرساختی که reusable stack میسازد نباید بین نمونهها زیاد vary کند.
الگوهای مرتبط
reusable stack بهبودی روی anti-pattern copy-paste environment است (نگاه کنید به «Anti-pattern: Copy-Paste Environments» در صفحهٔ ۷۰) و نگهداشتن چندین نمونه consistent را آسانتر میکند.
الگوی wrapper stack (نگاه کنید به «الگو: Wrapper Stack» در صفحهٔ ۹۰) از stack component برای تعریف reusable stack استفاده میکند، اما پروژهٔ پشتهٔ متفاوتی برای set کردن مقادیر پارامتر هر نمونه دارد.
ساخت محیطها با چندین پشته
الگوی reusable stack رویکردی برای پیادهسازی چندین محیط توصیف میکند. در فصل ۵ روشهای مختلف ساختاردهی زیرساخت یک سیستم در چندین پشته را توصیف کردم (نگاه کنید به «الگوها و anti-patternها برای ساختاردهی پشتهها» در صفحهٔ ۵۶). چند راه برای پیادهسازی پشتهها وجود دارد که این دو بعد محیطها و ساختار سیستم را ترکیب میکند.
حالت ساده پیادهسازی کل سیستم بهصورت یک پشته است. وقتی نمونهای از پشته تأمین میکنید، یک محیط کامل دارید. این را در نمودار الگوی reusable stack (شکل ۶-۵) نشان دادم.
اما سیستمهای بزرگتر را باید به چندین پشته تقسیم کنید. مثلاً اگر الگوی service stack را دنبال کنید («الگو: Service Stack» در صفحهٔ ۶۰) برای هر سرویس یک پشتهٔ جدا دارید، همانطور که در شکل ۶-۶ نشان داده شده.
شکل ۶-۶. مثال استفاده از پشتهٔ زیرساخت جداگانه برای هر سرویس
برای ساخت چندین محیط، برای هر محیط یک نمونه از هر service stack تأمین میکنید، همانطور که در شکل ۶-۷ نشان داده شده.
شکل ۶-۷. استفاده از چندین پشته برای ساخت هر محیط
دستوراتی مانند زیر برای ساخت محیط کامل با چندین پشته استفاده میکنید:
text
> stack up env=staging --source product_browse_stack/src
SUCCESS: stack 'product_browse-staging' created
> stack up env=staging --source product_search_stack/src
SUCCESS: stack 'product_search-staging' created
> stack up env=staging --source shopping_basket_stack/src
SUCCESS: stack 'shopping_basket-staging' createdفصل ۱۵ استرategیهای تقسیم سیستمها به چندین پشته را توضیح میدهد، و فصل ۱۷ نحوهٔ integrate کردن زیرساخت بین پشتهها را بحث میکند.
جمعبندی
reusable stack باید الگوی workhorse برای بیشتر تیمهایی باشد که نیاز به مدیریت زیرساخت بزرگ دارند. پشته واحد مفیدی برای test و deliver کردن تغییرات است. اطمینان میدهد هر نمونهٔ محیط بهصورت consistent تعریف و ساخته شده. جامعیت پشته، بهجای ماژول، بهعنوان واحد تغییر، توانایی deliver کردن آسان و سریع و مکرر تغییرات را تقویت میکند.
¹ Charity Majors تجربیات دردناک کار با multiple-environment stack را در یک پست blog به اشتراک گذاشت.