شرح Kubernetes وإدارة الحاويات

شرح Kubernetes وإدارة الحاويات

حين نتحدث عن تشغيل التطبيقات الحديثة في بيئات الإنتاج، فنحن لا نتحدث فقط عن “رفع التطبيق” على خادم وانتهى الأمر. العالم اليوم تغيّر كثيرًا، والتطبيقات صارت تتكوّن من عشرات الخدمات الصغيرة، وقواعد بيانات، ومهام خلفية، وواجهات API، وعمليات معالجة، وكل جزء يحتاج إلى تشغيل موثوق، ومراقبة مستمرة، وقدرة على التوسع، واستجابة سريعة عند حدوث أي خلل. هنا تظهر أهمية Kubernetes، أو كما يحب كثيرون تسميته “العقل المنظم” الذي يدير الحاويات بدلًا منا، ويحوّل الفوضى التشغيلية إلى نظام قابل للفهم والتحكم.

في البداية قد يبدو Kubernetes معقدًا، وهذا طبيعي جدًا. أغلب المطورين والمهندسين يمرون بهذه المرحلة: تقرأ عن Pod وDeployment وService وIngress وNamespace وConfigMap وSecret، ثم تشعر أن كل شيء متشابك. لكن الحقيقة أن Kubernetes ليس معقدًا بقدر ما هو شامل. هو لا يهدف إلى تعقيد الحياة، بل إلى حل مشكلة حقيقية جدًا: كيف نشغّل تطبيقاتنا داخل الحاويات بشكل ثابت، متكرر، قابل للتوسّع، وقابل للاستعادة، دون أن نضطر لإدارة كل خادم وكل نسخة يدويًا؟ إذا فهمت الفكرة الكبرى أولًا، ستصبح التفاصيل لاحقًا أوضح بكثير.

ما هي الحاويات أصلًا ولماذا احتجنا Kubernetes؟

قبل Kubernetes كانت هناك مشاكل متكررة في نشر التطبيقات. قد يعمل التطبيق على جهاز المطور بشكل ممتاز، ثم يفشل على الخادم بسبب اختلاف المكتبات أو نسخ النظام أو المتغيرات البيئية. هنا جاءت الحاويات لتقول ببساطة: “ضع التطبيق مع اعتمادياته في صندوق معزول، وشغّله في أي مكان بنفس الشكل تقريبًا.” هذا الصندوق هو الحاوية Container. وإذا كانت الحاوية هي الصندوق، فـ Docker كان من أشهر الأدوات التي سهلت إنشاء هذه الصناديق وتشغيلها.

لكن مع الوقت اكتشفنا أن تشغيل حاوية واحدة ليس هو التحدي الحقيقي. التحدي الحقيقي يبدأ عندما يكون لديك:

  • عشرات الحاويات موزعة على عدة خوادم.

  • نسخة يجب تحديثها دون توقف الخدمة.

  • تطبيق يجب أن يتوسع تلقائيًا عند الضغط.

  • حاويات قد تتعطل وتحتاج إلى إعادة تشغيل فوري.

  • تواصل بين الخدمات الداخلية.

  • إعدادات تختلف بين بيئة التطوير والإنتاج.

  • سرّيات حساسة مثل كلمات المرور والمفاتيح.

  • مراقبة وحوكمة وتوزيع للحمل.

هنا لم يعد Docker وحده كافيًا، بل احتجنا إلى منصة تنسيق Orchestration. Kubernetes جاء ليكون هذه المنصة. هو لا ينشئ الحاويات فقط، بل يدير دورة حياتها كاملة: التوزيع، الإبقاء على العدد المطلوب، إعادة التشغيل، التوسع، الاكتشاف الداخلي، التحديث التدريجي، والعزل المنطقي.

ما هو Kubernetes ببساطة؟

Kubernetes هو نظام مفتوح المصدر لإدارة الحاويات على نطاق واسع. فكر فيه كمدير تشغيل ذكي يراقب ما يحدث داخل العنقود Cluster، ويضمن أن عدد النسخ المطلوبة من التطبيق تعمل دائمًا، وأن الخدمة تبقى متاحة، وأن أي خلل يتم إصلاحه تلقائيًا قدر الإمكان.

بمعنى أبسط: أنت لا تقول لـ Kubernetes “شغّل هذه الحاوية فقط”، بل تقول له “أريد ثلاث نسخ من هذا التطبيق، واجعلها متاحة، ووزعها على الخوادم المتوفرة، وراقب صحتها، وإذا تعطلت واحدة فاستبدلها، وإذا زاد الضغط فوسعها”. هذا الانتقال من التفكير اليدوي إلى التفكير التوجيهي Declarative هو أهم تغيير ذهني في عالم Kubernetes.

فلسفة Kubernetes: أخبره بالهدف لا بالخطوات

