عمليات Kubernetes المتقدمة

الموارد المخصصة والمشغّلون

22 دقيقة الدرس 3 من 30

الموارد المخصصة والمشغّلون

يأتي Kubernetes مزوّداً بأنواع موارد مدمجة — Deployments وServices وConfigMaps — لكن قوّته الحقيقية تكمن في قابلية التوسّع. تتيح لك تعريفات الموارد المخصصة (CRDs) تعليم Kubernetes أنواعاً خاصة بنطاقك: Database أو TLSCertificate أو KafkaTopic. أما نمط المشغّل (Operator) فيُغلّف CRD بحلقة تحكّم تتصرف بناءً على حالة هذا المورد، وتُرمجّ المعرفة التشغيلية التي كانت ستعيش في دليل تشغيل يدوي. هكذا تُقدّم Datadog وConfluent وMongoDB وغيرها من شركات البنية التحتية للبيانات منتجاتها المبنية على Kubernetes بشكل أصيل.

ما هو تعريف المورد المخصص (CRD)؟

CRD هو بحد ذاته كائن Kubernetes API — مُخزَّن في etcd، تُديره الـ API Server — يُسجّل نقطة وصول REST جديدة ضمن group/version/kind محدد. بمجرد تطبيق CRD، يمكنك إنشاء موارد مخصصة (CRs) — أي نسخ من هذا النوع — باستخدام kubectl apply -f تماماً مثل Pods وDeployments، وتتولى الـ API Server التحقق من صحتها وفق مخطط OpenAPI v3 الذي أعلنته.

دورة الحياة بسيطة: ثبّت الـ CRD مرة واحدة (عادةً عبر Helm chart أو مانيفست تثبيت operator)، ثم أنشئ موارد مخصصة — الـ CRD يُعرّف الشكل، والـ CR يُمثّل البيانات.

# CRD بسيط يُعرّف نوع "Database" في المجموعة dba.example.com apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: databases.dba.example.com # يجب أن يكون <plural>.<group> spec: group: dba.example.com scope: Namespaced # أو Cluster names: plural: databases singular: database kind: Database shortNames: [db] versions: - name: v1alpha1 served: true storage: true # نسخة واحدة فقط storage=true schema: openAPIV3Schema: type: object properties: spec: type: object required: [engine, storage] properties: engine: type: string enum: [postgres, mysql, mariadb] storage: type: string replicas: type: integer minimum: 1 default: 1 status: type: object properties: phase: type: string endpoint: type: string subresources: status: {} additionalPrinterColumns: - name: Engine type: string jsonPath: .spec.engine - name: Phase type: string jsonPath: .status.phase - name: Age type: date jsonPath: .metadata.creationTimestamp
عرّف دائماً مخطط OpenAPI v3. بدونه تقبل الـ API Server أي بيانات عشوائية في spec. يمنحك المخطط التحقق من الصحة من طرف الخادم، والإكمال التلقائي في إضافات بيئات التطوير، والحماية من الأخطاء الإملائية في الإنتاج.

بعد تطبيق الـ CRD، إنشاء CR مطابق تماماً لأي كائن Kubernetes آخر:

# نسخة مورد مخصص — كائن "Database" apiVersion: dba.example.com/v1alpha1 kind: Database metadata: name: orders-db namespace: commerce spec: engine: postgres storage: 100Gi replicas: 3 --- # استعراض المورد مثل أي مورد أصيل kubectl get db -n commerce kubectl describe db orders-db -n commerce kubectl get db orders-db -n commerce -o jsonpath='{.status.phase}'

نمط المشغّل (Operator)

الـ CRD وحده خامل — تُخزّنه الـ API Server، لكن لا شيء يتصرف بناءً عليه. المشغّل (Operator) هو متحكّم يراقب الـ CRs ويدفع الكتلة نحو الحالة المطلوبة المُعلنة فيها. هذه هي نفس حلقة المصالحة التي تُشغّل المتحكمات المدمجة (متحكم Deployment، متحكم StatefulSet)، لكنها مكتوبة بواسطتك أو بائع لتُرمجّ معرفة النطاق.

Operator reconciliation loop Engineer kubectl apply API Server validates schema stores CR in etcd emits Watch event Watch Operator Observe (Get CR) Diff desired vs actual Act (create StatefulSet, Service, Secrets…) Update status Create/Patch Cluster Resources StatefulSet, Service Secret, PVC, etc. Reconcile on drift
حلقة مصالحة المشغّل: يراقب CRs، يحسب الفارق، يتصرف على الكتلة، يُحدّث الحالة — بشكل مستمر.

المصالحة: لاحظ، قارن، تصرّف

كل مشغّل يُنفّذ دالة Reconcile تستقبل طلباً (زوج namespace/name). يجب أن تكون الدالة idempotent — قد تُستدعى آلاف المرات. التدفق القياسي:

  1. احضر الـ CR من الـ API Server.
  2. احسب ما يجب أن تبدو عليه الكتلة بناءً على spec.
  3. قارن مع ما هو موجود فعلياً (الموارد المملوكة).
  4. تصرّف — أنشئ، حدّث، أو احذف الموارد المملوكة.
  5. حدّث status على الـ CR ليعكس الحالة الفعلية.

