Kubernetes v1.36 خبر از قابلیت‌های جدیدی برای کاهش مشکل Staleness در controllers و افزایش Observability می‌دهد. این تغییرات به خصوص برای کسانی که در حوزه DevOps / SRE با controllers سروکار دارند مفید است و به کاهش رفتارهای ناخواسته ناشی از دیدگاه‌های قدیمی (stale) کمک می‌کند. 🚀

Staleness چیست؟ به زبان ساده، Staleness وقتی رخ می‌دهد که cache محلی controller دید به‌روزی از وضعیت کلاستر نداشته باشد. برای سرعت بخشیدن، controllers معمولاً از یک cache محلی تغذیه می‌کنند که با watch روی API server پر می‌شود. وقتی قرار است عملی انجام شود، controller ابتدا به cache نگاه می‌کند و در صورت نیاز با reconciliation آن را همگام‌سازی می‌کند. 🔁

مشکلاتی مثل restart شدن controller، قطعی API server، یا رخدادهای پررقابت روی منابع (مثل pods) می‌توانند باعث شوند cache از وضعیت واقعی عقب بماند. نتیجه می‌تواند اقدام نادرست controller، عدم اقدام وقتی لازم است یا تأخیر زیاد در اقدام باشد—مسائل ظریفی که اغلب تنها بعد از بروز خطا در تولید مشخص می‌شوند.

تغییرات در v1.36 شامل بهبودهایی در client-go و همچنین بکارگیری این بهبودها در پیاده‌سازی کنترلرهای پراستفاده در kube-controller-manager است. در client-go یک صف جدید با پردازش اتمیک دسته‌ای اضافه شده که با نام ویژگی AtomicFIFO شناخته می‌شود. این سازوکار می‌تواند عملیاتِ دریافت‌شده به‌صورت batch (مثلاً لیست اولیه‌ای که informer برای پر کردن cache می‌گیرد) را به‌صورت اتمیک مدیریت کند و از ناسازگاری ناشی از رخدادهای خارج از ترتیب جلوگیری کند.

بعلاوه، client-go حالا امکان introspect روی cache را فراهم می‌کند تا آخرین resource version که cache دیده را بخوانید—این کار با تابع LastStoreSyncResourceVersion() روی Store انجام می‌شود. این قابلیت پایه‌ی امکانات کاهش staleness در kube-controller-manager است و به controller‌ها اجازه می‌دهد قبل از اقدام، وضعیت cache را بررسی کنند.

در kube-controller-manager، v1.36 این منطق را برای چهار controller پراستفاده فعال کرده است: DaemonSet controller، StatefulSet controller، ReplicaSet controller و Job controller. این کنترلرها عمدتاً روی pods عمل می‌کنند که معمولاً بیشترین contention را دارند. قابلیت به‌صورت پیش‌فرض روشن است و از طریق feature gate عمومی StaleControllerConsistency و gateهای مخصوص هر کنترلر مثل StaleControllerConsistencyDaemonSet می‌توان آن را غیرفعال کرد.

رفتار وقتی feature روشن باشد این است که controller قبل از اقدام، آخرین resource version cache را چک می‌کند؛ اگر این مقدار کمتر از resource versionی باشد که controller خودش قبلاً روی API server نوشته، controller اقدام را رد می‌کند تا از عمل کردن بر پایه‌ی یک دیدگاه قدیمی جلوگیری شود. ⚠️

برای نویسندگان informer، client-go یک ساختار به اسم ConsistencyStore فراهم کرده تا به‌راحت‌تر از امکانات استفاده کنند. پیاده‌سازی نمونه در ReplicaSet informer نشان می‌دهد چطور از این ابزار برای جلوگیری از reconciliation روی اطلاعات stale بهره بگیریم. رابط اصلی به شکل زیر است:

type ConsistencyStore interface {
    // WroteAt records that the given object was written at the given resource version.
    WroteAt(owningObj runtime.Object, uid types.UID, groupResource schema.GroupResource, resourceVersion string)
    // EnsureReady returns true if the cache is up to date for the given object.
    EnsureReady(namespacedName types.NamespacedName) bool
    // Clear removes the given object from the consistency store.
    Clear(namespacedName types.NamespacedName, uid types.UID)
}

توضیح توابع:

– WroteAt: زمانی که controller روی API server چیزی می‌نویسد باید فراخوانی شود تا آخرین resource version نوشته‌شده ثبت شود؛ owningObj شی‌ای است که controller روی آن reconciliation انجام می‌دهد و uid برای تشخیص نمونه‌ها استفاده می‌شود. 📝

– EnsureReady: قبل از reconciliation این تابع چک می‌شود؛ اگر cache برای آن شی به‌روز باشد true برمی‌گرداند و در غیر این صورت false. این تابع از داده‌های ثبت‌شده توسط WroteAt استفاده می‌کند.

– Clear: وقتی شی حذف می‌شود این تابع برای پاک‌سازی ورودی مربوطه فراخوانی می‌شود تا اندازه‌ی ConsistencyStore رشد نامحدود نکند. uid کمک می‌کند بین اشیایی که با همان نام دوباره ساخته شده‌اند تمایز قائل شویم.

علاوه بر کاهش staleness، v1.36 متریک‌های مربوطه را هم به kube-controller-manager اضافه کرده تا Observability بهتری داشته باشیم. متریک آلفای جدید stale_sync_skips_total تعداد دفعاتی را نشان می‌دهد که controller به‌خاطر cache stale از sync صرف‌نظر کرده است و به ازای هر controller قابل مشاهده است. 🧭

همچنین client-go متریکی با نام store_resource_version دارد که آخرین resource version هر shared informer را منتشر می‌کند (با labelهای Group, Version, Resource). ترکیب این متریک‌ها با stale_sync_skips_total به شما امکان می‌دهد راحت‌تر تشخیص دهید که آیا cache یک controller عقب است یا خیر—مثلاً با مقایسه این مقادیر با resource version فعلی API server.

گام بعدی: SIG API Machinery قصد دارد این قابلیت را برای کنترلرهای بیشتری توسعه دهد و بازخورد جامعه را می‌پذیرد. همچنین در همکاری با controller-runtime تلاش می‌شود این semantics (مثل read-your-own-writes) به‌صورت عمومی در اختیار هر controller ای قرار گیرد تا توسعه‌دهندگان controllerها مجبور به پیاده‌سازی دستی آن نباشند. اگر روی controller-runtime کار می‌کنید این خبر به معنی کاهش کار اضافی و رفتار قابل پیش‌بینی‌تر خواهد بود. 💡

خلاصه: v1.36 ابزارهای مفیدی برای کاهش مشکلات ناشی از cache stale و برای مشاهده بهتر رفتار controllerها اضافه کرده است. این تغییرات به‌ویژه در سناریوهای با contention بالا یا هنگام restart/قطعی‌ها اهمیت دارند و به تیم‌های SRE/DevOps کمک می‌کنند رفتار controllers را با اطمینان بیشتری مدیریت کنند. 👍