من أجمل مفاهيم Kubernetes أنه يعتمد على الحالة المرغوبة Desired State. أنت لا تكتب أوامر تفصيلية من نوع: “أنشئ الحاوية الآن، ثم انسخها، ثم ضعها في هذا الخادم، ثم راقبها”. لا، أنت تكتب ملفًا يصف ما تريد أن يكون عليه الوضع النهائي، وKubernetes يعمل باستمرار ليجعل الواقع يطابق هذا الوصف.

مثال بسيط: إذا قلت له إنك تريد 3 نسخ من التطبيق، ثم تعطل خادم، سيحاول إعادة توزيع النسخ المتبقية. وإذا توقف Pod، سيعيد إنشاءه. وإذا قللت العدد إلى 2، سيخفض العدد. هذا المفهوم هو سر القوة في Kubernetes، لأنه يقلل الاعتماد على التدخل اليدوي ويحوّل البنية التحتية إلى نظام ذاتي الإصلاح Self-healing بدرجة كبيرة.

مكونات Kubernetes الأساسية

لفهم Kubernetes جيدًا، تحتاج إلى رؤية الصورة الكبرى أولًا. العنقود يتكون عادة من نوعين من المكونات: Control Plane و Worker Nodes.

1) Control Plane

هذا هو “مخ” النظام. هو المسؤول عن اتخاذ القرارات، وتخزين حالة العنقود، وجدولة الحاويات، والتأكد من أن كل شيء يسير كما هو مطلوب. من مكوناته:

  • API Server: بوابة التواصل الأساسية مع Kubernetes. كل أوامر kubectl تمر عبره.

  • etcd: قاعدة البيانات التي تحفظ حالة العنقود.

  • Scheduler: يقرر على أي Node سيتم تشغيل الـ Pod.

  • Controller Manager: يراقب ويصلح الحالة عندما تختلف عن المطلوب.

  • Cloud Controller Manager: يتعامل مع مزايا المزود السحابي إن وُجد.

2) Worker Nodes

هذه هي الخوادم التي تُشغَّل عليها الحاويات فعليًا. كل Node تحتوي عادة على:

  • kubelet: الوكيل الذي يستقبل الأوامر من Control Plane وينفذها محليًا.

  • container runtime: مثل containerd أو CRI-O لتشغيل الحاويات.

  • kube-proxy: يساعد في الشبكات وتوجيه الحركة إلى الخدمات المناسبة.

الفكرة هنا أن Control Plane لا يشغّل التطبيق بنفسه، بل ينسق ويقرر، بينما Worker Nodes تنفذ فعليًا.

ما هو الـ Pod؟

إذا كانت الحاوية هي أصغر وحدة تشغيل، فإن Pod هو أصغر وحدة في Kubernetes. الـ Pod قد يحتوي حاوية واحدة أو أكثر تشترك في نفس الشبكة ونفس التخزين المؤقت.

في معظم الحالات، ستجد Pod يحتوي حاوية واحدة فقط، لكن وجود أكثر من حاوية داخل نفس الـ Pod يكون مفيدًا عندما تكون هذه الحاويات مرتبطة ارتباطًا شديدًا، مثل حاوية تطبيق رئيسية وحاوية مساعدة تقوم بالتسجيل أو المراقبة أو التحضير.

الشيء المهم: أنت عادة لا تدير الحاوية مباشرة في Kubernetes، بل تدير Pod، لأن Kubernetes يفكر على هذا المستوى. وهذا قد يبدو غريبًا في البداية، لكنه منطقي لأن الـ Pod يمثل بيئة تشغيل متكاملة.

ما الفرق بين Pod وContainer؟

الحاوية Container هي عملية معزولة تشغّل التطبيق، أما الـ Pod فهو غلاف أعلى يضم حاوية أو أكثر، ويمنحها نفس IP ونفس مساحة الشبكة ونفس بعض الخصائص التشغيلية. لذلك عندما تريد التوسع أو التوزيع، Kubernetes لا ينقل “الحاوية” فقط، بل يتعامل مع الـ Pod كوحدة كاملة.

هذا التصميم يسمح بتنسيق أفضل بين الحاويات المتعاونة داخل نفس التطبيق المصغّر. ومع ذلك، القاعدة العملية البسيطة هي: ابدأ بحاوية واحدة داخل Pod واحد ما لم تكن لديك حاجة حقيقية لتعدد الحاويات في نفس Pod.

Deployment: القلب النابض للتطبيقات المتكررة

الـ Pod وحده لا يكفي إذا أردت إدارة نسخ متعددة وتحديثات سلسة. هنا يأتي Deployment. الـ Deployment يصف كيف يجب أن تكون نسخ التطبيق، وعددها، وكيف يتم تحديثها، وكيف يتم الرجوع عن التحديث إذا حدثت مشكلة.

