خب یه مروری کنیم پستهای قبلی رو:
توصیه میکنم که حتما این پستها رو هم مطالعه کنید. بریم که ادامه بدیم.
priority and preemption
توی کوبرنتیز، هر پاد نماینده مجموعهای از کانتینرهای اجرایی است. گاهی تعداد منابع در دسترس (مثل CPU و RAM) برای اجرای همه پادها کافی نیست. در این شرایط، مفهوم اولویت پدیدار میشود تا اسکجولر تشخیص دهد کدام پادها مهمتر هستند یا باید در زمان ازدحام منابع، حتماً اجرا شوند.
هنگامی که پادی با اولویت بالا در صف اسکجولینگ است اما منابع کافی برای اجرای آن وجود ندارد، اسکجولر سعی میکند با متوقف کردن یا حذف موقت پادهای کماهمیتتر، منابع لازم را آزاد کند. به این فرآیند Preemption میگویند. با این کار، از منابع محدود موجود به شکلی هوشمندانهتر استفاده میشود و پادهایی که برای عملکرد کل سیستم یا اپلیکیشن حیاتی هستند، در اولویت قرار میگیرند. البته تمام این اولویتها توسط ما ایجاد و مدیریت میشه و دست خودمون هست. نکتهی دیگه اینکه اسکجولر برای جانمایی پادها روی نودهای کلاستر یه صفی داره و بررسی میکنه که اگر توی صفش یه پاد با اولویت بالایی وجود داشته باشه و براش جا نباشه سعی میکنه با جابهجایی پادهای کم اهمیت فضا رو برای استقرار پاد با اولویت بالاتر فراهم کنه.
پادها میتوانند اولویت داشته باشند. اولویت نشاندهنده اهمیت یک پاد نسبت به سایر پادها است. اگر یک پاد نتواند Schedule شود، Scheduler تلاش میکند پادهای با اولویت پایینتر را برکنار (Preempt) کند تا شرایط برای زمانبندی پاد در حال انتظار فراهم گردد.
pod priority
وقتی اولویت پاد فعال باشد، Scheduler پادهای در حال انتظار را بر اساس اولویت آنها مرتب میکند و یک پاد در حال انتظار با اولویت بالاتر را در صف زمانبندی جلوتر از پادهای دیگر با اولویت کمتر قرار میدهد. به همین دلیل، اگر نیازمندیهای زمانبندی یک پاد با اولویت بالا برآورده شوند، ممکن است این پاد زودتر از پادهای با اولویت پایینتر زمانبندی شود. اگر چنین پادی نتواند اسکجول شود، مثلا نودی که منابع کافی داشته باشه رو پیدا نکنه اسکجولر برای این پاد، آنگاه اسکجولر ادامه داده و سعی میکند سایر پادهای با اولویت پایینتر را جابهجا نماید.
وقتی پادها ایجاد میشوند، ابتدا وارد یک صف شده و منتظر زمانبندی (Scheduling) میمانند. اسکجولر یکی از پادها را از صف انتخاب کرده و تلاش میکند آن را روی یک Node مستقر کند. اگر هیچ نودی یافت نشود که تمام نیازمندیهای مشخصشده پاد را برآورده سازد، Preemption برای پاد در انتظار فعال میشود.
در این سناریو، پادی که در انتظار زمانبندی است را پاد P بنامیم. منطق Preemption تلاش میکند یک نود بیابد که با حذف یک یا چند پاد با اولویت پایینتر نسبت به P، امکان زمانبندی P روی آن نود فراهم شود. اگر چنین نودی پیدا شود، یک یا چند پاد با اولویت پایینتر از روی آن نود خارج (evict) میشوند. پس از حذف این پادها، P میتواند روی آن گره زمانبندی شود.
priority class
در کوبرنتیز PriorityClass یک آبجکت بدون فضای نام (non-namespaced) است که یک نگاشت از نام کلاس اولویت به مقدار عددی اولویت ایجاد میکند. نام این آبجکت در فیلد name مربوط به metadata آن مشخص میشود. مقدار اولویت در فیلد اجباری value تعیین میگردد. هر چه مقدار بالاتر باشد، اولویت بیشتر است. نام یک آبجکت PriorityClass باید یک نام زیردامنهی معتبر DNS باشد و نباید با -system شروع شود.
با استفاده از این ساختار ما مشخص میکنیم که اولویت پادهامون به چه صورت است که در زمان قرارگرفتن روی نود و بلند شدن از روی نود اولویت آنها مشخص شود. به صورت کلی پادها با اولویت بالا آخر از همه از روی نودها بلند میشوند و همواره اول از همه روی نودها قرار میگیرند. مثال بزنم براتون مثلا نود شما به کلاستر اضافه شده. خیلی مهمه که پادهای مربوط به نتورک و استوریج و این چنین موارد اول روی آن نود ایجاد و استقرار پیدا کنه تا بقیه پادها. چون مابقی پادها برای عملکرد درست خود به این پادها نیاز دارند.
ابتدا Kubelet کلاس QoS و سپس مقدار اولویت پاد را برای حذف پادها در نظر میگیرد. این امر تنها زمانی اتفاق میافتد که در نودها کمبود منابع وجود داشته باشد.
با این حال، منطق Preemption تنها زمانی فعال میشود که پادهای با اولویت بالا در صف زمانبندی (Scheduling Queue) باشند. در فرایند Preemption، زمانبند رتبه QoS پاد را نادیده میگیرد. در حالی که حذف مبتنی بر QoS بدون نیاز به صف زمانبندی، تنها بهدلیل کمبود منابع انجام میشود.
فرآیند Node-pressure eviction فرآیندی است که طی آن kubelet بهصورت پیشگیرانه پادها را برای بازیابی منابع بر روی نودها خاتمه میدهد. منابع اون نود داره تموم میشه و با این شرایط عملکرد و کارایی اون نود هم با مشکل مواجه میشه برای همین برخی از پادها رو جابه جا میکنه تا بتونه روی اون نود فضا خالی کنه.
مثلا kubelet منابعی مانند CPU، حافظه، فضای دیسک و inode سیستم فایل را در نودهای کلاستر شما پایش میکند. هنگامی که یک یا چند مورد از این منابع به سطح خاصی از مصرف برسند، kubelet میتواند بهصورت پیشگیرانه یک یا چند پاد روی آن نود را از کار بیندازد تا منابع را بازیابی کرده و از بروز کمبود جلوگیری کند. به صورت نگاه بالا و از سمت اطاق فکر کوبرنتیز نگاه کنیم یه نود داره منابعش تموم میشه برای این که بتونه ادامهی کارایی خودش رو داشته باشه پادهای با اولویت پایینتر رو جابه جا میکنه و روی نودهای دیگه بالا میاره تا عملکرد نود از بین نره.
نکته دیگه اینکه kubelet تلاش میکند قبل از خاتمه دادن به پادهای کاربران نهایی، منابع سطح نود را بازیابی کند. برای مثال، زمانی که منابع دیسک محدود شده باشند، ابتدا ایمیجهای بلااستفاده کانتینرها را حذف میکند.
حذف از طریق API (API-initiated eviction) فرآیندی است که در آن شما با استفاده از Eviction API، یک آبجکت Eviction ایجاد میکنید که موجب خاتمهی کنترلشدهی پاد میشود.
شما میتوانید با فراخوانی مستقیم Eviction API و استفاده از یک کلاینت kube-apiserver (مثلاً دستور kubectl drain
) عمل حذف را درخواست کنید. این کار یک آبجکت Eviction ایجاد میکند که به سرور API دستور میدهد پاد را خاتمه دهد.
Pod Disruption Budget
یک Pod Disruption Budget (PDB) به شما امکان میدهد محدودیتی برای ایجاد اختلال در برنامهتان تعیین کنید، تا زمانی که پادهای آن برای دلایلی نظیر بهروزرسانیها یا عملیات نگهداری روتین بر روی نودهای کوبرنتیز نیاز به زمانبندی مجدد دارند، میزان اختلال را کنترل کنید.
یک PDB تعداد پادهای یک برنامه تکرارشونده (replicated) را که میتوانند همزمان تحت اختلال ارادی (voluntary disruption) قرار بگیرند، محدود میکند. برای مثال، یک برنامه مبتنی بر quorum مایل است که تعداد رپلیکاهای در حال اجرای خود هرگز کمتر از تعداد مورد نیاز برای حفظ کوآروم نشود.
در کوبرنتیز، پادها ممکن است به دلایل مختلف از جمله بهروزرسانی نودها، ارتقای نرمافزار، یا جابجایی بار کاری نیاز به حذف موقت یا جابجایی داشته باشند. برخی از این عملیات، داوطلبانه (voluntary) هستند؛ یعنی مدیر یا ابزارهای مدیریت کلاستر بهطور ارادی پادها را از بین میبرند یا جابهجا میکنند (برای مثال، با اجرای فرمان kubectl drain
برای تعمیر نود).
در کوبرنتیز PDBها به شما این امکان را میدهند که تضمین کنید هنگام انجام این تغییرات ارادی، فقط تعداد محدودی از پادهای برنامه کاهش مییابد. در واقع PDB کاری میکند که حتی در صورت انجام عملیات نگهداری یا ارتقا، سطح پایداری و در دسترس بودن برنامه حفظ شود. این خیلی نکتهی مهمی است. البته برای تعداد پاد چه بیشتر و چه کمتر از یه تعداد میتونیم این کار رو کنیم. این قابلیت به ما این امکان رو میده که مدیریت بهتری روی کلاستر اپلیکیشن خودمون داشته باشیم و جلوگیری میکنه که اعضای اون کلاستر از یه تعدادی بیشتر و یا کمتر نشود.
زمانبند کوبرنتیز (kube-scheduler) را میتوان طوری پیکربندی کرد که با استفاده از تابع اولویت RequestedToCapacityRatioResourceAllocation، منابع عادی و منابع توسعهیافته را بهصورت Bin Packing بستهبندی کند. تابعهای اولویت (Priority Functions) میتوانند برای تنظیم دقیق و شخصیسازی رفتار زمانبند بر اساس نیازهای خاص استفاده شوند. به عبارتی طوری برنامهریزی کند که منابع مشخصی از نودها مورد استفاده قرار بگیرد به گونهای که یه نود خیلی خالی و یکی دیگه خیلی پر نباشه تا بتونه همواره یه تعداد معقولی از پادها رو دیپلوی کنه. در صورتی که نیاز باشه منابع بیشتری به یه پاد اختصاص پیدا کنه و یه جورایی منابع زیادی لازم باشه و هیچ نودی اون رو نداشته باشه کوبرنتیز با ری اسکجولینگ که انجام میده برخی از پادها رو جابه جا میکنه تا این فضا رو برای اون پاد فراهم کنه.
bin packing
امنیت در کوبرنتیز مستلزم توجه همزمان به زیرساخت اجرا (مانند پابلیک کلاد، دیتاسنتر سازمانی، یا سرورهای co-located)، اجزای کلاستر کوبرنتیز و در نهایت اپلیکیشنهایی است که روی آن اجرا میشوند. اگر لایهی زیربنایی، مثلاً محیط ابری، به درستی ایمن نشده باشد، هیچ تضمینی وجود ندارد که بخشهای بالادستی که روی آن اجرا میشوند امن باقی بمانند. اغلب ارائهدهندگان سرویس ابری دستورالعملهایی برای افزایش امنیت Workloadها در محیط خود ارائه میدهند تا مطمئن شوید که تنظیمات شبکه، دسترسیها و سرویسهای زیرساختی به درستی امنسازی شدهاند.
Kubernetes Security
دو حوزه اصلی برای تأمین امنیت کوبرنتیز عبارتند از:
امنسازی اجزای کلاستر : این بخش شامل Control Plane و مؤلفههایی مانند kube-apiserver، kube-scheduler، kube-controller-manager و غیره میشود. همچنین تنظیمات درست برای etcd (پایگاه داده کوبرنتیز) و Kubelet روی نودها، اطمینان از TLS در ارتباطات داخلی، RBAC و استفاده از Admission Controllerها برای محدودسازی پیکربندیهای ناامن ضروری است.
امنسازی اپلیکیشنها : کانتینرها، ایمیجها و کد درون آنها، نحوه تعامل کانتینر با سیستمعامل میزبان و سایر کانتینرها و در نهایت شبکه کانتینری و مخازن ذخیرهسازی همگی از لایههای امنیتی مهم هستند. بررسی مداوم ایمیجها برای آسیبپذیریها، استفاده از اسکنرهای امنیتی، بهکارگیری حداقل دسترسیها (Principle of Least Privilege) و بهروزرسانی مستمر کتابخانهها و پکیجها از موارد کلیدی در ایمنسازی این لایه است.
سیاستهای امنیتی پاد در کوبرنتیز
از سیاستهای امنیتی برای محدودسازی سطح دسترسی و پیکربندی پادها استفاده میشود. برای مثال:
حداقل محدودیت و حداکثر دسترسی را دارد که اگرچه انعطافپذیری بیشتری میدهد، اما خطر بالاتری از نظر امنیتی دارد. کلا توصیه نمیشه که این طوری پیش بره. این نوع دسترسی میتونه برای ما دردسرهای زیادی ایجاد کنه.
اعمال حداقل محدودیتها برای جلوگیری از افزایش سطح دسترسی شناختهشده، بدون ایجاد محدودیتهای شدید.
سختترین سیاستها را دنبال میکند و بر اساس بهترین شیوههای سختسازی پادها عمل میکند. این حالت برای سرویسهای حساس و محیطهای تولیدی حیاتی است.
این سیاستها را میتوان در حالتهای مختلف مانند Enforce
(اعمال اجباری و جلوگیری از ایجاد پاد نامطابق)، Audit
(صرفاً ثبت وقایع بدون جلوگیری)، و Warn
(هشدار به کاربر اما اجازهی ساخت پاد) فعال کرد.
تا پیش از نسخههای اخیر، PodSecurityPolicy (PSP) برای تعریف شرایط امنیتی پادها در سطح کلاستر استفاده میشد. اما از نسخه ۱.۲۱ این ویژگی منسوخ و در نسخه ۱.۲۵ حذف شده است. جایگزین این قابلیت، استفاده از Pod Security Admission است که سیاستهای امنیتی را در سطح Namespaces اعمال میکند. همچنین میتوانید از پلاگینهای خیلی خوبی همانند Gatekeeper (مبتنی بر Open Policy Agent ) استفاده کنید تا قوانین دلخواه امنیتی خود را با کنترل بیشتری اجرا نمایید.
نکات تکمیلی و توصیهها
استفاده از شبکه ایمن: اطمینان حاصل کنید که Network Policies در کوبرنتیز فعال بوده و ارتباطات بین پادها، نودها و سرویسها مطابق اصل حداقل دسترسی کنترل شده است. به صورت پیشفرض تمام پادها در تمام namespaceها به همدیگر دسترسی دارند. با نتورک پالیسی میتونیم به خوبی جلوی این دسترسیها رو بگیریم و این پیشفرض رو تغییر بدیم. در ضمن میتونیم پادهای خودمون رو Protect کنیم.
رمزنگاری و مدیریت کلیدها: استفاده از TLS برای ارتباطات داخلی، رمزنگاری دادههای حساس در etcd، و مدیریت امن Secretها از طریق Key Management Service (KMS) یا Vault ضروری است. به صورت پیشفرض کلیدها و رمزها داخل کوبرنتیز encode هستند که با استفاده از این ابزارها میتونیم آنها رو encrypt کنیم.
ثبت وقایع و پایش امنیتی: فعالسازی Auditing در kube-apiserver، استفاده از ابزارهای پایش و هشداردهی (مانند Prometheus و Alertmanager)، و بررسی مداوم لاگها میتواند از بروز حملات و نفوذهای احتمالی جلوگیری کند. با استفاده از Auditing ما به خوبی متوجه میشم که کی داره چی کار میکنه و این خیلی کمکمون میکنه که Forensics انجام بدیم و بررسی کنیم که چه اتفاقی افتاده که این مشکل رو برای ما ایجاد کرده. کلا دیتای خیلی مهمی رو در اختیار ما قرار میده.
مدیریت چرخه عمر ایمیجها: تنها از ایمیجهای رسمی و معتبر استفاده کرده، آنها را اسکن و بهروزرسانی کنید. بهتر است ریجستری خصوصی و امن داشته باشید تا از ورود ایمیجهای مخرب جلوگیری شود و همواره ایمیجهایی که داریم استفاده میکنیم رو پاییش و بررسی کنیم و از بروز مشکلات احتمالی روی آنها جلوگیری کنیم.
امنیت کوبرنتیز یک فرآیند چندلایه است: از تأمین امنیت زیرساخت ابری و اعمال کنترلهای دسترسی RBAC در کنترلپلین، تا اجرای سیاستهای امنیتی پاد، مدیریت ایمیجها، تنظیم شبکههای کانتینری و جایگزین کردن PSP با مکانیسمهای جدیدتر مانند Pod Security Admission. با رعایت این اصول و توصیهها، میتوانید محیط کوبرنتیز خود را در برابر تهدیدات مختلف مقاومتر و مطمئنتر نمایید.
در ادامه مسیر تو بلاگ پستهای بعدی موارد مربوط به سطح دسترسی رو توی کوبرنتیز بررسی میکنیم.
مراقب خودتون باشید. 🌹🐳🌹