فئات التخزين والتوفير الديناميكي
فئات التخزين والتوفير الديناميكي
في الدرس السابق تعلمت أن PersistentVolumes وPersistentVolumeClaims تفصل تعريفات الـ pod عن التخزين المادي الكامن تحتها. لكن التوفير المسبق اليدوي لـ PV لكل فريق تطوير يطلب تخزينًا غير مستدام تشغيليًا على نطاق واسع — إذ يستلزم تدخلًا بشريًا لكل قاعدة بيانات أو cache جديدة. التوفير الديناميكي يحل هذه المشكلة: يرسل المطور PersistentVolumeClaim يصف الحجم والخصائص المطلوبة، فيُنشئ المُوفّر (provisioner) — وهو controller يعمل داخل الكلاستر — موارد التخزين الحقيقية تلقائيًا، ويربطها بـ PV جديد، ويعيد حجمًا جاهزًا للتركيب. وكائن API الذي يحكم هذا السلوك هو StorageClass.
تشريح فئة التخزين StorageClass
فئة التخزين هي مورد على مستوى الكلاستر (لا تنتمي إلى namespace) تُحدد ثلاثة أشياء: المُوفّر (provisioner) (أي driver يُنشئ الحجم)، والمعاملات (parameters) (تهيئة خاصة بالـ driver مثل نوع القرص، ومستوى IOPS، ومفتاح التشفير)، وسياسة الاسترداد (reclaimPolicy) (ما يحدث لموارد التخزين الأساسية عند حذف PVC المالكة لها).
storageClassName تحصل على الافتراضي في الكلاستر. على EKS، الافتراضي الجاهز هو StorageClass قديمة من نوع gp2 — سابقة لـ gp3 وأكثر تكلفة بالغيغابايت مقابل أداء أساسي أسوأ. من أولى تعديلات البنية التحتية في أي مشروع EKS تعيين StorageClass من نوع gp3 كافتراضية وإلغاء الافتراضية عن gp2. إهمال هذا يؤدي إلى إنفاق كل فريق بصمت على أقراص gp2.المُوفّرون: In-Tree مقابل CSI
تاريخيًا شحنت Kubernetes مشغّلات الأحجام مدمجةً في الـ controller-manager binary (تسمى موفري "in-tree"، مثل kubernetes.io/aws-ebs). هذه مهجورة الآن وأُزيلت من إصدارات Kubernetes الحديثة. البديل الحديث هو Container Storage Interface (CSI) — مواصفة gRPC محايدة بين البائعين تتيح لموردي التخزين شحن مشغّلاتهم كأحمال عمل عادية في Kubernetes (Deployments وDaemonSets)، منفصلة تمامًا عن دورة إصدار Kubernetes. كل كلاستر إنتاجي اليوم يجب أن يستخدم مشغّلات CSI حصرًا.
أبرز موفري CSI الشائعة:
ebs.csi.aws.com— AWS EBS (تخزين كتلي)efs.csi.aws.com— AWS EFS (ملفات،ReadWriteMany)disk.csi.azure.com— Azure Managed Diskspd.csi.storage.gke.io— GCP Persistent Diskrbd.csi.ceph.com— Ceph RBD (مُدار ذاتيًا)driver.longhorn.io— Longhorn (مُدار ذاتيًا، تخزين كتلي مُتكرر)
volumeBindingMode: فخ مناطق التوافر المتعددة
هذا الحقل هو المصدر الأكثر شيوعًا للحوادث الإنتاجية المتعلقة بالتخزين في الكلاسترات السحابية. له قيمتان:
- Immediate — يُنشئ الموفر PV ويربطه فور إنشاء PVC. يحدث هذا قبل جدولة أي pod، لذا يُنشأ الحجم في منطقة توافر عشوائية. عندما يحاول المجدول بعد ذلك وضع الـ pod، قد يختار عقدة في منطقة توافر مختلفة حيث لا يمكن الوصول إلى حجم EBS — يظل الـ pod في حالة
Pendingإلى الأبد مع خطأ volume node affinity conflict. - WaitForFirstConsumer — يُؤجّل إنشاء PV حتى يتم جدولة pod يرجع إلى PVC. يختار المجدول العقدة أولًا، ثم يُنشئ الموفر الحجم في نفس منطقة توافر تلك العقدة. هذا هو الوضع الصحيح الوحيد للتخزين الكتلي المُدرك للمناطق في الكلاسترات متعددة مناطق التوافر.
سياسات الاسترداد: ما يحدث عند حذف PVC
تتحكم reclaimPolicy في StorageClass بدورة حياة موارد التخزين الأساسية بعد حذف PVC التي ترتبط بها. هناك ثلاث قيم، لكن القيمتين العمليتين فقط هما:
- Delete (الافتراضي لمعظم StorageClasses السحابية) — يحذف مشغّل CSI الموارد الأساسية (مثلًا: يُنهي حجم EBS) فور تحرير PV. هذا فعّال لكنه خطير: حذف PVC في namespace خاطئة سيُدمر بيانات الإنتاج بصورة لا رجعة فيها. اقرن دائمًا StorageClasses بسياسة Delete مع حماية حذف PVC (مثل سياسات النسخ الاحتياطي عبر Velero أو webhook للتحقق يمنع حذف PVC في namespaces الحساسة).
- Retain — ينتقل PV إلى مرحلة
Releasedوتُحفظ موارد التخزين الأساسية. يجب على المدير حذف كائن PV يدويًا وتنظيف الموارد الأساسية اختياريًا. هذه هي السياسة الصحيحة لأي طبقة تحمل بيانات لا يمكن تحمّل خسارتها — قواعد البيانات، ومخازن الـ blobs، وسجلات التدقيق. يمكن إعادة إرفاق البيانات المستردة بإنشاء PV جديد يشير إلى نفس الموارد الأساسية وPVC جديد معvolumeNameمطابق.
kubectl patch pv <pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'. افعل هذا قبل حذف PVC.توسيع الحجم: تكبير PVC بدون توقف
عندما يمتلئ حجم ما، الاستجابة الصحيحة ليست توفير حجم جديد ونقل البيانات — بل توسيع PVC الحالية في مكانها. لكي يعمل هذا، يجب أن تتحقق ثلاثة شروط: يجب أن تمتلك StorageClass الخاصية allowVolumeExpansion: true، ويجب أن ينفّذ مشغّل CSI استدعاءات RPC الخاصة بـ ControllerExpandVolume وNodeExpandVolume، وللأحجام ذات نظام الملفات يجب أن يكتمل التوسع على جانب العقدة أثناء تركيب الحجم.
تدعم مشغّلات CSI الحديثة لجميع مزودي السحابة الكبار التوسيع أثناء التشغيل — يُعاد تحديد حجم EBS عبر AWS API ويُوسَّع نظام الملفات (ext4 أو XFS) دون فصل التركيب. للمشغّلات القديمة أو المُدارة ذاتيًا التي تدعم التوسع في وضع عدم الاتصال فقط، يجب حذف الـ pod أولًا، والسماح بفصل PV، ثم تعديل PVC وإعادة تشغيل الـ pod.
kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.80 في Prometheus لتتوسع قبل امتلاء القرص، لا بعد انهيار التطبيق.الخلاصة: تصميم StorageClass على نطاق الإنتاج
يمتلك الكلاستر الناضج عادةً ثلاث إلى خمس StorageClasses تُرسم على طبقات متمايزة من حيث التكلفة والأداء. تصميم تمثيلي لكلاستر AWS EKS قد يبدو هكذا: StorageClass افتراضية ebs-gp3 لأحمال العمل العامة، وفئة ebs-io2 لقواعد البيانات التي تتطلب IOPS متوقعة، وفئة efs-shared لأحمال العمل read-write-many مثل بيانات تدريب ML أو الإعدادات المشتركة، وفئة local-nvme (باستخدام local-path أو TopoLVM CSI) لأحمال العمل المؤقتة الحساسة لزمن الاستجابة التي تتحمل فقدان البيانات عند فشل العقدة.
كل StorageClass تتعامل مع بيانات الإنتاج يجب أن تمتلك reclaimPolicy: Retain، وallowVolumeExpansion: true، وvolumeBindingMode: WaitForFirstConsumer. هذه الإعدادات الثلاثة معًا تمنع تعارضات مناطق التوافر، وفقدان البيانات العرضي، والمعاناة التشغيلية من إعادة توفير الأحجام لزيادة السعة.
VolumeSnapshotClass (مشابهة لـ StorageClass) وأخذ لقطات متسقة مع الأعطال لـ PVCs عبر kubectl apply -f volumesnapshot.yaml. اللقطات هي أساس خطوط النسخ الاحتياطي والاسترداد لأحمال العمل ذات الحالة، وشرط أساسي للاختبار الآمن لعمليات ترحيل الأحجام أو تغييرات المخطط في كلاسترات الإنتاج.