عندما تكتب Deployment، أنت تخبر Kubernetes:
“أريد هذا التطبيق أن يعمل بعدد معين من النسخ، وأريدك أن تحافظ على هذا العدد، وأن تستبدل النسخ القديمة تدريجيًا عندما أطلب تحديثًا.”

مثال Deployment بسيط

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  strategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: nginx:1.27
          ports:
            - containerPort: 80

هذا المثال يطلب 3 نسخ من تطبيق يستخدم صورة Nginx. Kubernetes سيحاول الحفاظ على 3 Pods تعمل دائمًا، وعندما يحدث تحديث، سيستبدل النسخ بطريقة متدرجة بدلًا من إسقاط الخدمة دفعة واحدة.

Service: كيف تصل إلى التطبيق؟

وجود Pod أو عدة Pods لا يعني أنك تستطيع الوصول إليها بسهولة من الخارج أو حتى من داخل العنقود إن لم تُعرِّف Service. الـ Service هي طبقة ثابتة تمنح تطبيقك عنوانًا مستقرًا، حتى لو تغيرت Pods من الداخل.

لماذا نحتاج ذلك؟ لأن الـ Pod قد يُحذف ويُعاد إنشاؤه بعنوان IP جديد. لو اعتمدت على IP الخاص به مباشرة، ستكسر الوصول كلما تغيّر. لكن Service تعطيك نقطة وصول ثابتة، وتقوم بتوزيع الحركة على Pods المناسبة.

أنواع الخدمات الشائعة

  • ClusterIP: للوصول الداخلي داخل العنقود.

  • NodePort: يفتح منفذًا على كل Node للوصول الخارجي المباشر.

  • LoadBalancer: يطلب Load Balancer من المزود السحابي.

  • ExternalName: لربط خدمة باسم DNS خارجي.

مثال Service

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 80

هذه الخدمة ستقوم بتوجيه الطلبات إلى Pods التي تحمل التسمية app: my-app.

Ingress: التوجيه الذكي للويب

إذا كانت Service هي المدخل الداخلي، فإن Ingress هو الطبقة التي تنظم الوصول من الإنترنت إلى عدة خدمات HTTP/HTTPS من خلال اسم نطاق واحد أو أكثر. باستخدام Ingress يمكنك أن تقول:

  • هذا النطاق يذهب إلى خدمة الـ frontend.

  • هذا المسار /api يذهب إلى خدمة الـ backend.

  • هذا المسار /admin يذهب إلى لوحة الإدارة.

Ingress يحتاج عادة إلى Ingress Controller مثل NGINX Ingress Controller أو Traefik. هو ليس مجرد ملف YAML، بل تعريف لسلوك التوجيه، بينما الـ Controller هو من ينفذه فعليًا.

مثال Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80

بهذا الشكل يصبح التحكم في التوجيه أكثر مرونة من الاعتماد على خدمات منفصلة لكل جزء من التطبيق.

ConfigMap وSecret: الإعدادات دون إعادة بناء الصورة

من الأخطاء الشائعة أن يضع المطور الإعدادات داخل التطبيق نفسه أو داخل صورة Docker. هذا يجعل كل تغيير صغير يتطلب إعادة بناء الصورة وإعادة نشرها. Kubernetes يعالج هذه المشكلة عبر ConfigMap و Secret.

ConfigMap

يُستخدم لتخزين إعدادات غير حساسة، مثل:

  • اسم المضيف

  • إعدادات البيئة

  • قيم debug

  • عناوين الخدمات

Secret

يُستخدم لتخزين البيانات الحساسة، مثل:

  • كلمات المرور

  • رموز API

  • شهادات TLS

  • مفاتيح التشفير

رغم أن Secret في Kubernetes ليس تشفيرًا سحريًا بحد ذاته إلا إذا فعلت التشفير المناسب على مستوى التخزين، فإنه يظل وسيلة أنظف من وضع السرّيات داخل الكود أو الصور.

مثال ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  APP_DEBUG: "false"
  API_URL: "https://api.example.com"

مثال Secret

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
stringData:
  DB_PASSWORD: "super-secret-password"
  JWT_SECRET: "very-secret-key"

استخدامهما داخل Pod

apiVersion: v1
kind: Pod
metadata:
  name: config-demo
spec:
  containers:
    - name: app
      image: nginx:1.27
      env:
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: DB_PASSWORD

هذا الأسلوب يمنحك مرونة كبيرة، ويجعل تطبيقك أقرب إلى فلسفة Twelve-Factor App.

Namespaces: تنظيم العالم داخل Kubernetes

