أساسيات Kubernetes للمبتدئين

إذا كنت تعمل مع الحاويات (Containers) ووصلت إلى مرحلة تشغّل فيها عشرات منها على عدة خوادم، فستصطدم سريعًا بأسئلة صعبة: من يعيد تشغيل الحاوية إذا سقطت؟ كيف توزّع الحِمل بين النسخ؟ كيف تطرح إصدارًا جديدًا دون توقّف الخدمة؟ هنا يأتي دور Kubernetes (يُختصر إلى K8s)، وهو نظام مفتوح المصدر لتنسيق الحاويات (Container Orchestration). في هذا المقال نشرح المفاهيم الأساسية بلغة عملية، ثم ننشر أول تطبيق فعلي معًا.

ما هو Kubernetes ولماذا نحتاجه؟

Kubernetes هو منصّة تدير دورة حياة الحاويات نيابةً عنك: تشغيلها، ومراقبتها، وإعادة جدولتها عند الأعطال، وتوسيعها أفقيًا، وتوزيع الشبكة بينها. بدلًا من أن تقول للنظام «شغّل هذه الحاوية على هذا الخادم»، تصف له الحالة المطلوبة (Desired State) — مثل «أريد ثلاث نسخ من تطبيقي تعمل دائمًا» — ويتكفّل هو بالوصول إلى هذه الحالة والحفاظ عليها. هذا الأسلوب يُسمّى النموذج التصريحي (Declarative Model)، وهو جوهر طريقة عمل K8s.

الفائدة العملية واضحة: إذا سقطت إحدى النسخ، يكتشف Kubernetes الفارق بين الواقع (نسختان) والمطلوب (ثلاث نسخ) ويُنشئ نسخة بديلة تلقائيًا. هذه الحلقة المستمرة من المقارنة والتصحيح (Reconciliation Loop) هي ما يجعل العنقود يداوي نفسه (Self-healing).

بنية العنقود (Cluster Architecture)

أي عنقود Kubernetes ينقسم إلى جزأين: مستوى التحكّم (Control Plane) الذي يتّخذ القرارات، والعُقد العاملة (Worker Nodes) التي تشغّل أحمال العمل الفعلية.

مستوى التحكّم (Control Plane)

  • kube-apiserver: الواجهة الأمامية للعنقود. كل أمر kubectl يصل إلى هنا، وكل المكوّنات تتحدث عبره. هو نقطة الدخول الوحيدة.
  • etcd: قاعدة بيانات من نوع key-value تخزّن حالة العنقود كاملةً. إذا فقدت etcd فقدت العنقود، لذا يجب أخذ نسخ احتياطية منها بانتظام.
  • kube-scheduler: يقرّر على أي عُقدة يُشغَّل كل Pod جديد، بناءً على الموارد المتاحة والقيود.
  • kube-controller-manager: يشغّل حلقات التحكّم التي تقارن الحالة الفعلية بالمطلوبة وتصحّح الفارق.

العُقد العاملة (Worker Nodes)

  • kubelet: وكيل يعمل على كل عقدة، يتلقّى الأوامر من الـ api-server ويتأكّد أن الحاويات المطلوبة تعمل فعلًا.
  • kube-proxy: يدير قواعد الشبكة على العقدة ويتيح التواصل مع الـ Pods عبر الـ Services.
  • Container Runtime: المحرّك الذي يشغّل الحاويات فعليًا، مثل containerd.

المفاهيم الأساسية

  • Pod: أصغر وحدة قابلة للنشر في K8s. هو غلاف يحوي حاوية واحدة (أو أكثر تتشارك الشبكة والتخزين). أنت نادرًا ما تنشئ Pod مباشرةً؛ بل تتركه لكائن أعلى يديره.
  • ReplicaSet: يضمن وجود عدد محدّد من نُسخ الـ Pod تعمل في أي لحظة. هو من يُنشئ بديلًا عند سقوط نسخة.
  • Deployment: الطبقة التي تتعامل معها عادةً. يدير ReplicaSet نيابةً عنك، ويتيح التحديثات المتدرّجة (Rolling Updates) والرجوع لإصدار سابق (Rollback).
  • Service: عنوان شبكي ثابت لمجموعة Pods. بما أن الـ Pods تأتي وتذهب وتتغيّر عناوين IP الخاصة بها، يوفّر الـ Service اسمًا وعنوانًا ثابتين يوزّعان الطلبات على النسخ الحيّة.
  • Namespace: تقسيم منطقي للعنقود يفصل الموارد (مثلًا dev وprod) داخل نفس العنقود.

