أساسيات Kubernetes

مشروع: نشر تطبيق على Kubernetes

18 دقيقة الدرس 10 من 32

مشروع: نشر تطبيق على Kubernetes

تتحوّل النظريات إلى هندسة حقيقية فقط حين تنجح في شحن شيء إلى كلاستر حقيقي، ومشاهدته وهو ينتشر، والتحقق من أنه يخدم الحركة، ثم استعادة الوضع بأمان حين يسوء. يأخذك هذا الدرس الختامي عبر كامل دورة حياة نشر احترافي: كتابة Namespace ثم ملف Deployment ثم Service — ثم إجراء تحديث تدريجي وتراجع طارئ عند الحاجة. كل خطوة تعكس بدقة ما يقوم به مهندسو المنصات في كبرى الشركات عشرات المرات يومياً.

الخطوة 1 — إنشاء Namespace مخصص

كل تطبيق حقيقي يعيش في Namespace خاص به حتى تُطبَّق عليه حصص الموارد وسياسات RBAC وسياسات الشبكة بشكل معزول تماماً دون التأثير على فرق العمل الأخرى. أنشئ Namespace مخصصاً قبل كتابة أي مورد آخر.

# namespace.yaml apiVersion: v1 kind: Namespace metadata: name: webapp labels: env: production team: platform
kubectl apply -f namespace.yaml kubectl get namespaces webapp # NAME STATUS AGE # webapp Active 3s
ضع التسميات (Labels) على Namespace فوراً. تُستخدَم التسميات مثل env وteam لاحقاً بواسطة محددات NetworkPolicy وأدوات تتبع التكاليف ولوحات المراقبة. إضافتها لاحقاً يُفضي إلى ثغرات صامتة في السياسات.

الخطوة 2 — كتابة ملف Deployment

يتطلب Deployment الإنتاجي أكثر من صورة حاوية وعدد نسخ. يحتاج إلى طلبات الموارد وحدودها حتى يتمكن المجدوِل من توزيع الأحمال بكفاءة ولا يُنهي نواة النظام pod عشوائياً بسبب نفاد الذاكرة. يحتاج إلى readinessProbe حتى لا يوجّه Kubernetes الحركة إلى نسخة بدأت لكنها لم تستعد بعد. ويحتاج إلى livenessProbe حتى تُعاد تشغيل العمليات المتوقفة تلقائياً. فضلاً عن استراتيجية RollingUpdate مضبوطة لضمان نشر بدون توقف.

# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: webapp namespace: webapp labels: app: webapp version: v1.0.0 spec: replicas: 3 selector: matchLabels: app: webapp strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # pod إضافي واحد أثناء النشر maxUnavailable: 0 # لا تقل عن العدد المطلوب — ضمان عدم التوقف template: metadata: labels: app: webapp version: v1.0.0 spec: containers: - name: webapp image: ghcr.io/yourorg/webapp:1.0.0 ports: - containerPort: 8080 resources: requests: cpu: "250m" memory: "256Mi" limits: cpu: "500m" memory: "512Mi" readinessProbe: httpGet: path: /healthz/ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3 livenessProbe: httpGet: path: /healthz/live port: 8080 initialDelaySeconds: 15 periodSeconds: 20 failureThreshold: 3 env: - name: APP_ENV value: "production" - name: LOG_LEVEL value: "info" terminationGracePeriodSeconds: 30
لا تتجاهل حدود الموارد في بيئة الإنتاج أبداً. Pod بلا حدود يمكنه استنزاف كل موارد CPU على العقدة وتجويع جميع الجيران. وبدون حد للذاكرة، يؤدي أي تسرب في الذاكرة إلى إنهاء العقدة نفسها بواسطة نواة النظام. ضع دائماً requests وlimits معاً. نقطة البداية: اجعل الحدود ضعف الطلبات المقاسة، ثم اضبط بعد اختبار الحمل.

الخطوة 3 — كشف التطبيق عبر Service

يمنح Service حجراتك عنوان IP ظاهرياً ثابتاً واسم DNS بصرف النظر عن العقدة التي تعمل عليها أو عدد مرات إعادة تشغيلها. استخدم ClusterIP (الافتراضي) للخدمات الداخلية، أو LoadBalancer عندما تحتاج إلى نقطة دخول خارجية. هنا نستخدم ClusterIP — في الكلاستر الحقيقي سيكون هناك Ingress أو موزع حمل خارجي أمامه.