عندما تكبر البيئات، يصبح من الضروري فصل الموارد منطقيًا. Namespace هو طريقة لتقسيم العنقود إلى مساحات مستقلة نسبيًا. يمكنك استخدام Namespace لكل بيئة أو فريق أو مشروع.

مثلاً:

  • dev

  • staging

  • production

  • finance-team

  • marketing-team

هذا الفصل يساعد في:

  • تقليل التضارب في أسماء الموارد.

  • تطبيق سياسات وصول مختلفة.

  • تنظيم الحصص Resource Quotas.

  • عزل الموارد بشكل إداري أفضل.

مثال Namespace

apiVersion: v1
kind: Namespace
metadata:
  name: production

ثم يمكنك نشر الموارد داخل هذا الـ Namespace.

Probes: كيف يعرف Kubernetes أن التطبيق حي؟

من أهم ميزات Kubernetes أنه لا يكتفي بتشغيل الحاوية، بل يراقب حالتها الصحية. هذا يتم عبر livenessProbe و readinessProbe و startupProbe.

livenessProbe

تسأل: هل التطبيق ما زال يعمل؟
إذا كانت الإجابة لا، يمكن لـ Kubernetes إعادة تشغيل الحاوية.

readinessProbe

تسأل: هل التطبيق جاهز لاستقبال الطلبات؟
إذا لم يكن جاهزًا، فلن يرسل له traffic حتى يصبح جاهزًا.

startupProbe

مفيد للتطبيقات التي تحتاج وقتًا طويلًا في البداية قبل أن تصبح جاهزة، مثل بعض التطبيقات الثقيلة أو الخدمات التي تقوم بتهيئة كبيرة.

مثال

apiVersion: v1
kind: Pod
metadata:
  name: probe-demo
spec:
  containers:
    - name: app
      image: nginx:1.27
      ports:
        - containerPort: 80
      livenessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 10
        periodSeconds: 5
      readinessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 5
        periodSeconds: 3

الفرق بين هذه البروبس مهم جدًا في الإنتاج، لأن كثيرًا من الأعطال الظاهرية ليست “توقفًا” كاملًا، بل “عدم جاهزية” مؤقتة. Kubernetes يساعدك على التمييز بين الحالتين.

Requests وLimits: احترام الموارد

داخل بيئة تحتوي على عدة تطبيقات، لا يكفي أن تقول: “شغّلني”. عليك أن تحدد مقدار الموارد الذي يحتاجه التطبيق، حتى لا يستهلك كل شيء ويؤذي باقي الخدمات.

  • requests: الحد الأدنى المطلوب لضمان جدولة مناسبة.

  • limits: الحد الأعلى المسموح به.

مثال

apiVersion: v1
kind: Pod
metadata:
  name: resource-demo
spec:
  containers:
    - name: app
      image: nginx:1.27
      resources:
        requests:
          cpu: "100m"
          memory: "128Mi"
        limits:
          cpu: "500m"
          memory: "256Mi"

إذا لم تضبط هذه القيم، فقد يسبب تطبيق واحد مشاكل لباقي التطبيقات. في الإنتاج، هذا الأمر ليس تفصيلًا، بل أساس للاستقرار.

التخزين في Kubernetes: عندما تحتاج البيانات إلى البقاء

الحاويات بطبيعتها مؤقتة. إذا حُذفت الحاوية، غالبًا تضيع البيانات المخزنة داخلها ما لم تستخدم تخزينًا خارجيًا. لذلك لدينا مفهومان مهمان:

  • Volume: مساحة تخزين مرتبطة بالـ Pod.

  • PersistentVolume / PersistentVolumeClaim: طبقة أكثر تنظيمًا لتوفير تخزين دائم.

مثال PersistentVolumeClaim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-storage
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

مثال استخدام PVC داخل Pod

apiVersion: v1
kind: Pod
metadata:
  name: storage-demo
spec:
  containers:
    - name: app
      image: nginx:1.27
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: web-data
  volumes:
    - name: web-data
      persistentVolumeClaim:
        claimName: app-storage

هذا يتيح لك تشغيل تطبيقات تحتاج إلى حالة State مثل قواعد البيانات أو أنظمة الملفات أو التخزين المؤقت طويل الأمد بطريقة أكثر أمانًا.

StatefulSet: عندما تحتاج كل نسخة إلى هوية ثابتة

بعض التطبيقات ليست مجرد نسخ متطابقة قابلة للتبديل بسهولة، بل تحتاج إلى هوية ثابتة وترتيب معين، مثل قواعد البيانات الموزعة أو الأنظمة التي تعتمد على أسماء مستقرة. هنا يأتي StatefulSet.

على عكس Deployment، فإن StatefulSet يمنح كل Pod اسمًا ثابتًا وترتيبًا محددًا، ويكون مفيدًا عندما تعتمد الخدمة على الاستمرارية والهوية.