أنواع الـ Service الشائعة

  • ClusterIP (الافتراضي): يعطي عنوانًا داخليًا لا يمكن الوصول إليه إلا من داخل العنقود. مناسب للتواصل بين الخدمات.
  • NodePort: يفتح منفذًا (من النطاق 30000–32767) على كل عقدة، فيصير التطبيق متاحًا من خارج العنقود عبر NodeIP:NodePort.

مثال عملي: نشر nginx

لنبدأ بأبسط طريقة. الأمر التالي ينشئ Deployment يشغّل حاوية nginx واحدة:

kubectl create deployment web --image=nginx:1.27

تحقّق من أن كل شيء يعمل:

kubectl get deployments
kubectl get pods

الآن وسّع التطبيق إلى ثلاث نسخ بأمر واحد. سيتولّى الـ ReplicaSet إنشاء النسختين الإضافيتين:

kubectl scale deployment web --replicas=3

كرّر kubectl get pods وستجد ثلاثة Pods قيد التشغيل. لكشف التطبيق خارج العنقود، أنشئ Service من نوع NodePort يربط منفذ الحاوية 80:

kubectl expose deployment web --type=NodePort --port=80

اعرف المنفذ الذي اختاره العنقود:

kubectl get service web

منيفست YAML: الطريقة التصريحية

الأوامر السابقة سريعة، لكن الطريقة الاحترافية هي وصف كل شيء في ملف YAML تحفظه في نظام التحكّم بالإصدارات (Git). الملف التالي يعرّف Deployment وService معًا:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80

لاحظ أن الـ Service يجد الـ Pods عبر تطابق الـ selector مع الـ labels في قالب الـ Deployment (هنا app: web). هذا الرابط هو حجر الأساس. احفظ الملف باسم web.yaml ثم طبّقه:

kubectl apply -f web.yaml

للتشخيص، استخدم الأوامر الثلاثة الأهم في حياتك اليومية مع K8s:

kubectl get pods -o wide
kubectl describe pod <pod-name>
kubectl logs <pod-name>

get يعطيك نظرة سريعة، وdescribe يعرض الأحداث والتفاصيل والأخطاء، وlogs يُظهر مخرجات التطبيق داخل الحاوية.

أخطاء شائعة

  • عدم تطابق الـ labels والـ selector: إذا كتبت app: web في الـ Deployment وapp: nginx في الـ Service، فلن يجد الـ Service أي Pod وستحصل على «لا شيء يستجيب» دون رسالة خطأ واضحة. تأكّد دائمًا من تطابقهما حرفيًا.
  • الخلط بين أنواع الـ Service: استخدام ClusterIP ثم التعجّب لماذا لا يصل إليك التطبيق من المتصفّح. ClusterIP داخلي فقط؛ للوصول الخارجي تحتاج NodePort أو موازِن حِمل (LoadBalancer).
  • CrashLoopBackOff: حالة شائعة تعني أن الحاوية تبدأ ثم تسقط مرارًا، فيُبطئ K8s محاولات إعادة التشغيل. السبب غالبًا خطأ في إعداد التطبيق أو متغيّر بيئة ناقص. شخّصها فورًا بـ kubectl logs <pod-name> وkubectl describe pod <pod-name> لقراءة سبب الخروج.
  • نسيان تحديد وسم الصورة (Image Tag): استخدام nginx دون إصدار يجلب latest، وقد يتغيّر تحت قدميك. حدّد إصدارًا صريحًا مثل nginx:1.27 لإعادة إنتاج موثوقة.

الخاتمة والخطوة التالية

غطّينا بنية العنقود، والفرق بين Pod وReplicaSet وDeployment وService، ونشرنا تطبيقًا حقيقيًا ووسّعناه وكشفناه. الخطوة التالية الطبيعية: ثبّت minikube على جهازك لتجربة كل هذه الأوامر في بيئة محلية آمنة، ثم جرّب تحديثًا متدرّجًا عبر kubectl set image deployment/web nginx=nginx:1.27.1 وراقب كيف يستبدل K8s النسخ واحدةً تلو الأخرى دون توقّف الخدمة. بعدها انتقل إلى مفاهيم ConfigMap وSecret لإدارة الإعدادات والأسرار باحتراف.

اترك تعليقاً