استخدام Services لربط التطبيقات وعمل موازنة أحمال (Load Balancing)
استخدام Services لربط التطبيقات وعمل موازنة أحمال Load Balancing
عند تشغيل تطبيقاتك على Kubernetes فإن الحاويات لا تعيش بهوية شبكية ثابتة. أي Pod قد يُحذف ويُعاد إنشاؤه بعنوان IP مختلف تماماً. هنا يظهر الدور الحقيقي لـ Services باعتبارها طبقة ربط مستقرة بين المستهلكين والنسخ المتغيرة من التطبيق.
إذا كنت قد قرأت مقال ما هو Kubernetes (K8s)؟ ومتى ننتقل من Docker Compose إليه؟ فستعرف أن الانتقال إلى هذا العالم لا يتعلق فقط بالتشغيل، بل بكيفية بناء شبكة داخلية موثوقة وقابلة للتوسع. كما أن فهم Pods, Nodes, Clusters هو الأساس قبل استيعاب لماذا تُعد Service عنصراً معمارياً لا غنى عنه.
ما هي Kubernetes Service فعلياً؟
هي كائن شبكي يوفّر نقطة وصول مستقرة لمجموعة من Pods يتم اختيارها عبر labels. بدلاً من أن يتصل تطبيق frontend مباشرة بعنوان متغير لحاوية backend، فإنه يتصل باسم منطقي ثابت مثل api-service.
هذا التجريد يحقق فائدتين محوريتين:
- الربط الداخلي بين التطبيقات عبر
Service Discovery. - توزيع الحركة على عدة نسخ من التطبيق لتحقيق
Load Balancing.
لماذا لا يكفي استخدام Pods مباشرة؟
لأن Pods كيانات مؤقتة بطبيعتها. عند إعادة الجدولة أو التوسعة أو التحديث التدريجي Rolling Update ستتغير العناوين. أي ربط مباشر يعني هشاشة عالية، وتعقيداً في التكوين، واحتمال توقف الخدمات عند أول عملية نشر.
هذا مشابه للمشكلة التي عالجها Docker Networks داخل البيئات الأصغر، لكن Kubernetes يضيف طبقة أكثر نضجاً للتوجيه والاكتشاف والتوازن.
الأنواع الأساسية من Services
ClusterIP
النوع الافتراضي، ويستخدم لتمكين الاتصال الداخلي فقط داخل الـ Cluster. مثالي عندما تريد من تطبيق frontend الوصول إلى backend أو من backend إلى قاعدة بيانات داخلية.
NodePort
يفتح منفذاً ثابتاً على كل Node لتمكين الوصول الخارجي. يُستخدم غالباً للاختبار أو البيئات الصغيرة، لكنه ليس الخيار المثالي للإنتاج عندما تحتاج إلى إدارة شهادات TLS ومسارات متعددة.
LoadBalancer
في البيئات السحابية، يطلب هذا النوع من المزود إنشاء موزع أحمال خارجي وربطه بالخدمة. هذا يختصر كثيراً من التعقيد، خاصة عند النشر على مزودات تدعم التكامل المباشر مع AWS أو GCP.
مثال عملي: ربط Deployment بخدمة داخلية
إذا كنت قد تابعت مقال كتابة أول ملف Deployment لتشغيل تطبيقك على نظام Kubernetes فستعرف أن الخطوة التالية الطبيعية هي كشف هذه النسخ عبر Service.
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
spec:
replicas: 3
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: myorg/backend:1.0
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
في هذا المثال، الخدمة لا تهتم بأي نسخة فعلية تستقبل الطلب. ما يهمها فقط هو العثور على كل Pod يحمل الوسم app: backend ثم تمرير الطلبات إليه.
كيف تتم Load Balancing داخلياً؟
عند وصول الطلب إلى Service تقوم مكونات الشبكة داخل Kubernetes مثل kube-proxy بإنشاء قواعد توجيه نحو مجموعة من Endpoints. هذه المجموعة تمثل النسخ الحية الجاهزة لاستقبال الترافيك.
هذا يعني أن زيادة عدد النسخ من 3 إلى 10 لا تتطلب تعديل التطبيق المستهلك. فقط توسّع Deployment، وستتكفل الخدمة بتوزيع الأحمال تلقائياً. هذه الفكرة هي قلب الأنظمة القابلة للتوسع الأفقي Horizontal Scaling.
إتاحة التطبيق خارجياً باستخدام LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
هذا التكوين مناسب للتطبيقات التي يجب أن تكون متاحة من الإنترنت. في السحابة سيجري إنشاء عنوان خارجي وربطه بالخدمة، ثم توزيع الحركة على النسخ الخلفية. وغالباً ما يُستخدم هذا النمط مع خطوط CI/CD الآلية مثلما ناقشنا في ما هو الـ CI/CD؟ ولماذا نؤتمت عمليات اختبار ونشر الأكواد؟ حتى يصبح النشر والتوسعة والتحديث عملية موحدة وقابلة للتكرار.
لا تجعل كل خدمة من نوع
LoadBalancerفي بيئة الإنتاج بدون حاجة واضحة. هذا يرفع التكلفة، ويزيد سطح الهجوم، ويعقّد التحكم في المنافذ والشهادات. الأفضل غالباً استخدام نقطة دخول مركزية مع سياسات وصول محكمة.
أفضل الممارسات المعمارية والأمنية
1) استخدام labels دقيقة
أي خطأ في المطابقة بين selector ووسوم الحاويات قد يربط الخدمة بنسخ خاطئة أو لا يربطها بأي شيء. لذلك صمم وسومك بناءً على البيئة والدور والإصدار إن لزم.
2) ربط الجاهزية بـ readiness probes
الخدمة يجب أن توجّه الترافيك فقط إلى النسخ الجاهزة. بدون ذلك قد تصل الطلبات إلى تطبيق لم ينته من الإقلاع أو لا يزال يحمّل الاتصال بقاعدة البيانات.
3) عزل الطبقات الحساسة
اجعل قواعد البيانات والخدمات الداخلية الحساسة وراء ClusterIP فقط، ولا تعرّضها مباشرة للإنترنت. هذا ينسجم مع مبدأ أقل صلاحية وأقل انكشاف شبكي.
قبل أي تحديث إنتاجي، اختبر تأثير تغيير
Service selectorsأو المنافذ في بيئة مرحلية. خطأ بسيط في المنفذtargetPortقد يسببDowntimeكاملاً رغم أن الحاويات نفسها تعمل.
كيف ينسجم ذلك مع الأتمتة وعمليات النشر؟
في البيئات الاحترافية، لا يتم إنشاء خدمات Kubernetes يدوياً على الخادم. بل تُدار عبر ملفات YAML محفوظة في المستودع، وتُنشر تلقائياً ضمن Pipeline. وهذا يتكامل مباشرة مع مفاهيم بناء أوامر YAML لأتمتة تشغيل السكربتات التلقائية عند كل Push.
كما يمكن استخدام Terraform لإنشاء طبقات الشبكة أو موزعات الأحمال السحابية، ثم استخدام Kubernetes manifests لربط التطبيقات فوقها. هذا ينسجم مع فلسفة Infrastructure as Code حيث تصبح البنية والشبكة والتوجيه جزءاً من الكود القابل للمراجعة والإرجاع والتتبع.
الخلاصة
تُعد Services في Kubernetes أكثر من مجرد وسيلة كشف منفذ. إنها طبقة استقرار وربط واكتشاف وتوزيع أحمال بين مكونات التطبيق. من دونها يصبح كل microservice معتمداً على عناوين متغيرة ومعرضاً لانقطاعات غير متوقعة.
عندما تصمم خدماتك بعناية، وتربطها مع Deployments صحيحة، وتضيف جاهزية ومراقبة وأتمتة نشر، فإنك تبني منصة قادرة على التوسع الآمن، وتحمل الضغط، وتقليل مخاطر التوقف. وهذا بالضبط ما يميز البنية الحديثة في عالم DevOps.
2 comments