أمثلة على الاستخدام:

  • قواعد البيانات

  • الكلاسترز المتزامنة

  • الرسائل الموزعة

  • بعض تطبيقات التخزين

الفرق الجوهري بين Deployment وStatefulSet هو أن الأول يركّز على النسخ المتطابقة القابلة للاستبدال، بينما الثاني يركز على الهوية والاستمرارية.

DaemonSet: نسخة على كل Node

في بعض الأحيان تحتاج إلى تشغيل نسخة من التطبيق على كل عقدة في العنقود، مثل وكلاء المراقبة، أو أدوات تسجيل logs، أو برامج جمع المقاييس. هذا دور DaemonSet.

مثلاً، إذا أردت أن يتواجد agent للمراقبة على كل Node، فالـ DaemonSet هو الحل الطبيعي. كلما أُضيفت Node جديدة، Kubernetes يشغل عليها النسخة المناسبة تلقائيًا.

Job وCronJob: للمهام التي تنتهي

ليست كل الأعمال في Kubernetes تطبيقات مستمرة. بعض المهام تُنفَّذ مرة واحدة وتنتهي، مثل:

  • ترحيل قاعدة البيانات

  • معالجة ملف كبير

  • توليد تقرير

  • تنفيذ مهمة تنظيف

Job

يُستخدم لتشغيل مهمة حتى تكتمل.

CronJob

يُستخدم لتشغيل مهمة وفق جدول زمني، مثل cron في لينكس.

مثال CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: alpine
              command: ["sh", "-c", "echo backup started && sleep 5"]
          restartPolicy: OnFailure

هذا النوع من الموارد مفيد جدًا في التشغيل اليومي، لأنه يتيح إدارة المهام الدورية داخل نفس منظومة التنسيق.

Helm: مدير الحزم لعالم Kubernetes

عندما يبدأ عدد ملفات YAML بالازدياد، يصبح من الصعب الحفاظ على كل شيء يدويًا. هنا يأتي Helm، وهو مدير حزم يساعدك على نشر التطبيقات عبر Charts قابلة لإعادة الاستخدام والتخصيص.

Helm مفيد جدًا عندما تريد:

  • إعادة استخدام نفس البنية لتطبيقات متعددة.

  • تمرير قيم مختلفة لكل بيئة.

  • نشر التطبيقات المعروفة بسرعة.

  • تقليل التكرار في ملفات YAML.

فكر في Helm كطريقة لقول: “لدي قالب، وأريد نسخه مع قيم مختلفة حسب البيئة.” هذا يختصر وقتًا كبيرًا ويجعل النشر أكثر تنظيمًا.

Kustomize: تخصيص الموارد دون قوالب معقدة

أحيانًا لا تريد قوالب Helm، بل تريد طريقة منظمة لتعديل ملفات YAML الموجودة. هنا يأتي Kustomize، الذي يسمح لك ببناء نسخ مختلفة من نفس الموارد عبر طبقات من التخصيص.

مثلاً:

  • ملف أساسي للـ base

  • تعديلات خاصة بالتطوير

  • تعديلات خاصة بالإنتاج

هذا الأسلوب جميل عندما تريد الحفاظ على YAML واضح ومباشر، مع القدرة على الاختلاف بين البيئات.

kubectl: أداة التحكم اليومية

kubectl هو الرفيق اليومي لأي شخص يعمل مع Kubernetes. من خلاله يمكنك:

  • إنشاء الموارد

  • عرض الحالة

  • حذف الموارد

  • مشاهدة السجلات

  • الدخول إلى الحاويات

  • تنفيذ الأوامر داخلها

أوامر أساسية

kubectl get pods
kubectl get deployments
kubectl get services
kubectl describe pod my-app-pod
kubectl logs my-app-pod
kubectl exec -it my-app-pod -- sh
kubectl apply -f deployment.yaml
kubectl delete -f deployment.yaml

أوامر مفيدة جدًا في التشخيص

kubectl get all
kubectl get events
kubectl get pods -o wide
kubectl describe deployment my-app
kubectl top pods
kubectl top nodes

في الواقع، كثير من وقت العمل على Kubernetes لا يذهب في “الإنشاء” فقط، بل في الفهم: لماذا لم يعمل الـ Pod؟ لماذا لم تُجَدول الحاوية؟ لماذا لم تستجب الخدمة؟ kubectl هو البوصلة في هذا العالم.

دورة حياة النشر داخل Kubernetes

حين تنشر تطبيقًا في Kubernetes، تمر العملية عادة بمراحل متكررة:

  1. بناء صورة Docker.

  2. رفع الصورة إلى Registry.

  3. كتابة ملفات YAML أو Helm Chart.

  4. تطبيق الموارد على العنقود.

  5. مراقبة الـ Pods والخدمات.

  6. اختبار الوصول.

  7. تعديل القيم عند الحاجة.

  8. توسيع النسخ أو تقليلها.

  9. تحديث الصورة أو التهيئة.

  10. الرجوع إلى إصدار سابق عند الفشل.

