به گزارش از وبسایت cncf 😊

در گذشته، ارتقاء کنترل‌پلن Kubernetes اغلب به معنای شب‌زنده‌داری، ورود SSH به تک‌تک گره‌ها و اجرای دستی مراحل بود. این‌بار من خواستم ببینم خط لوله‌ای که ساخته‌ام آیا واقعاً آن فرآیند را خودکار، ساده و قابل‌اعتماد می‌کند یا خیر.

ریشه‌های پلتفرم این خوشهٔ mgmt جایی بود که همه‌چیز آغاز شد: بوت‌استرپ با OpenTofu شامل سه گرهٔ کنترل‌پلن، K3s HA و Cilium CNI که همه به‌عنوان infrastructure as code تعریف شده بودند. این را به‌عنوان ریشه‌ای در نظر گرفتم که قرار است با گذر زمان ابزارها و بارکاری‌های بیشتری روی آن رشد کنند و خوشه‌های بیشتری از آن منشعب شوند؛ بنابراین قبل از ساخت موارد بالادستی، باید این ریشه‌ها محکم باشند.

در سال 2026 و با سرعت بالای انتشار CVEها در سراسر پشته، چیزی که بیشتر از افزودن ویژگی می‌خواستم، یک فرآیند ارتقاء واقعاً ساده و قابل‌اعتماد بود که لازم نباشد هر بار مراحل را دوباره یاد بگیرم. هدف من استفاده حداکثری از ابزارهای Cloud Native Computing Foundation (CNCF) برای جلوگیری از vendor lock-in و توسعهٔ تجربهٔ Golden Kubestronaut بود. 🚀

تنظیم اولیه: سه گرهٔ کنترل‌پلن با k3s HA که روی Kairos Hadron اجرا می‌شدند. Kairos یک توزیع immutable OS است که با ارتقاء مبتنی بر A/B partition و تصاویر امضا شده با cosign کار می‌کند: تصویر جدید روی پارتیشن غیرفعال نوشته می‌شود و سپس reboot انجام می‌گیرد. rollback ساده است: بوت مجدد روی پارتیشن قدیمی. این طراحی زنجیرهٔ تأمین محکمی ایجاد می‌کند، اما تنها زمانی اهمیت دارد که خودِ ارتقاء به‌درستی انجام شود.

نسخهٔ قبلی خط لوله یک اشکال ظریف اما خطرناک داشت: مقدار concurrency: 0 در مشخصات ارتقاء، که من آن را به‌معنای «یک گره در هر نوبت» فرض کرده بودم، درحالی‌که در عمل به‌معنای «همهٔ گره‌ها هم‌زمان» بود. در آزمایش، هر سه گرهٔ کنترل‌پلن هم‌زمان reboot شدند؛ نصاب etcd زنده ماند، اما این شانسی بیش نبود. اصلاح سریع: concurrency: 1 شد که ستون فقرات فرایند را پایدار کرد.

ابزارهای خط لوله (شش ابزار که هر کدام نقش مشخصی دارند):

– Gitea: مخزن Git میزبان که روی خوشهٔ Kairos + k3s خودش اجرا می‌شود و یک runner برای Gitea Actions برای اجرای CI دارد. اینجا جایی است که هر مشخصه، خط‌مشی و ارتقاء نگهداری می‌شود.
– Renovate: چک می‌کند quay.io/kairos/hadron برچسب جدید دارد و وقتی upstream نسخه‌ای منتشر می‌کند، یک PR باز می‌کند که به‌طور معمول دو خط را بامپ می‌کند: tag تصویر و metadata.name در ارتقاء CR.
– Kyverno: یک ClusterPolicy Admission gateway که هر ارتقاء CR را که تصویر آن با quay.io/kairos/hadron:* همخوانی نداشته باشد رد می‌کند تا پیکربندی نادرست یا typosquatting قبل از رسیدن به گره‌ها مسدود شود.
– Cosign: امضای تصویر را در برابر هویت OIDC GitHub Actions بررسی می‌کند — نه فقط «آیا برچسب درست است»، بلکه «آیا واقعاً این تصویر توسط همان خط لولهٔ CI ساخته شده است».
– ArgoCD: GitOps که PR ادغام‌شده را به‌عنوان trigger می‌بیند و manifest جدید را اعمال می‌کند — هیچ kubectl انسانی اجرا نمی‌شود.
– kairos-operator: عامل واقعی ارتقاء. منابع NodeOpUpgrade را نظارت می‌کند، یک گره را cordon/upgrade می‌کند، تصویر را می‌کشد، پارتیشن A/B را می‌نویسد، reboot می‌کند، صبر می‌کند تا گره دوباره join شود و سپس به گرهٔ بعدی می‌رود.

واقعی‌ترین اتفاق: مهرهای زمانی ارتقاء در 7 ژوئیه به Hadron v0.4.0 به‌صورت زیر ثبت شد:

