کنترلکنندههای Kubernetes قلب تپنده مدل اعلامی این پلتفرم هستند. آنها وضعیت فعلی خوشه را مشاهده میکنند، آن را با وضعیت مطلوب مطابقت میدهند و دائماً در حال هماهنگسازی هستند. اما وقتی پای مقیاسهای بزرگ به میان میآید، سختترین چالشها دیگر نوشتن حلقه هماهنگسازی نیست. بلکه حفظ صحت و درستی در شرایطی است که حافظههای پنهان دچار تاخیر میشوند، اشیاء مدام جابهجا میشوند و اجرای قوانین نیازمند یک دید کامل از خوشه است. در این مقاله، درسهایی را که از راهاندازی دو کنترلکننده حیاتی در Amazon EKS در مقیاس بزرگ آموختهایم، مرور میکنیم.
اگرچه Kubernetes اتصال اولیه پادها را فراهم میکند، اما اجرای دقیق قوانین ترافیکی را به پلتفرم زیرین واگذار میکند. این یعنی باید قوانینی مانند «چه کسی اجازه صحبت با چه کسی را دارد» را به دستورالعملهایی تبدیل کنیم که مسیر داده (datapath) بتواند اجرا کند. برای یک آزمایش کوچک این مسئله ساده است، اما برای یک محیط تولیدی، تبدیل به چالشی میشود که باید یک بار قوانین را تعریف کرد و مطمئن شد در طول روز که پادها ساخته و نابود میشوند، این قوانین حفظ میشوند.
در Amazon EKS، دو کنترلکننده این شکاف را پر میکنند:
- کنترلکننده خطمشی شبکه (Network Policy Controller): ترافیک داخل خوشه را مدیریت کرده و مشخص میکند کدام پادها مجاز به ارتباط با یکدیگر هستند.
- کنترلکننده منابع VPC (VPC Resource Controller): نحوه دسترسی پادهای منتخب به منابع AWS خارج از خوشه را با تخصیص گروههای امنیتی AWS به هر پاد تنظیم میکند. این همان فایروال حالتداری است که AWS برای نمونههای EC2 استفاده میکند.
به طور معمول، یک پاد گروه امنیتی خاص خود را ندارد و گروه امنیتی گره را به ارث میبرد. برای مثال، اگر یک پایگاه داده RDS بخواهد به یک پاد خاص اجازه دسترسی بدهد، باید ورودی را از گروه امنیتی گره مجاز کند که به تمام پادهای دیگر روی آن گره نیز اجازه دسترسی میدهد. اما یک پاد با گروه امنیتی اختصاصی میتواند مستقیماً مجوز داده شود و در نتیجه قانون فقط به آن بار کاری واحد اجازه میدهد.
هر دو کنترلکننده از نوع معمولی Kubernetes هستند. آنها خوشه را رصد میکنند، وضعیت را در حافظه ذخیره میکنند و آنچه را که مشاهده میکنند با آنچه اعلام شده مطابقت میدهند. این هماهنگسازی به شما این امکان را میدهد که قوانین را یک بار اعلام کنید و به حفظ آنها با وجود تغییرات پادها اعتماد کنید. پلتفرم EKS این کنترلکنندهها را در خوشههایی از چند پاد تا صد هزار گره اجرا میکند و این تنوع مقیاس، منبع تجربیات ارزشمندی است. در مقیاس کوچک، مشکلاتی مانند ردپای حافظه، صحت هماهنگسازی و تعامل اجزا به راحتی نادیده گرفته میشوند؛ اما با هزاران گره به محدودیتهای اساسی تبدیل میشوند.
«در مقیاسهای کوچک، ردپای حافظه کنترلکننده، صحت هماهنگسازی و تعاملات مؤلفه به راحتی نادیده گرفته میشوند. با هزاران گره، آنها به محدودیتهای الزامآوری تبدیل میشوند که تفاوت بین یک قانون پایدار و یک قانون ناقص را رقم میزنند.»
این مقاله به بررسی نحوه کار این کنترلکنندهها، تأثیر مقیاسدهی بر آنها و الگوهایی که از راهاندازی آنها پدیدار شده میپردازد.
دو انتخاب طراحی حیاتی در مقیاس بزرگ
کنترلکننده خط مشی شبکه: از برچسبها تا اجرا در سطح بسته
یک NetworkPolicy اعلام قصدی است که با برچسبها نوشته میشود. مثلاً، پادهای با برچسب app=web فقط از پادهای با برچسب app=api ترافیک دریافت کنند. برچسبها قانون را پایدار نگه میدارند، اما مسیر داده با آدرسهای IP کار میکند. برنامه eBPF روی هر گره با IPهای مشخص پادها تطبیق میکند، بنابراین نیاز است که برچسب app=api به مجموعه فعلی IPها تبدیل شده و این مجموعه با تغییر پادها بهروز بماند.
سوال کلیدی این است که این «حل» کجا انجام شود. برخی پیادهسازیها این کار را روی هر گره انجام میدهند. اما EKS آن را یک بار و به صورت متمرکز انجام میدهد. یک هماهنگکننده در کنترلکننده خط مشی شبکه، اشیاء NetworkPolicy، Podها، Namespaceها و Serviceها را نظارت میکند، انتخابکنندهها را به IPها حل میکند و نتیجه را در یک منبع به نام PolicyEndpoint ذخیره میکند.
زمانی که یک خط مشی با تعداد زیادی پاد مطابقت دارد، کنترلکننده نتیجه را بین چندین PolicyEndpoint تقسیم میکند. این یعنی قانون app=web که 5000 پاد دارد به 5 PolicyEndpoint با 1000 IP تقسیم میشود. این تکنیک از مشکلات اشتراک گذاری حافظه در یک شیء جلوگیری کرده و بهروزرسانیهای جزئی را ممکن میسازد.
برای کسب اطلاعات بیشتر در مورد معماری کنترلکنندههای Kubernetes میتوانید به مستندات رسمی Kubernetes Controllers مراجعه کنید.
چالشهای مقیاس: حافظه و صحت
هر کنترلکننده تمام وضعیت خوشه را در حافظه خود ذخیره میکند. حتی یک حافظه پنهان با یک میلیون شیء، با فرض 1 کیلوبایت برای هر شیء، حداقل 1 گیگابایت حافظه مصرف میکند. در نتیجه هر کنترلکننده معمولاً میلیاردها بایت حافظه مصرف میکند. مشکل زمانی ایجاد میشود که دو منبع با اندازههای مختلف با هم مرتبط شوند. برای مثال، اگر یک NetworkPolicy با هزاران پاد مطابقت داشته باشد و هر پاد پس از 10 ثانیه جایگزین شود، کنترلکننده باید دائماً مجموعه IPها را دوباره محاسبه کند.
یکی از مهمترین تکنیکهای بهینهسازی، پردازش دستهای (batching) است. به جای واکنش فوری به هر تغییر، کنترلکننده تغییرات را در یک بازه زمانی کوتاه جمعآوری کرده و سپس یکباره پردازش میکند. این کار فشار بر CPU و API سرور را کاهش میدهد.
با مطالعه تجربیات Amazon EKS و الگوهای طراحی مانند استفاده از حافظههای پنهان چندسطحی و تقسیمبندی میتوان از اشتباهات رایج جلوگیری کرد. همچنین ابزارهایی مانند Prometheus برای مانیتورینگ این کنترولرها بسیار مفید هستند.
یافتههای کلیدی و درسهای اجرایی
- جلوگیری از وابستگی بر اساس اندازه (Size-Based Affinity): اشیاء با اندازههای بسیار متفاوت نباید در یک حافظه پنهان مشترک ذخیره شوند. مثلاً، هممکانی یک NetworkPolicy با 10 IP و یک PolicyEndpoint با 10,000 IP مشکلساز است.
- توانمندسازی اجرای افزونگی و تجمیع (Force-Redundancy and Aggregation): یک WorkQueue باید تضمین کند که محاسبات روی یک کلید پیش از تکرار شدن کامل شود. این کار با مشاهدات سریال روی یک کلید تضمین میشود.
- استفاده از تخصیصهای تدریجی (Incremental Allocations): به جای یک تخصیص بزرگ در آرایه، بهتر است از sliceها با ظرفیت اولیه 0 و الحاق تدریجی استفاده شود. این کار تکهتکه شدن حافظه را کاهش میدهد.
- نمایهسازی مداوم و تست فشار (Continuous Profiling and Stress Testing): انجام تستهای استرس در مقیاس واقعی برای شناسایی تنگناهای حافظه و CPU ضروری است. ابزارهایی مانند
pprofبسیار مفید هستند.
در نهایت باید گفت که گاهی یک انتخاب ساده در استفاده از حافظه یا نحوه پردازش تغییرات میتواند تفاوت بین یک کنترلکننده پایدار و یک کنترلکننده ناپایدار باشد. این درسها فراتر از شبکه هستند و برای هر توسعهدهنده Kubernetes که با مقیاس سروکار دارد، کاربردی خواهند بود.
تصاویر و نمودارهای مرتبط با معماری داخلی این کنترلکنندهها میتوانند به درک بهتر مفاهیم کمک کنند.