هذه الدورة تختلف عن النشر التقليدي الذي كان غالبًا يعتمد على SSH ونسخ الملفات يدويًا وإعادة تشغيل الخدمات. Kubernetes يجعل النشر أقرب إلى نظام هندسي منظم، وليس مجرد عملية تشغيل.

مثال عملي كامل: تطبيق ويب بسيط

لنفترض أننا نريد تشغيل تطبيق ويب بسيط باستخدام Nginx. سنحتاج إلى Deployment وService، وربما Ingress لاحقًا.

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: nginx:1.27
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "200m"
              memory: "128Mi"
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10

Service

apiVersion: v1
kind: Service
metadata:
  name: web-app-service
spec:
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP

Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
spec:
  rules:
    - host: web.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-app-service
                port:
                  number: 80

بهذه البنية البسيطة أصبح لديك تطبيق قابل للوصول، قابل للتوسّع، وقابل للإدارة ضمن بيئة Kubernetes.

كيف يتم التوسع Scaling؟

واحدة من أجمل ميزات Kubernetes هي القدرة على التوسع أفقيًا. بدلًا من تكبير خادم واحد فقط، يمكنك زيادة عدد النسخ من التطبيق. هذا مفيد جدًا عندما يزيد عدد الطلبات، لأن حمل العمل يتوزع على عدة Pods.

التوسع اليدوي

kubectl scale deployment web-app --replicas=5

التوسع التلقائي

يتم عبر Horizontal Pod Autoscaler الذي يراقب مؤشرات مثل استهلاك CPU أو بعض المقاييس الأخرى، ثم يزيد أو يقلل عدد Pods حسب الحاجة.

التوسع التلقائي لا يعني فقط “أكثر = أفضل”، بل يعني الاستجابة الذكية للضغط مع الحفاظ على التكاليف ضمن حدود معقولة.

Rolling Updates وRollback: التحديث بدون صدمة

في السابق، كان التحديث أحيانًا يعني توقفًا كاملًا. أما في Kubernetes، فالتحديث التدريجي RollingUpdate يسمح باستبدال النسخ القديمة تدريجيًا بالنسخ الجديدة، بحيث تبقى الخدمة متاحة خلال التحديث.

إذا حدث خلل بعد التحديث، يمكنك الرجوع بسهولة نسبيًا إلى الإصدار السابق عبر rollback. وهذا يمنح الفرق التشغيلية ثقة أكبر في النشر المستمر.

أمثلة مفيدة

kubectl rollout status deployment/web-app
kubectl rollout history deployment/web-app
kubectl rollout undo deployment/web-app

هذه الأوامر قد تنقذ يومك في لحظة حرجة، خصوصًا عندما تكتشف أن نسخة جديدة سببت مشكلة في الإنتاج.

الأمن في Kubernetes: لا تترك العالم مفتوحًا

Kubernetes قوي، لكنه أيضًا حساس إذا أُسيء استخدامه. لأنك تدير بيئة كبيرة، فإن أي خطأ في الصلاحيات أو كشف السرّيات قد يكون مكلفًا. لذلك الأمن ليس طبقة إضافية، بل جزء أساسي من التصميم.

نقاط مهمة جدًا

  • استخدام RBAC لتحديد من يملك الوصول إلى ماذا.

  • عدم تشغيل الحاويات كـ root ما أمكن.

  • استخدام Secrets بحذر.

  • تقييد الشبكات بين الخدمات عبر Network Policies.

  • استخدام صور موثوقة ومحدثة.

  • فحص الصور قبل النشر.

  • تحديد الموارد requests/limits.

  • تطبيق مبدأ أقل صلاحية ممكنة.

مثال RBAC مبسط

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-binding
  namespace: production
subjects:
  - kind: User
    name: dev-user
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

الأمن في Kubernetes ليس مجرد “إعداد” مرة واحدة، بل ثقافة تشغيل كاملة.

المراقبة Logging وMonitoring: لأن ما لا تراه لا تستطيع إصلاحه

حين يبدأ النظام في الإنتاج، يصبح السؤال الأهم: ماذا يحدث الآن؟ لا يكفي أن تقول “التطبيق يعمل”. يجب أن تعرف:

  • كم عدد الطلبات؟

  • هل توجد أخطاء؟

  • ما مستوى الضغط على الذاكرة والمعالج؟

  • هل Pods تعيد التشغيل؟

  • أين وقعت المشكلة؟

المراقبة Monitoring

غالبًا تستخدم أدوات مثل Prometheus وGrafana لتجميع المقاييس وعرضها في لوحات جميلة وواضحة.

