StatefulSets
StatefulSets
معظم أعباء العمل في Kubernetes عديمة الحالة: أي Pod مطابق لأي Pod آخر، ويمكن إيقاف أو استبدال الـ Pods بأي ترتيب. قواعد البيانات وسماسرة الرسائل وذاكرات التخزين المؤقت الموزعة لا تستطيع العمل بهذه الطريقة. يمتلك وسيط Kafka هوية ثابتة يستخدمها الوسطاء الآخرون والعملاء في إعداداتهم. تمتلك عقدة Cassandra جزءاً من البيانات يجب أن يتبعها عبر عمليات إعادة التشغيل. تشكل عقد Elasticsearch مجموعة بالاسم ويجب أن تعيد الانضمام بنفس معرف العقدة بعد الترقية التدريجية. وُجدت StatefulSets لمنح الـ Pods هوية ثابتة ودائمة — اسم مضيف يمكن التنبؤ به، وتسلسل بدء وإيقاف منظم، ومطالبة PersistentVolumeClaim مخصصة تبقى على قيد الحياة عبر إعادة جدولة Pod.
ما الذي يجعل StatefulSet مختلفاً
يتعامل Deployment مع جميع Pods الخاصة به على أنها قطعان قابلة للاستبدال. يتعامل StatefulSet مع كل Pod باعتباره فرداً محدداً ومرتباً. الضمانات التي يوفرها هي:
- هوية شبكة ثابتة: يحصل كل Pod على اسم DNS بصيغة
<pod-name>.<headless-service>.<namespace>.svc.cluster.local— على سبيل المثالpostgres-0.postgres-headless.production.svc.cluster.local. يظل اسم المضيف هذا ثابتاً عبر عمليات إعادة التشغيل وإعادة الجدولة إلى عقد مختلفة. - تخزين ثابت: لكل Pod مطالبة PersistentVolumeClaim خاصة به تُنشأ من كتلة
volumeClaimTemplates. يُسمى المطالبة<volume-name>-<pod-name>(مثلdata-postgres-0). عند إعادة جدولة Podpostgres-0، يعيد الاتصال بنفس PVC — وبالتالي بنفس البيانات الأساسية. - طرح منظم ومتحكم فيه: يتم إنشاء وحذف الـ Pods بترتيب حتمي (0، 1، 2 … للإنشاء؛ عكسي للحذف). يجب أن يكون Pod في حالة Running وReady قبل بدء الترتيب التالي.
clusterIP: None) بشكل منفصل. هذه الخدمة هي ما يسجل سجلات DNS لكل Pod في DNS الخاصة بالمجموعة. بدونها، لا تعمل ضمانة اسم المضيف الثابت.تشريح مانيفست StatefulSet
فيما يلي StatefulSet لـ PostgreSQL واقعي للإنتاج. لاحظ ترابط serviceName وتعريف Headless Service وvolumeClaimTemplates:
الهوية الثابتة من الداخل
عند تطبيق هذا المانيفست، ينشئ Kubernetes Pods باسم postgres-0 وpostgres-1 وpostgres-2 — بهذا الترتيب، منتظراً أن يكون كل منها Ready قبل المتابعة. كما ينشئ تلقائياً PVCs باسم data-postgres-0 وdata-postgres-1 وdata-postgres-2. يسجل DNS الخاص بالمجموعة بعد ذلك سجلات A بحيث يتحلل postgres-0.postgres-headless.production.svc.cluster.local دائماً إلى IP الخاص بـ Pod المسمى postgres-0 حالياً، بصرف النظر عن العقدة الفيزيائية التي يعمل عليها.
التحديثات التدريجية وحقل Partition
تتقدم تحديثات StatefulSet التدريجية بترتيب رقمي عكسي (من الفهرس الأعلى أولاً). حقل partition في updateStrategy.rollingUpdate هو أحد أكثر عناصر التحكم في الإنتاج فائدة — وأقلها استخداماً. يعني ضبط partition: 2 على مجموعة مكونة من ثلاث نسخ أن Pod postgres-2 فقط يُحدَّث عند تغيير قالب Pod. تستمر Pods postgres-0 وpostgres-1 في تشغيل المواصفات القديمة. هذه آلية كاناري مدمجة لأعباء العمل ذات الحالة: تحقق من الإصدار الجديد على النسخة الأقل تأثيراً قبل تطبيقه على الـ Primary.
دورة حياة PVC وسياسة الاحتفاظ
بشكل افتراضي، لا يتم حذف PVCs التي أنشأها StatefulSet عند تقليص StatefulSet أو حذفه. هذا مقصود: لا تريد أن يمسح kubectl delete statefulset بيانات قاعدة البيانات. منذ Kubernetes 1.27 يتيح حقل persistentVolumeClaimRetentionPolicy التحكم في هذا بشكل صريح:
whenDeleted: Delete أبداً على قواعد البيانات في الإنتاج إلا إذا كانت لديك نسخ احتياطية مُتحققة ومختبرة. سيدمر kubectl delete statefulset --cascade=foreground مع تلك السياسة كل PVC. الافتراضي الآمن هو Retain على كلا المحورين — تعيش PVCs بعد StatefulSet ويجب تنظيفها يدوياً.أنماط الفشل في الإنتاج
فهم أوجه فشل StatefulSets لا يقل أهمية عن معرفة كيفية تكوينها:
- انقسام الدماغ بعد فشل العقدة: إذا أصبحت عقدة غير قابلة للوصول (تقسيم شبكة وليس عطلاً)، يُعلَّم خادم API على Pods الخاصة بها بـ
Unknownلكنه لا يُنهيها قسراً. لن ينشئ متحكم StatefulSet Pod بديلاً للترتيب N طالما قد يكون Pod بالاسم نفسه لا يزال يعمل — يُفضل خطأ عدم وجود نسخة مكررة على خطر وجود podين يكتبان في نفس PVC. الإصلاح في الإنتاج: ضبط تسامح قصير لـnode.kubernetes.io/unreachableعلى مواصفة Pod، أو استخدام Pod disruption budget مع سير عمل استنزاف عقدة من cluster autoscaler. - استنزاف سعة PVC: على عكس Deployments، لا يستطيع Pod في StatefulSet الانتقال إلى PVC مختلف إذا امتلأ تخزينه. ضع دائماً تنبيهات التخزين عند 75% من الطاقة واستخدم
allowVolumeExpansion: trueعلى StorageClass حتى تتمكن من تغيير الحجم دون توقف. - ترتيب Init Container لتشغيل المجموعة لأول مرة: تحتاج التطبيقات ذات الحالة مثل Cassandra أو Kafka إلى معرفة ما إذا كانت العقدة الأولى في مجموعة جديدة أم عقدة استبدال تنضم إلى مجموعة موجودة. استخدم init container يستعلم عن الـ headless service — إذا أعاد تحليل DNS صفر سجلات، فهي مجموعة جديدة؛ وإذا أعاد IPs موجودة، فهي عملية انضمام.
role: primary تُضاف إلى Pod الرئيسي عند البدء لجعل اختيار Service أمراً بسيطاً.التوسع والحذف
قم بالتوسع بـ kubectl scale statefulset postgres --replicas=5 -n production. تُضاف Pods الجديدة بترتيب تصاعدي (3، 4) ويجب أن يكون كل منها Ready قبل إنشاء التالي. للتقليص، شغّل نفس الأمر بعدد أقل — تُنهى Pods بترتيب تنازلي (4، 3). تبقى PVCs الخاصة بها ما لم تقل سياسة الاحتفاظ خلاف ذلك. احرص دائماً على استنزاف نسخة ذات حالة بأمان (تسليم العمليات الجارية، مسح WAL، إطلاق عضوية المجموعة) بالتأكد من وجود خطاف preStop مناسب أو بتكوين التطبيق للتعامل مع SIGTERM بتسلسل إيقاف نظيف ضمن terminationGracePeriodSeconds.