أُطر عمل مثل controller-runtime (Go، تستخدمها kubebuilder وOperator SDK) تتولى بنية الـ watch وقوائم العمل وانتخاب القائد. أنت تكتب الخطوات 1-5 فقط.

استخدم مراجع المالك للحذف التتالي. عندما يُنشئ المشغّل موارد فرعية (StatefulSets وServices وSecrets)، اضبط الـ CR كـ ownerReference. عند حذف الـ CR، يُتابع Kubernetes الحذف تلقائياً إلى جميع الموارد المملوكة — لا تحتاج إلى منطق finalizer مخصص في معظم الحالات.

بناء مشغّل: البداية السريعة مع Kubebuilder

Kubebuilder هو أداة التهيئة القياسية المبنية على Go، تُديرها مجموعة Kubernetes SIG API Machinery. تُولّد مانيفست CRD، وهيكل المتحكم، وتهيئة الـ webhook، وعلامات RBAC من سير عمل CLI واحد.

# تثبيت kubebuilder CLI (يتطلب Go 1.21+) curl -L -o kubebuilder \ "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)" chmod +x kubebuilder && mv kubebuilder /usr/local/bin/ # إطلاق مشروع operator جديد mkdir database-operator && cd database-operator kubebuilder init --domain dba.example.com --repo github.com/acme/database-operator # هيكلة API (يُولّد CRD + controller) kubebuilder create api \ --group dba \ --version v1alpha1 \ --kind Database \ --resource \ --controller # توليد مانيفستات CRD من تعليقات Go التوضيحية make manifests # تشغيل المتحكم محلياً مقابل كتلة حقيقية make run # النشر على الكتلة make docker-build docker-push IMG=ghcr.io/acme/database-operator:v0.1.0 make deploy IMG=ghcr.io/acme/database-operator:v0.1.0
Operator SDK مقابل Kubebuilder مقابل المشغّلات المبنية على Helm. Operator SDK يبني على kubebuilder (نفس النتيجة، أدوات إضافية). المشغّلات المبنية على Helm تُغلّف Helm chart — سريعة البناء لكن لا تستطيع التعبير عن منطق مصالحة معقّد. مشغّلات Python/Kopf سهلة للنماذج الأولية لكن نادراً ما تصل إلى الإنتاج في نطاق واسع. لأي شيء ذي حالة أو تشغيل معقّد، استخدم Go + kubebuilder.

متى تبني مشغّلاً (ومتى لا تفعل)

تُضيف المشغّلات تكلفة تشغيلية حقيقية: أنت تملك الكود، وإصدار CRD وتحويله، ومسار الترقية. إطار القرار المستخدم في شركات التقنية الكبرى:

  • ابنِ مشغّلاً عندما تمتلك مكوّناً ذا حالة مع منطق دورة حياة معقّد (failover، نسخ احتياطي، ترحيل مخطط، إعادة تشغيل متدرجة) لا يمكن التعبير عنه بـ Helm chart أو كائنات Kubernetes القياسية وحدها.
  • استخدم مشغّلاً موجوداً لقواعد البيانات المعروفة وقوائم الرسائل وأكوام المراقبة — Prometheus Operator وcert-manager وCloudNativePG وStrimzi (Kafka) وKEDA تغطي الغالبية العظمى من حالات الاستخدام.
  • لا تبنِ مشغّلاً للأحمال عديمة الحالة. Deployment + HPA + ConfigMap كافٍ في الغالب. ميزانية التعقيد حقيقية.
إصدار CRD دائم. بمجرد أن ينشر المستخدمون CRs بإصدار v1alpha1، لا يمكنك إزالة هذا الإصدار بدون webhook للتحويل ونافذة ترحيل. خطّط لسطح API بعناية قبل GA — اعمل عليه كما تعمل على REST API عام.

الـ Status Subresource والـ Conditions

الـ status subresource (مُفعَّل عبر subresources: status: {} في الـ CRD) يجعل status نقطة API منفصلة — الـ kubectl patch على /status لا يتطلب RBAC على المورد الرئيسي، وتحديثات spec لا تُدمّر الـ status بالخطأ. دائماً نمذج الـ status باستخدام نمط Conditions (مصفوفة من إدخالات type/status/reason/message/lastTransitionTime) — يتوافق هذا مع ما يتوقعه kubectl wait --for=condition=Ready وما يستعلم عنه لوحات SRE.

# انتظر حتى يصبح مورد يُديره المشغّل جاهزاً kubectl wait db/orders-db \ --for=condition=Ready \ --timeout=300s \ -n commerce # تحقق من الـ conditions مباشرة kubectl get db orders-db -n commerce \ -o jsonpath='{.status.conditions[*].type}{"\n"}{.status.conditions[*].status}' # استعراض سجلات المتحكم kubectl logs -n database-operator-system \ -l control-plane=controller-manager \ --tail=100 -f

إتقان CRDs ونمط المشغّل يفتح لك قابلية التوسّع الكاملة لـ Kubernetes. كل مشروع cloud-native رئيسي — cert-manager وArgo CD وIstio وTekton وKEDA — هو في جوهره مشغّل. فهم كيفية عمله داخلياً يجعلك أكثر فعالية عند تشخيص الأخطاء في الإنتاج، ويؤهّلك لبناء مشغّلك الخاص عندما تستدعي الحاجة ذلك حقاً.