السجلات Logging

السجلات مهمة جدًا في تتبع سبب المشاكل. يجب أن يكون التطبيق نفسه يكتب logs بشكل منظم إلى stdout/stderr، حتى يستطيع Kubernetes والمنظومة المحيطة جمعها.

أوامر مفيدة

kubectl logs my-app-pod
kubectl logs -f my-app-pod
kubectl logs my-app-pod --previous

السجل الجيد ليس مجرد نص، بل أداة تشخيص. كلما كان التطبيق واضحًا في تسجيل الأحداث، كلما كانت صيانته أسهل.

أفضل الممارسات عند العمل مع Kubernetes

عند التعامل مع Kubernetes، تظهر بعض العادات الجيدة التي تصنع فرقًا كبيرًا على المدى البعيد:

1) اجعل تطبيقك Stateless قدر الإمكان

التطبيقات التي لا تعتمد على الحالة الداخلية أسهل بكثير في التوسع والاستبدال. كلما فصلت البيانات إلى قاعدة بيانات أو تخزين خارجي، أصبح التطبيق أكثر مرونة.

2) استخدم ملفات تعريف واضحة

سمِّ الموارد بطريقة متسقة. فوضى الأسماء في Kubernetes تتحول بسرعة إلى فوضى تشغيلية.

3) لا تتجاهل requests وlimits

هذه ليست تفاصيل تجميلية. إنها ضمانة للاستقرار.

4) ابدأ بسيطًا ثم توسع

لا تبنِ بنية معقدة من اليوم الأول. ابدأ بـ Deployment + Service، ثم أضف Ingress وConfigMap وSecrets وHPA حسب الحاجة.

5) اختبر على بيئة staging

لا تُجرّب أفكارًا غير ناضجة مباشرة على production.

6) راقب بروبز الصحة

كثير من الأعطال تتفاقم بسبب غياب readiness وliveness checks.

7) استخدم versioning للصورة

لا تعتمد على latest في الإنتاج. استخدم أرقام إصدارات واضحة.

8) صغّر الصورة Docker

كلما كانت الصورة أخف، كان النشر أسرع والأمان أفضل غالبًا.

مثال Dockerfile جيد لتطبيق ويب

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

هذا المثال يوضح فكرة بناء التطبيق في مرحلة ثم نسخ الناتج إلى صورة تشغيل أخف. مثل هذه الممارسات تجعل الصور النهائية صغيرة ونظيفة.

Kubernetes في الحياة الواقعية: لماذا يحبه الفرق التقنية؟

الجواب ليس لأن Kubernetes “رائج”، بل لأنه يحل مشاكل حقيقية جدًا. عندما يكون لديك فريق صغير قد يبدو ثقيلًا في البداية، لكن عندما تكبر المنظومة، تبدأ الفوائد في الظهور بوضوح:

  • توحيد طريقة النشر.

  • تقليل الاختلاف بين البيئات.

  • استعادة الخدمات عند الأعطال.

  • نشر تدريجي يقلل المخاطر.

  • توزيع الحمل بشكل أفضل.

  • مرونة في التوسع.

  • فصل واضح بين التطبيق والتشغيل.

كثير من الفرق تقول في البداية: “Kubernetes مبالغ فيه.” ثم بعد أشهر من النمو، تكتشف أنه لم يكن مبالغًا فيه، بل كان سابقًا لاحتياجاتها الحالية. ولهذا السبب، من الحكمة دائمًا أن تفهمه مبكرًا حتى لو لم تستخدمه بكل ثقله منذ اليوم الأول.

التحديات الشائعة عند التعلم

من الطبيعي أن يواجه المتعلم بعض الصعوبات:

كثرة المفاهيم

الأسماء كثيرة، لكن كل مفهوم له وظيفة محددة.

ملفات YAML

قد تبدو متعبة في البداية، لكن مع الوقت ستصبح مألوفة.

التشخيص

أحيانًا لا يكون الخطأ في Kubernetes نفسه، بل في الصورة، أو الشبكة، أو الإعدادات، أو الصلاحيات.

الذهنية الجديدة

أنت لا تدير خوادم يدويًا كما في السابق، بل تصف الحالة المطلوبة وتدع النظام يعمل لتحقيقها.

من المفيد هنا أن تتذكر أن التعلم لا يكون دفعة واحدة. Kubernetes يشبه بناء لغة ثانية: في البداية تحسن القراءة ببطء، ثم تبدأ ترى الجمل منطقية، ثم فجأة تصبح قادرًا على كتابة مشاريع كاملة بثقة.

مثال متكامل لتطبيق PHP أو Laravel داخل Kubernetes