# service.yaml apiVersion: v1 kind: Service metadata: name: webapp namespace: webapp labels: app: webapp spec: selector: app: webapp # يوجه إلى أي pod يحمل هذا التسمية ports: - name: http protocol: TCP port: 80 # المنفذ الذي يستمع إليه Service targetPort: 8080 # المنفذ داخل الحاوية type: ClusterIP
# تطبيق الملفات الثلاثة دفعة واحدة kubectl apply -f namespace.yaml -f deployment.yaml -f service.yaml # مراقبة انتشار النشر حتى الاكتمال kubectl rollout status deployment/webapp -n webapp # Waiting for deployment "webapp" rollout to finish: 0 of 3 updated replicas are available... # deployment "webapp" successfully rolled out # التحقق من حالة الـ pods (يجب أن تكون 3/3 Ready) kubectl get pods -n webapp -l app=webapp # التحقق من أن Service لديه Endpoints (pods سليمة خلفه) kubectl get endpoints -n webapp webapp
Deployment, Service, and Namespace layout Namespace: webapp Service ClusterIP :80 Deployment — 3 replicas (webapp:1.0.0) Pod A webapp :8080 Ready Pod B webapp :8080 Ready Pod C webapp :8080 Ready selector: app=webapp ReplicaSet owns Pods; Deployment owns ReplicaSet
يُنشئ Deployment مجموعة ReplicaSet تمتلك ثلاثة Pods؛ يوجّه Service الحركة إلى كل Pod سليم عبر محددات التسميات — كل ذلك داخل Namespace الخاص بالتطبيق.

الخطوة 4 — إجراء تحديث تدريجي (Rolling Update)

حين يدفع خط CI الخاص بك وسماً جديداً للصورة، حدّث الـ Deployment. بفضل maxUnavailable: 0، سيُشغّل Kubernetes الـ pod الجديد أولاً (maxSurge: 1) ولن يُنهي أياً من الـ pods القديمة إلا بعد أن تجتاز الـ pod الجديدة فحص الاستعداد. الحركة لن تنقطع أبداً.

# تحديث وسم الصورة — يُطلق عملية النشر فوراً kubectl set image deployment/webapp webapp=ghcr.io/yourorg/webapp:1.1.0 -n webapp # مشاهدة التقدم بشكل حي حتى الاكتمال kubectl rollout status deployment/webapp -n webapp --watch # التحقق: ReplicaSet الجديد نشط، القديم مُصفَّر kubectl get replicasets -n webapp # NAME DESIRED CURRENT READY AGE # webapp-6d8f9b7c4f 3 3 3 45s <-- جديد (v1.1.0) # webapp-5c7b8d6a3e 0 0 0 12m <-- قديم (v1.0.0)
يحتفظ Kubernetes بـ ReplicaSets القديمة افتراضياً (يتحكم فيها spec.revisionHistoryLimit، القيمة الافتراضية 10). هذا هو سر سرعة التراجع — ReplicaSet القديمة ما زالت موجودة وتحتاج فقط إلى إعادة تشغيلها. لا تضع revisionHistoryLimit: 0 في الإنتاج وإلا ستفقد قدرة التراجع كلياً.

الخطوة 5 — التراجع الطارئ (Rollback)

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

# عرض سجل النشر — يُدرج كل مراجعة مع سببها kubectl rollout history deployment/webapp -n webapp # REVISION CHANGE-CAUSE # 1 kubectl set image ... webapp:1.0.0 # 2 kubectl set image ... webapp:1.1.0 # التراجع إلى المراجعة السابقة مباشرة (الأكثر شيوعاً) kubectl rollout undo deployment/webapp -n webapp # أو التراجع إلى مراجعة محددة kubectl rollout undo deployment/webapp -n webapp --to-revision=1 # التأكد من اكتمال التراجع kubectl rollout status deployment/webapp -n webapp # التحقق من أن كل pod تعمل الآن بالصورة القديمة kubectl get pods -n webapp -o wide
ضع تعليقاً على كل نشر مع سبب التغيير. قبل تطبيق إصدار جديد، نفّذ: kubectl annotate deployment/webapp kubernetes.io/change-cause="deploy v1.1.0 — fix login bug" -n webapp. هذا يملأ عمود CHANGE-CAUSE في سجل النشر ويجعل الجداول الزمنية للحوادث وتقارير ما بعدها أسهل بكثير في القراءة.

قائمة مراجعة الإنتاج قبل الشحن

قبل أن تشحن أي فريق حقيقي Deployment إلى الإنتاج، يتحقق من التالي:

  1. طلبات الموارد وحدودها مضبوطة — CPU والذاكرة على كل حاوية.
  2. فحوصات الاستعداد والحيوية مهيأة — تشير إلى نقاط نهاية صحية سريعة وواعية بالتبعيات.
  3. استراتيجية التحديث تضمن عدم التوقفmaxUnavailable: 0 مع maxSurge: 1 على الأقل.
  4. وسم الصورة مثبّت بإصدار محدد أو رقم تشفيري — لا تستخدم :latest أبداً في الإنتاج.
  5. الأسرار ليست في متغيرات البيئة كنص صريح — استخدم Kubernetes Secrets أو خزنة خارجية.
  6. الـ Namespace لديه ResourceQuota — حتى لا يستنزف فريق واحد موارد الكلاستر بأكمله.
  7. PodDisruptionBudget (PDB) مُعرَّف — حتى يحترم تصريف العقد أثناء الترقيات اتفاقية مستوى الخدمة.

الملفات في هذا الدرس مبسّطة عمداً لأغراض التوضيح. في البنية التحتية الحقيقية، يُضيف Helm chart أو Kustomize overlay الحقول المتبقية تلقائياً.

ES
Edrees Salih
منذ ساعة

We are still cooking the magic in the way!