حالت تاریک
فصل ۱ — مقدمه
Kubernetes یک orchestrator متنباز برای deploy برنامههای containerized است. در ابتدا توسط Google توسعه یافت و از دهه تجربه در deploy سیستمهای scalable و reliable در containerها از طریق APIهای application-oriented الهام گرفت.¹
از زمان معرفی در ۲۰۱۴، Kubernetes به یکی از بزرگترین و محبوبترین پروژههای متنباز جهان تبدیل شده است. به API استاندارد برای ساخت برنامههای cloud-native تبدیل شده و تقریباً در هر public cloud حضور دارد. Kubernetes زیرساخت اثباتشدهای برای سیستمهای توزیعشده است که برای developerهای cloud-native در همهٔ مقیاسها — از خوشهای از رایانههای Raspberry Pi تا انباری پر از جدیدترین ماشینها — مناسب است. نرمافزار لازم برای ساخت و deploy موفق سیستمهای توزیعشدهٔ reliable و scalable را فراهم میکند.
شاید بپرسید وقتی میگوییم «سیستمهای توزیعشدهٔ reliable و scalable» منظورمان چیست. روزبهروز سرویسهای بیشتری از طریق شبکه و API ارائه میشوند. این APIها اغلب توسط یک سیستم توزیعشده تحویل داده میشوند؛ بخشهای مختلف پیادهسازی API روی ماشینهای مختلف اجرا میشوند، از طریق شبکه به هم وصلاند و اقداماتشان را از طریق ارتباط شبکه هماهنگ میکنند. چون به این APIها برای همهٔ جنبههای زندگی روزمرهمان (مثلاً یافتن مسیر به نزدیکترین بیمارستان) بیشتر وابستهایم، این سیستمها باید بسیار reliable باشند. نباید fail کنند، حتی اگر بخشی از سیستم crash کند یا به هر دلیلی از کار بیفتد. بههمینترتیب باید availability را حتی در rollout نرمافزار یا رویدادهای نگهداری حفظ کنند. در نهایت، چون بخش بیشتری از جهان آنلاین میشود و از چنین سرویسهایی استفاده میکند، باید بسیار scalable باشند تا ظرفیتشان با استفادهٔ فزاینده بدون redesign رادیکال سیستم توزیعشدهای که سرویسها را پیاده میکند، رشد کند.
¹ Brendan Burns et al., "Borg, Omega, and Kubernetes: Lessons Learned from Three Container-Management Systems over a Decade," ACM Queue 14 (2016): 70–93, available at http://bit.ly/2vIrL4S.
بسته به زمان و دلیلی که این کتاب را در دست گرفتهاید، ممکن است سطحهای متفاوتی از تجربه با containerها، سیستمهای توزیعشده و Kubernetes داشته باشید. شاید قصد دارید برنامهتان را روی زیرساخت public cloud، در data center خصوصی یا محیط hybrid بسازید. صرفنظر از تجربهتان، باور داریم این کتاب به شما کمک میکند بیشترین بهره را از Kubernetes ببرید.
دلایل زیادی برای استفاده از containerها و container APIهایی مثل Kubernetes وجود دارد، اما باور داریم همه به یکی از این مزایا برمیگردند:
- Velocity
- Scaling (هم نرمافزار و هم تیمها)
- Abstract کردن زیرساخت
- Efficiency
در بخشهای بعدی توضیح میدهیم Kubernetes چگونه میتواند هر یک از این ویژگیها را فراهم کند.
Velocity
Velocity مؤلفهٔ کلیدی در تقریباً همهٔ توسعهٔ نرمافزار امروز است. صنعت نرمافزار از ارسال محصول بهصورت CD یا DVD جعبهای به نرمافزاری که از طریق سرویسهای وب تحویل داده میشود و ساعتی بهروز میشود، تکامل یافته است. این منظر در حال تغییر یعنی تفاوت شما با رقبا اغلب سرعت توسعه و deploy اجزا و ویژگیهای جدید، یا سرعت پاسخ به نوآوریهای دیگران است.
با این حال مهم است توجه کنید velocity به معنای صرف سرعت خام نیست. کاربران همیشه به دنبال بهبود تکراریاند، اما بیشتر به سرویس highly available علاقهمندند. زمانی قابلقبول بود سرویس هر شب نیمهشب برای نگهداری down باشد. اما امروز همهٔ کاربران uptime مداوم میخواهند، حتی اگر نرمافزاری که اجرا میکنند دائماً در حال تغییر باشد.
در نتیجه velocity بر اساس تعداد خام featureهایی که در ساعت یا روز ship میکنید اندازهگیری نمیشود، بلکه بر اساس تعداد چیزهایی که میتوانید ship کنید در حالی که سرویس highly available بماند.
به این ترتیب، containerها و Kubernetes ابزارهایی میدهند که سریع حرکت کنید و در عین حال available بمانید. مفاهیم هستهای که این را ممکن میکنند عبارتاند از:
- Immutability
- Declarative configuration
- سیستمهای online self-healing
این ایدهها همه به هم مرتبطاند و سرعت deploy reliable نرمافزار را بهشدت بهبود میدهند.
ارزش Immutability
Containerها و Kubernetes developerها را تشویق میکنند سیستمهای توزیعشدهای بسازند که به اصول immutable infrastructure پایبند باشند. با immutable infrastructure، وقتی یک artifact در سیستم ساخته شد، از طریق تغییرات کاربر تغییر نمیکند.
بهطور سنتی، کامپیوترها و سیستمهای نرمافزاری بهعنوان mutable infrastructure تلقی میشدند. با mutable infrastructure، تغییرات بهصورت بهروزرسانیهای تدریجی روی سیستم موجود اعمال میشوند. این بهروزرسانیها میتوانند یکجا یا در بازهٔ طولانی پخش شوند. upgrade سیستم با ابزار apt-get update نمونهٔ خوبی از بهروزرسانی سیستم mutable است. اجرای apt بهترتیب binaryهای بهروز را دانلود میکند، روی binaryهای قدیمی کپی میکند و بهروزرسانیهای تدریجی روی فایلهای پیکربندی انجام میدهد. در سیستم mutable، وضعیت فعلی زیرساخت بهصورت یک artifact واحد نمایش داده نمیشود، بلکه انباشتی از بهروزرسانیها و تغییرات تدریجی در طول زمان است. در بسیاری از سیستمها این بهروزرسانیهای تدریجی نهتنها از upgrade سیستم، بلکه از تغییرات operator هم میآیند. علاوه بر این، در هر سیستمی که تیم بزرگی اداره میکند، احتمال زیاد است این تغییرات توسط افراد مختلف انجام شده و در بسیاری موارد جایی ثبت نشده باشند.
در مقابل، در سیستم immutable، بهجای سری بهروزرسانیها و تغییرات تدریجی، یک image کامل و جدید ساخته میشود و بهروزرسانی فقط image قدیمی را در یک عملیات با image جدیدتر جایگزین میکند. تغییر تدریجی وجود ندارد. همانطور که تصور میکنید، این جابهجایی بزرگی نسبت به دنیای سنتیتر configuration management است.
برای ملموستر کردن در دنیای containerها، دو روش متفاوت برای upgrade نرمافزار در نظر بگیرید:
- میتوانید به container لاگین کنید، دستوری برای دانلود نرمافزار جدید اجرا کنید، server قدیمی را kill کنید و جدید را start کنید.
- میتوانید image container جدید بسازید، به container registry push کنید، container موجود را kill کنید و یکی جدید start کنید.
در نگاه اول، این دو رویکرد تقریباً یکسان به نظر میرسند. پس چه چیزی در ساخت container جدید reliability را بهبود میدهد؟
تفاوت کلیدی artifactی است که میسازید و سابقهٔ نحوهٔ ساخت آن. این سوابق فهم دقیق تفاوتهای نسخهٔ جدید را آسان میکنند و اگر مشکلی پیش آمد، تعیین میکنند چه تغییر کرده و چگونه اصلاح شود.
علاوه بر این، ساخت image جدید بهجای تغییر image موجود یعنی image قدیمی هنوز هست و میتواند سریع برای rollback در صورت خطا استفاده شود. در مقابل، وقتی binary جدید را روی binary موجود کپی میکنید، چنین rollbackی تقریباً غیرممکن است.
Immutable container imageها در هستهٔ همهٔ چیزهایی است که در Kubernetes میسازید. امکان تغییر imperative containerهای در حال اجرا وجود دارد، اما این anti-pattern است و فقط در موارد extreme که گزینهٔ دیگری نیست (مثلاً اگر تنها راه موقت تعمیر سیستم production حیاتی است) باید استفاده شود. و حتی در آن صورت، تغییرات باید بعداً از طریق بهروزرسانی declarative configuration ثبت شوند، وقتی آتش خاموش شد.
Declarative Configuration
Immutability فراتر از containerهای در حال اجرا در cluster به نحوهٔ توصیف برنامه به Kubernetes گسترش مییابد. همهچیز در Kubernetes یک object پیکربندی declarative است که وضعیت مطلوب سیستم را نشان میدهد. کار Kubernetes اطمینان از تطابق وضعیت واقعی جهان با این وضعیت مطلوب است.
مثل mutable در برابر immutable infrastructure، declarative configuration جایگزین imperative configuration است؛ جایی که وضعیت جهان با اجرای سری دستورالعملها تعریف میشود نه اعلام وضعیت مطلوب جهان. در حالی که دستورات imperative عمل را تعریف میکنند، پیکربندیهای declarative وضعیت را تعریف میکنند.
برای درک این دو رویکرد، وظیفهٔ تولید سه replica از یک نرمافزار را در نظر بگیرید. با رویکرد imperative، پیکربندی میگوید «A را اجرا کن، B را اجرا کن و C را اجرا کن.» پیکربندی declarative متناظر «replicas برابر سه» خواهد بود.
چون وضعیت جهان را توصیف میکند، declarative configuration برای فهمیدن نیاز به اجرا ندارد. اثر آن بهطور مشخص اعلام شده است. چون اثرات declarative configuration قبل از اجرا قابل فهم است، declarative configuration بسیار کمخطاتر است. علاوه بر این، ابزارهای سنتی توسعهٔ نرمافزار مثل source control، code review و unit testing میتوانند در declarative configuration به روشهایی استفاده شوند که برای دستورات imperative غیرممکن است. ایدهٔ ذخیرهٔ declarative configuration در source control اغلب «infrastructure as code» نامیده میشود.
ترکیب وضعیت declarative ذخیرهشده در version control و توانایی Kubernetes برای تطبیق واقعیت با این وضعیت declarative، rollback تغییر را بهشدت آسان میکند. کافی است وضعیت declarative قبلی سیستم را دوباره اعلام کنید. این در سیستمهای imperative معمولاً غیرممکن است، چون اگرچه دستورات imperative میگویند چگونه از نقطهٔ A به B برسید، بهندرت دستورالعملهای معکوس برای بازگشت را شامل میشوند.
سیستمهای Self-Healing
Kubernetes یک سیستم online و self-healing است. وقتی پیکربندی وضعیت مطلوب دریافت میکند، فقط یکبار مجموعهای از اقدامات برای تطبیق وضعیت فعلی با وضعیت مطلوب انجام نمیدهد. بهطور مداوم اقدام میکند تا وضعیت فعلی با وضعیت مطلوب تطابق داشته باشد. یعنی نهتنها سیستم را initialize میکند، بلکه در برابر هر failure یا اغتشاشی که ممکن است سیستم را ناپایدار کند و reliability را تحت تأثیر قرار دهد محافظت میکند.
تعمیر سنتیتر operator شامل سری دستی گامهای mitigation یا مداخلهٔ انسانی در پاسخ به alert است. تعمیر imperative مثل این گرانتر است (چون معمولاً نیاز به operator on-call دارد). همچنین کندتر است، چون انسان اغلب باید بیدار شود و لاگین کند. علاوه بر این کمreliableتر است چون سری عملیات تعمیر imperative از همهٔ مشکلات مدیریت imperative بخش قبل رنج میبرد. سیستمهای self-healing مثل Kubernetes هم بار operator را کاهش میدهند و هم reliability کلی سیستم را با انجام تعمیرات reliable سریعتر بهبود میدهند.
بهعنوان نمونهٔ ملموس این رفتار self-healing، اگر وضعیت مطلوب سه replica را به Kubernetes اعلام کنید، فقط سه replica نمیسازد — بهطور مداوم اطمینان میدهد دقیقاً سه replica وجود دارد. اگر دستی replica چهارم بسازید، Kubernetes یکی را destroy میکند تا تعداد به سه برگردد. اگر دستی یک replica را destroy کنید، Kubernetes یکی میسازد تا دوباره به وضعیت مطلوب برگردید.
سیستمهای online self-healing velocity developer را بهبود میدهند چون زمان و انرژی که ممکن است صرف operations و نگهداری کنید میتواند به توسعه و تست featureهای جدید برود.
در شکل پیشرفتهتر self-healing، کار قابلتوجهی اخیراً در paradigm operator برای Kubernetes انجام شده است. با operatorها، منطق پیشرفتهتر لازم برای نگهداری، scale و heal یک نرمافزار خاص (مثلاً MySQL) در برنامهٔ operator رمزگذاری میشود که بهصورت container در cluster اجرا میشود. کد در operator مسئول تشخیص و heal سلامت هدفمندتر و پیشرفتهتر از آنچه از طریق self-healing عمومی Kubernetes ممکن است است. اغلب این بهصورت «operatorها» بستهبندی میشود که در بخش بعدی بحث میشوند.
Scaling سرویس و تیمهای شما
با رشد محصول، اجتنابناپذیر است که هم نرمافزار و هم تیمهایی که آن را توسعه میدهند scale کنید. خوشبختانه Kubernetes میتواند در هر دو هدف کمک کند. Kubernetes scalability را با ترجیح معماریهای decoupled به دست میآورد.
Decoupling
در معماری decoupled، هر جزء از سایر اجزا با APIهای تعریفشده و service load balancer جدا شده است. APIها و load balancerها هر بخش سیستم را از دیگران جدا میکنند. APIها بافری بین implementer و consumer فراهم میکنند و load balancerها بافری بین instanceهای در حال اجرای هر سرویس.
Decoupling اجزا از طریق load balancerها scale کردن برنامههایی که سرویس شما را تشکیل میدهند را آسان میکند، چون افزایش اندازه (و در نتیجه ظرفیت) برنامه میتواند بدون تنظیم یا reconfigure لایههای دیگر سرویس انجام شود.
Decoupling سرورها از طریق APIها scale کردن تیمهای توسعه را آسانتر میکند، چون هر تیم میتواند روی یک microservice کوچکتر با surface area قابلفهم تمرکز کند. APIهای crisp بین microserviceها مقدار overhead ارتباط بین تیمها لازم برای ساخت و deploy نرمافزار را محدود میکند. این overhead ارتباطی اغلب عامل محدودکنندهٔ اصلی هنگام scale تیمهاست.
Scaling آسان برای برنامهها و Clusterها
بهطور ملموس، وقتی نیاز به scale سرویس دارید، ماهیت immutable و declarative Kubernetes پیادهسازی این scaling را بهشدت ساده میکند. چون containerهای شما immutable هستند و تعداد replica فقط عددی در config declarative است، scale کردن سرویس به بالا فقط تغییر عددی در فایل پیکربندی، اعلام این وضعیت declarative جدید به Kubernetes و واگذاری بقیه به آن است. یا میتوانید autoscaling راه بیندازید و Kubernetes آن را برایتان انجام دهد.
البته چنین scalingی فرض میکند منابع در cluster شما برای مصرف در دسترس است. گاهی واقعاً باید خود cluster را scale up کنید. باز هم Kubernetes این کار را آسانتر میکند. چون بسیاری از ماشینها در یک cluster از نظر عملیاتی با سایر ماشینهای آن مجموعه یکساناند و برنامهها با container از جزئیات ماشین decouple شدهاند، افزودن منابع اضافی به cluster فقط imaging ماشین جدید از همان class و join کردن به cluster است. این با چند دستور ساده یا image ماشین از پیش آماده انجام میشود.
یکی از چالشهای scale منابع ماشین، پیشبینی استفادهٔ آنهاست. اگر روی زیرساخت فیزیکی اجرا میکنید، زمان بهدست آوردن ماشین جدید بر حسب روز یا هفته است. در زیرساخت فیزیکی و cloud، پیشبینی هزینهٔ آینده دشوار است چون پیشبینی رشد و نیازهای scaling برنامههای خاص سخت است.
Kubernetes میتواند پیشبینی هزینهٔ compute آینده را ساده کند. برای فهمیدن چرا، scale up سه تیم A، B و C را در نظر بگیرید. تاریخاً دیدهاید رشد هر تیم بسیار متغیر و سخت برای پیشبینی است. اگر برای هر سرویس ماشین جدا provision میکنید، مجبورید بر اساس حداکثر رشد مورد انتظار هر سرویس پیشبینی کنید، چون ماشینهای اختصاصیافته به یک تیم برای تیم دیگر قابل استفاده نیستند. اگر بهجای آن از Kubernetes برای decouple کردن تیمها از ماشینهای خاص استفاده کنید، میتوانید رشد را بر اساس رشد تجمیعی هر سه سرویس پیشبینی کنید. ترکیب سه نرخ رشد متغیر در یک نرخ رشد واحد نویز آماری را کاهش میدهد و پیشبینی قابلاطمینانتری از رشد مورد انتظار تولید میکند. علاوه بر این، decouple کردن تیمها از ماشینهای خاص یعنی تیمها میتوانند بخشهای کسری از ماشینهای یکدیگر را به اشتراک بگذارند و overheadهای مرتبط با پیشبینی رشد منابع محاسباتی را بیشتر کاهش دهند.
Scale کردن تیمهای توسعه با Microserviceها
همانطور که در تحقیقات مختلف ذکر شده، اندازهٔ ایدهآل تیم «two-pizza team» یا تقریباً شش تا هشت نفر است. این اندازهٔ گروه اغلب به اشتراکگذاری خوب دانش، تصمیمگیری سریع و حس هدف مشترک منجر میشود. تیمهای بزرگتر تمایل به مشکلات سلسلهمراتب، دید ضعیف و درگیری داخلی دارند که agility و موفقیت را مختل میکند.
با این حال، بسیاری از پروژهها برای موفقیت و دستیابی به اهداف به منابع بسیار بیشتری نیاز دارند. در نتیجه تنشی بین اندازهٔ ایدهآل تیم برای agility و اندازهٔ لازم تیم برای اهداف نهایی محصول وجود دارد.
راهحل رایج این تنش، توسعهٔ تیمهای decoupled و service-oriented است که هر کدام یک microservice میسازند. هر تیم کوچک مسئول طراحی و تحویل سرویسی است که تیمهای کوچک دیگر مصرف میکنند. تجمیع همهٔ این سرویسها در نهایت پیادهسازی surface area کلی محصول را فراهم میکند.
Kubernetes abstractionها و APIهای متعددی میدهد که ساخت این معماریهای microservice decoupled را آسانتر میکند:
- Podها، یا گروههای container، میتوانند imageهای container ساختهشده توسط تیمهای مختلف را در یک واحد deployable گروهبندی کنند.
- Kubernetes serviceها load balancing، naming و discovery برای جدا کردن یک microservice از دیگری فراهم میکنند.
- Namespaceها isolation و access control میدهند تا هر microservice درجهٔ تعامل سایر سرویسها با خود را کنترل کند.
- objectهای Ingress frontend آسانمصرفی میدهند که چند microservice را در یک surface area API خارجیشده ترکیب میکند.
در نهایت، decouple کردن image container برنامه و ماشین یعنی microserviceهای مختلف میتوانند روی همان ماشین colocate شوند بدون تداخل، که overhead و هزینهٔ معماریهای microservice را کاهش میدهد. ویژگیهای health-checking و rollout Kubernetes رویکرد یکنواختی به rollout برنامه و reliability تضمین میکند تا تکثیر تیمهای microservice منجر به تکثیر رویکردهای مختلف برای چرخهٔ عمر production و operations سرویس نشود.
Separation of Concerns برای Consistency و Scaling
علاوه بر consistencyی که Kubernetes به operations میآورد، decoupling و separation of concerns تولیدشده توسط stack Kubernetes به consistency بسیار بیشتری برای لایههای پایینتر زیرساخت منجر میشود. این به شما امکان میدهد operations زیرساخت را برای مدیریت بسیاری ماشینها با یک تیم کوچک و متمرکز scale کنید. زیاد دربارهٔ decoupling container برنامه و ماشین/OS صحبت کردیم، اما جنبهٔ مهم این decoupling این است که container orchestration API قرارداد crispی میشود که مسئولیتهای application operator را از cluster orchestration operator جدا میکند. به این «خط not my monkey, not my circus» میگوییم. application developer به service-level agreement (SLA) تحویلشده توسط container orchestration API تکیه میکند، بدون نگرانی از جزئیات نحوهٔ دستیابی به این SLA. بههمینترتیب، reliability engineer مربوط به container orchestration API روی تحویل SLA orchestration API تمرکز میکند بدون نگرانی از برنامههایی که روی آن اجرا میشوند.
این decoupling مسئولیتها یعنی تیم کوچکی که cluster Kubernetes را اجرا میکند میتواند مسئول پشتیبانی صدها یا حتی هزاران تیم باشد که برنامهها را در آن cluster اجرا میکنند (شکل ۱-۱). بههمینترتیب، تیم کوچکی میتواند مسئول دهها (یا بیشتر) cluster در سراسر جهان باشد. مهم است توجه کنید همان decoupling container و OS به reliability engineerهای OS امکان میدهد روی SLA سیستمعامل ماشین فردی تمرکز کنند. این خط مسئولیت جداگانهٔ دیگری میشود؛ operatorهای Kubernetes به SLA سیستمعامل تکیه میکنند و operatorهای OS فقط نگران تحویل آن SLA هستند. باز هم این به شما امکان میدهد تیم کوچکی از متخصصان OS را به ناوگانی از هزاران ماشین scale کنید.
شکل ۱-۱. نمایی از چگونگی decouple شدن تیمهای operations مختلف با استفاده از APIها
البته اختصاص حتی تیم کوچکی به مدیریت OS فراتر از مقیاس بسیاری سازمانهاست. در این محیطها، Kubernetes-as-a-Service (KaaS) مدیریتشده توسط public cloud provider گزینهٔ عالی است. با ubiquitous شدن Kubernetes، KaaS نیز بیشتر در دسترس شده تا جایی که اکنون تقریباً در هر public cloud ارائه میشود. البته استفاده از KaaS محدودیتهایی دارد، چون operator برای شما تصمیم میگیرد clusterهای Kubernetes چگونه ساخته و پیکربندی شوند. مثلاً بسیاری از پلتفرمهای KaaS featureهای alpha را غیرفعال میکنند چون میتوانند cluster مدیریتشده را ناپایدار کنند.
علاوه بر سرویس Kubernetes کاملاً مدیریتشده، اکوسیستم پررونقی از شرکتها و پروژهها برای نصب و مدیریت Kubernetes وجود دارد. طیف کاملی از راهحلها بین انجام «به روش سخت» و سرویس کاملاً مدیریتشده است. در نتیجه، تصمیم استفاده از KaaS یا مدیریت خودتان (یا چیزی بین این دو) تصمیمی است که هر کاربر باید بر اساس مهارتها و نیازهای موقعیت خود بگیرد. اغلب برای سازمانهای کوچک، KaaS راهحل آسانمصرفی میدهد که زمان و انرژی را روی ساخت نرمافزار برای کارشان متمرکز میکند نه مدیریت cluster. برای سازمان بزرگتری که میتواند تیم اختصاصی برای مدیریت cluster Kubernetes داشته باشد، ممکن است مدیریت خودشان منطقی باشد چون انعطاف بیشتری در قابلیتها و operations cluster میدهد.
Abstract کردن زیرساخت
هدف public cloud ارائهٔ زیرساخت self-service آسانمصرف برای developerهاست. با این حال، اغلب APIهای cloud حول آینهکردن زیرساختی که IT انتظار دارد متمرکزند، نه مفاهیمی (مثلاً «virtual machine» بهجای «application») که developerها میخواهند مصرف کنند. علاوه بر این، در بسیاری موارد cloud جزئیات پیادهسازی یا سرویسهای خاص cloud provider دارد. مصرف مستقیم این APIها اجرای برنامه در محیطهای متعدد یا بین cloud و محیط فیزیکی را دشوار میکند.
حرکت به سمت container APIهای application-oriented مثل Kubernetes دو مزیت ملموس دارد. اول، همانطور که قبلاً توصیف کردیم، developerها را از ماشینهای خاص جدا میکند. این نقش machine-oriented IT را آسانتر میکند، چون ماشینها میتوانند بهصورت تجمیعی برای scale کردن cluster اضافه شوند، و در context cloud هم portability بالایی میدهد چون developerها API سطح بالاتری مصرف میکنند که بر اساس APIهای خاص زیرساخت cloud پیاده شده است.
وقتی developerها برنامههایشان را بر اساس container image میسازند و با APIهای portable Kubernetes deploy میکنند، انتقال برنامه بین محیطها یا حتی اجرا در محیطهای hybrid فقط ارسال config declarative به cluster جدید است. Kubernetes plug-inهای متعددی دارد که شما را از cloud خاص abstract میکند. مثلاً Kubernetes serviceها میدانند چگونه load balancer روی همهٔ public cloudهای اصلی و چند زیرساخت private و فیزیکی مختلف بسازند. بههمینترتیب، PersistentVolume و PersistentVolumeClaimهای Kubernetes میتوانند برنامههای شما را از پیادهسازی storage خاص abstract کنند. البته برای دستیابی به این portability باید از سرویسهای مدیریتشده cloud (مثلاً DynamoDB آمازون، CosmosDB Azure یا Cloud Spanner گوگل) اجتناب کنید، که یعنی مجبورید راهحلهای storage متنباز مثل Cassandra، MySQL یا MongoDB را deploy و مدیریت کنید.
جمعبندی همهٔ اینها، ساختن روی abstractionهای application-oriented Kubernetes اطمینان میدهد تلاشی که برای ساخت، deploy و مدیریت برنامه میگذارید واقعاً portable در محیطهای گسترده است.
Efficiency
علاوه بر مزایای developer و مدیریت IT که containerها و Kubernetes میدهند، مزیت اقتصادی ملموسی هم برای abstraction وجود دارد. چون developerها دیگر به ماشین فکر نمیکنند، برنامههایشان میتوانند روی همان ماشینها colocate شوند بدون تأثیر روی خود برنامهها. یعنی taskهای چند کاربر میتوانند متراکم روی ماشینهای کمتر بستهبندی شوند.
Efficiency را میتوان با نسبت کار مفید انجامشده توسط ماشین یا process به کل انرژی صرفشده برای آن اندازه گرفت. وقتی به deploy و مدیریت برنامهها میپردازیم، بسیاری از ابزارها و processهای موجود (مثلاً bash script، apt update یا configuration management imperative) تا حدی inefficient هستند. هنگام بحث efficiency اغلب مفید است هم به هزینهٔ اجرای سرور و هم هزینهٔ انسانی مدیریت آن فکر کنیم.
اجرای سرور هزینهای بر اساس مصرف برق، نیاز خنککنندگی، فضای data center و قدرت compute خام دارد. وقتی سرور rack و روشن میشود (یا کلیک و spin up میشود)، متر بهطور لفظی شروع به دویدن میکند. هر زمان بیکاری CPU پول هدررفته است. در نتیجه بخشی از مسئولیتهای system administrator حفظ utilization در سطوح قابلقبول است که نیاز به مدیریت مداوم دارد. اینجاست که containerها و workflow Kubernetes وارد میشوند. Kubernetes ابزارهایی میدهد که توزیع برنامهها در خوشهٔ ماشینها را خودکار میکند و سطح utilization بالاتری نسبت به tooling سنتی ممکن میسازد.
افزایش بیشتر efficiency از این میآید که محیط تست developer میتواند سریع و ارزان بهصورت مجموعهای از containerهای در حال اجرا در نمای شخصی از cluster Kubernetes مشترک ساخته شود (با featureای به نام namespace). در گذشته، راه انداختن cluster تست برای developer ممکن بود به معنای روشن کردن سه ماشین بود. با Kubernetes ساده است همهٔ developerها یک cluster تست مشترک داشته باشند و استفادهٔشان را روی مجموعهٔ بسیار کوچکتری از ماشینها تجمیع کنند. کاهش تعداد کلی ماشینهای استفادهشده به نوبهٔ خود efficiency هر سیستم را بالا میبرد: چون بیشتر منابع (CPU، RAM و غیره) روی هر ماشین فردی استفاده میشود، هزینهٔ کلی هر container بسیار پایینتر میشود.
کاهش هزینهٔ instanceهای توسعه در stack شما امکان practiceهای توسعهای را میدهد که قبلاً از نظر هزینه ممنوع بود. مثلاً با deploy برنامه از طریق Kubernetes، deploy و تست هر commit که هر developer در کل stack داده میدهد قابل تصور میشود.
وقتی هزینهٔ هر deployment بر حسب تعداد کمی container اندازهگیری میشود، نه چند VM کامل، هزینهٔ چنین تستی بهشدت پایینتر است. بازگشت به ارزش اصلی Kubernetes، این افزایش تست velocity را هم بالا میبرد، چون سیگنالهای قوی از reliability کد و granularity جزئیات لازم برای شناسایی سریع محل معرفی مشکل را دارید.
خلاصه
Kubernetes برای تغییر رادیکال نحوهٔ ساخت و deploy برنامهها در cloud ساخته شد. اساساً برای دادن velocity، efficiency و agility بیشتر به developerها طراحی شد. امیدواریم بخشهای قبل ایدهای از چرایی deploy برنامهها با Kubernetes داده باشد. حالا که متقاعد شدید، فصلهای بعدی نحوهٔ deploy برنامه را آموزش میدهند.