لنفترض أنك تريد تشغيل تطبيق Laravel. ستحتاج غالبًا إلى:

  • صورة Docker مناسبة.

  • Deployment للتطبيق.

  • Service داخلية.

  • ConfigMap للإعدادات.

  • Secret لكلمة المرور.

  • PersistentVolume إذا احتجت تخزينًا.

  • ربما Ingress للوصول الخارجي.

مثال مختصر على Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: laravel-app
  template:
    metadata:
      labels:
        app: laravel-app
    spec:
      containers:
        - name: laravel
          image: myrepo/laravel-app:1.0.0
          ports:
            - containerPort: 8000
          env:
            - name: APP_ENV
              value: "production"
            - name: DB_HOST
              value: "mysql-service"
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: app-secret
                  key: DB_PASSWORD
          readinessProbe:
            tcpSocket:
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 5

مثال Service

apiVersion: v1
kind: Service
metadata:
  name: laravel-service
spec:
  selector:
    app: laravel-app
  ports:
    - port: 80
      targetPort: 8000
  type: ClusterIP

هذه البنية تصلح كبداية جيدة جدًا، ثم يمكنك توسيعها حسب الحاجة، مثل إضافة Queue workers أو Scheduler jobs أو تخزين مشترك.

عندما يصبح Kubernetes أداة يومية وليس مجرد موضوع نظري

بعد فترة من العمل، ستجد نفسك لا تنظر إلى Kubernetes كأداة “معقدة”، بل كبيئة طبيعية للتشغيل. ستبدأ في التفكير بهذه الطريقة:

  • كم Replica أحتاج؟

  • هل لدي readiness probe؟

  • هل الموارد مضبوطة؟

  • هل أحتاج Service أم Ingress؟

  • هل هذه المهمة Job أم Deployment؟

  • هل هذا الإعداد ConfigMap أم Secret؟

  • هل هذه الخدمة تحتاج PVC؟

  • هل أستطيع استخدام Namespace لعزل البيئة؟

هذه الأسئلة تصبح جزءًا من طريقة التفكير اليومية، وهذا في الحقيقة هو النضج التشغيلي الحقيقي.

خلاصة عملية

Kubernetes ليس مجرد أداة لنشر الحاويات، بل هو طريقة كاملة لإدارة التطبيقات الحديثة على نطاق واسع. قيمته الحقيقية تظهر عندما تريد الثبات، والقابلية للتوسع، والتعافي من الأعطال، والتحديث السلس، والتنظيم الجيد، والفصل بين البيئات، وإدارة الموارد بطريقة أكثر احترافية. أما الحاويات نفسها فهي الأساس الذي جعل هذا كله ممكنًا، لأنها وفرت وحدة تشغيل متكررة ومحمولة، لكن Kubernetes هو الذي أعطاها الحياة في بيئات الإنتاج المعقدة.

إذا بدأت اليوم في تعلم Kubernetes، فلا تحاول حفظ كل شيء دفعة واحدة. ابدأ بفهم الصورة الكبرى: Pod، Deployment، Service، Ingress، ConfigMap، Secret، ثم انتقل إلى التخزين، ثم التوسع، ثم المراقبة، ثم الأمن. مع كل خطوة ستشعر أن الفوضى بدأت تتحول إلى نظام، وأن ما كان يبدو “سحريًا” صار مفهومًا ومباشرًا. وفي النهاية ستدرك أن Kubernetes لا يطلب منك أن تكون خبيرًا بكل شيء من البداية، بل أن تبني فهمًا تدريجيًا يحترم الواقع ويعطيك أدوات قوية عندما تحتاج إليها فعلًا.

أمثلة أوامر عملية سريعة

kubectl apply -f namespace.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl get pods -n production
kubectl describe pod web-app-xyz
kubectl logs web-app-xyz
kubectl delete -f deployment.yaml

ملخص أخير

Kubernetes يمنحك:

  • إدارة منظمة للحاويات.

  • تعافيًا تلقائيًا من الأعطال.

  • نشرًا تدريجيًا وآمنًا.

  • توسيعًا مرنًا.

  • فصلًا واضحًا بين الإعدادات والسرّيات والكود.

  • بنية قابلة للنمو مع المشاريع الكبيرة.

وإذا كان الهدف هو تشغيل تطبيقات حديثة بشكل احترافي، ففهم Kubernetes لم يعد رفاهية تقنية، بل أصبح مهارة مركزية لكل من يعمل في تطوير البرمجيات، أو DevOps، أو إدارة الأنظمة، أو البنية السحابية.

#إدارة الحاويات #Docker #Pods #Deployments #Services #Ingress #Helm #DevOps #Cloud Native #Scaling #Orchestration #Container Management #Kubernetes بالعربي #Kubernetes

اشترك في نشرتنا البريدية

12k+

المشتركون

أسبوعيًا

التكرار

مجاني

دائمًا