11:40:20 پایه – 3 گره آماده، هسته 7.0.10-hadron
11:42:00 PR #9 ادغام شد (Renovate bump, 2-line diff, 2-CD) OutOfSync
11:42:37 ArgoCD Synced — CR قدیمی حذف شد، CR جدید ایجاد شد
11:42:43 پیش‌پرواز: کشیدن تصویر شروع می‌شود (324 مگابایت)
11:43:09 تصویر در 25.5s
11:43:11 کشیده شد — mgmtt:04:00:00:00:00:00. شکاف A/B نوشته شد، راه‌اندازی مجدد آغاز شد
~11:47 mgmt-cp-0 آماده – هسته 7.1.0-hadron تأیید شد
~11:47 mgmt-cp-1 محاصره، ارتقاء یافت، راه‌اندازی مجدد
~11:49 mgmt-cp-1 آماده – هسته 7.1.0-hadron
~11:49 mgmt-cp-2 محاصره شده، ارتقا یافته، راه‌اندازی مجدد
11:51:36 mgmt-cp-2 آماده — هسته 7.1.0-hadron. همه‌چیز تمام شد.
زمان کل دیواری: 11 دقیقه. مداخلهٔ انسانی پس از ادغام: صفر. نصاب etcd شکست نخورد. اختلال در بارکاری: هیچ.

تفاوت دو خطی که همه‌چیز را به حرکت درآورد:

--- a/upgrades/mgmt/hadron-upgrade.yaml
+++ b/upgrades/mgmt/hadron-upgrade.yaml
@@ -11,7 +11,7 @@
 فراداده:
 -  نام: hadron-mgmt-v0-3-0-0
 مشخصات:
 -  تصویر: quay.io/kairos/hadron:v0.3.0-standard-amd64-generic-v4.1.1-k3s-v1.35.5-k3s1
+  تصویر: quay.io/kairos/hadron:v0.4.0-standard-amd64-v2.3.5-1.3.5-1.3.5-1.3.5

چرا باید نام تغییر کند؟ NodeOpUpgrade یک Custom Resource است که kairos-operator آن را به‌عنوان «تمام‌شده» تگ می‌زند و سپس دیگر آن را دوباره پردازش نمی‌کند. تغییر صرف spec.image در یک CR موجود کاری از پیش نمی‌برد چون اپراتور آن شیء را تمام‌شده فرض می‌کند. Renovate باید metadata.name را در همان commit تغییر دهد تا ArgoCD CR قدیمی را حذف و یک CR کاملاً جدید ایجاد کند — آن محرک واقعی است.

یک اشکال دیگر که انتظارش را نداشتم: regex manager سفارشی Renovate از فیلدی به نام extractVersionTemplate برای تبدیل نسخهٔ با dash-style (مثل v0-3-0) به SemVer قابلِ مقایسه استفاده می‌کرد، اما این فیلد در تنظیمات سفارشی وجود نداشت و Renovate بی‌صدا کاری انجام نداد. نتیجه این شد که spec.image به‌درستی بامپ شد اما metadata.name بدون تغییر ماند؛ kairos-operator CR جدیدی ندید و به‌همین‌خاطر کاری انجام نداد. خط لوله به‌نظر می‌رسید شکست خورده، درحالی‌که در واقع فقط نیمه‌کاره اجرا شده بود.

رفع مشکل با تغییر به currentValueTemplate انجام شد، که به‌درستی v0-3-0 را به v0.3.0 تبدیل می‌کند تا مقایسهٔ نسخهٔ Renovate درست کار کند و سپس قالب dash-style برای تگ جدید ساخته شود. دقیقاً به‌خاطر همین است که مرحلهٔ بازبینی انسانی هنوز در این خط لوله وجود دارد — نه برای اجرا کردن ارتقاء (اتوماسیون این کار را انجام می‌دهد)، بلکه برای یافتن جاهایی که اتوماسیون به‌صورت خاموش‌و‌ساکت اشتباه انجام می‌دهد در حالی‌که ظاهراً درست به‌نظر می‌رسد.

حالا این خوشهٔ درگاه — یک deployment تک‌گرهٔ Kairos که Netbird را اجرا می‌کند — نیز به همان حلقهٔ ArgoCD GitOps متصل شده است: همان خط لوله، همان مسیر ارتقاء خودکار و بدون فرایند جداگانهٔ نگهداری. گام بعدی، اضافه کردن یک مرحلهٔ dry-run در CI است که به‌روزرسانی kairos-agent را شبیه‌سازی کند تا بازگشت از تصویر جدید قبل از ادغام PR تست شود و شکست‌ها قبل از لمس شدن گرهٔ زنده مشخص شوند.

پایان: آیا سیستم‌عامل immutable ای که بتواند در مقیاس کار کند را می‌خواهید؟ Kairos چنین سیستمی است. 😄