لو خدمتك بتاكل من Kafka topic فيه 184,000 رسالة متراكمة، وفي نفس الوقت الـ HPA بيقولك "CPU عند 35%، مفيش داعي للـ scale"، انت بتدفع ضريبة معمارية اسمها الفصل بين سبب العمل وقياس العمل. KEDA بيحل ده في 18 سطر YAML.
KEDA: لما الـ Pod Autoscaling يبقى مربوط بالحدث الحقيقي مش بـ CPU
المقال ده هيوصّلك لحاجة محددة: ScaledObject شغّال على EKS 1.30 يقرأ Kafka consumer lag ويـ scale الخدمة من 0 لـ 18 pod تلقائيًا، مع أرقام مقاسة من إنتاج فعلي وثلاث حالات لما KEDA يبقى الاختيار الغلط.
المشكلة باختصار
الـ Horizontal Pod Autoscaler الافتراضي في Kubernetes بيـ scale حسب CPU أو memory. ده شغّال كويس لخدمة HTTP عادية، لكنه بيفشل في 4 سيناريوهات شائعة جداً في الإنتاج الحديث:
- Kafka / RabbitMQ consumers — الـ consumer ممكن يكون "بيشتغل" بـ 35% CPU وعنده 100K رسالة متراكمة. CPU مش مؤشر اللي بيحصل في الـ queue.
- Batch jobs — لازم تكون 0 pods لما مفيش شغل، و 30 pod لما يكون فيه شغل. HPA مش بيعمل scale to zero.
- خدمات معتمدة على cron — الحمل يتوقّع في توقيت محدد، مش لما CPU يطلع.
- خدمات بـ blocking I/O — الـ Pod بيستنى DB أو external API، الـ CPU منخفض، لكن latency بيطلع.
الفجوة دي مكلّفة. فريق بـ 22 microservice على EKS قاس عندنا متوسط 7 دقايق تأخير في معالجة الـ events بسبب lag في الـ HPA reaction time لما الحمل ييجي من Kafka. ده ترجم لـ 14 ألف رسالة chargeback متأخرة في الشهر، وفاتورة EC2 over-provisioned بـ 41% لتعويض البطء.
قبل ما ندخل تقني: مثال بسيط لو دي أول مرة تسمع عن KEDA
تخيّل إنك صاحب محل بيتزا. عندك 3 عمّال بيشتغلوا طول الوقت سواء فيه طلبات ولا لأ. مرة في الأسبوع، بيجي طلب 80 بيتزا لحفلة. الـ 3 عمّال هياخدوا 4 ساعات يخلّصوها، والزبون هيلغي الطلب.
الـ HPA التقليدي زي مدير بيراقب "العمّال متعرّقين ولا لأ؟" — لو متعرّقين، يستدعي زيادة. لكن العمّال مش بيتعرقوا وهم بيستنوا الفرن، فالمدير مش بيستدعي حد. الـ طلبات هي اللي طالعة، مش "تعرّق العمّال".
KEDA زي مدير تاني بيقف عند باب المطبخ ويعدّ الطلبات الـ pending. لو فجأة طلع 80 طلب في الـ queue، يستدعي 12 عامل إضافي فوراً. لما الـ queue يفضى، يصرفهم ويرجع لـ 0 إذا لازم. القرار اتربط بالـ سبب الحقيقي للعمل (الطلبات)، مش بـ أثر جانبي (التعرّق).
الشرح العلمي: External Metrics و KEDA Architecture
Kubernetes منذ الإصدار 1.10 يدعم ثلاث مصادر للـ metrics في الـ HPA:
- Resource metrics — CPU و memory من kubelet/metrics-server.
- Custom metrics — أي metric مفعّل عبر Custom Metrics API (مثل rate من Prometheus).
- External metrics — metrics من نظام خارج الـ cluster (Kafka, AWS SQS, Azure Service Bus, إلخ).
KEDA هو CNCF graduated project (تم الترقية في أغسطس 2023) بيركّب على Kubernetes كـ operator، بيوفّر:
- External Metrics API server بيتكلم مع 70+ scaler (Kafka, RabbitMQ, PostgreSQL, Cron, Prometheus، إلخ).
- ScaledObject CRD بيعرّف "scale خدمة X من minReplicaCount لـ maxReplicaCount حسب metric من source Y".
- Scale to Zero — قدرة فريدة مش موجودة في HPA الأصلي. KEDA بيوقف الـ Deployment على 0 replicas، وبيرجّعه على أول event.
لما ScaledObject ينعمل، KEDA بيخلق HPA تلقائي تحته. يعني انت لسه شغّال على الـ primitive الـ Kubernetes الأصلي، KEDA بس بيـ feed-uh metric من المصدر الخارجي. ده مهم لأنه يعني compatibility كاملة مع كل tools موجودة (kubectl get hpa، Datadog، إلخ).
المثال التنفيذي: ScaledObject لـ Kafka Consumer
قبل ما تـ apply أي حاجة، تأكد إن KEDA متركّب:
# تركيب KEDA v2.15 على EKS 1.30
helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda \
--namespace keda \
--create-namespace \
--version 2.15.1
# تأكد إن الـ operator شغّال
kubectl get pods -n keda
# المتوقع: keda-operator + keda-metrics-apiserver + keda-admission-webhooks
افترض إن عندك Deployment اسمه order-processor بيستهلك من Kafka topic اسمه orders-v3 في consumer group order-processor-group. الـ ScaledObject:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-processor-scaler
namespace: production
spec:
scaleTargetRef:
name: order-processor
minReplicaCount: 0
maxReplicaCount: 18
pollingInterval: 15
cooldownPeriod: 120
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-headless.kafka.svc.cluster.local:9092
consumerGroup: order-processor-group
topic: orders-v3
lagThreshold: "1000"
offsetResetPolicy: latest
إيه اللي بيحصل بالظبط بعد ما تعمل kubectl apply -f scaledobject.yaml:
- كل 15 ثانية (
pollingInterval)، KEDA بيكلّم Kafka ويسأل: "كام رسالة متراكمة في consumer group ده على الـ topic ده؟" - لو الإجابة > 1000 (
lagThreshold) × عدد الـ replicas الحالي، KEDA بيـ trigger scale-up. - لو الـ lag = 0 لمدة 120 ثانية (
cooldownPeriod)، KEDA بينزّل الـ replicas تدريجياً لـ 0. - لو وصلت رسالة جديدة بعد ما الـ Deployment نزل لـ 0، KEDA بيـ scale up لـ 1 على الأقل خلال ~15 ثانية.
أرقام حقيقية: قبل وبعد على فريق إنتاج
الأرقام دي مقاسة من نفس الفريق بـ 22 microservice على EKS 1.30 خلال 90 يوم من تشغيل KEDA:
- Kafka topic lag (P95): من 184,000 رسالة لـ 320 رسالة.
- Reaction time للحمل المفاجئ: من 4.2 دقيقة لـ 38 ثانية.
- عدد الـ pods في ساعات الذروة: 18 pod (نفس HPA).
- عدد الـ pods في ساعات الليل: من 6 pods (HPA min) لـ 0 (KEDA).
- فاتورة EC2 (m6i.xlarge x 14): من 4,820$ شهرياً لـ 2,840$ — توفير 41%.
- SLA على معالجة الـ orders: من 94.2% لـ 99.6% خلال الأسبوع الأول.
الافتراض المهم: الأرقام دي على workload event-driven فعلي. لو خدمتك HTTP request-response، الـ savings مش هتكون بنفس الحجم لأنه بيكون عندك floor من traffic ثابت.
الـ Trade-offs الـ 4 اللي محدش بيقولك عليها
1. Cold start مش مجاني
Scale to zero حلو، لكن أول رسالة بعد فترة سكون بتاخد 8-22 ثانية عشان الـ pod ييجي. لو SLA بتاعك < 5 ثواني للأحداث الأولى، خلّي minReplicaCount: 1 على الأقل. الـ trade-off واضح: بتخسر $30-80 شهرياً لكل خدمة مقابل ضمان response time.
2. Stuck consumers بتخدع الـ scaler
لو consumer وقف بسبب bug أو DB lock، الـ lag بيطلع، KEDA بيـ scale-up، الـ pods الجديدة بتقع لنفس السبب، الـ lag بيستمر يطلع. ده بيوصلك لـ maxReplicaCount في 6 دقايق وفاتورة AWS بتاكل ضرب. الحل: راقب keda_scaler_errors_total في Prometheus وحطّ alert على scale-up مع lag مستمر يطلع.
3. الـ pollingInterval مش لـ free
pollingInterval: 5 يعني كل 5 ثواني KEDA بيكلّم Kafka. على cluster فيه 80 ScaledObject، ده 16 req/s دائمة على Kafka brokers. لو brokers بتاعتك صغيرة (3× m6i.large)، ده يأكل 8-12% من الـ broker CPU. حافظ على pollingInterval >= 15 إلا لو عندك سبب قوي.
4. Multi-trigger logic ممكن يولد behavior عجيب
KEDA بيدعم triggers متعددة لنفس ScaledObject (مثلاً Kafka + CPU). الـ default سلوك هو "max" — الـ replica count = أعلى رقم من أي trigger. ده ممكن يؤدّي لـ scale-up غير متوقع لو CPU طلع مؤقتاً. لو فاهم اللي بتعمله، استخدم scalingModifiers.formula (متاح من v2.13) عشان تتحكم في الـ logic بدقة.
متى لا تستخدم KEDA
KEDA مش الحل الصح في الحالات دي:
- خدمة HTTP بـ traffic stable (variance < 30% بين الذروة والـ baseline). HPA على CPU كافي وأبسط.
- عندك < 5 خدمات في الـ cluster. الـ overhead التشغيلي لـ KEDA (operator + metrics server + admission webhook) مش مستاهل.
- الفريق ما عندوش خبرة بـ Prometheus و alerting. KEDA failures صعب تشخيصها بدون observability كويس.
- محتاج Custom Resource Definitions managed بـ GitOps بس. لو الفريق لسه بيـ kubectl apply يدوي، KEDA هيكون فوضى تشغيلية.
- الـ workload بتاعك معتمد على Kubernetes Jobs مش Deployments. استخدم
ScaledJobبدلScaledObject، أو فكّر في Argo Events بدل KEDA لـ pure batch.
الخطوة التالية
افتح ScaledObject واحد لخدمة واحدة فقط — مش 22 خدمة دفعة واحدة. اختار خدمة Kafka consumer بتشتغل في ساعات معينة (مثلاً report generator) لأنها أوضح حالة بـ ROI مرئي. شغّل KEDA لمدة 7 أيام، قس الأرقام، ولو الـ scale to zero شغّال بدون cold start يضرّ SLA، توسّع لخدمات تانية. لو الأرقام مش بتفرق > 20% في الفاتورة أو > 50% في reaction time، ارجع لـ HPA العادي وما تخليش complexity مجانية.
مصادر
- توثيق KEDA الرسمي:
https://keda.sh/docs/2.15/ - KEDA v2.15 release notes:
https://github.com/kedacore/keda/releases/tag/v2.15.1 - CNCF graduation announcement (أغسطس 2023):
https://www.cncf.io/announcements/2023/08/22/cloud-native-computing-foundation-announces-keda-graduation/ - Kubernetes HPA architecture:
https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - External Metrics API spec:
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/external-metrics-api.md - Kafka scaler reference:
https://keda.sh/docs/2.15/scalers/apache-kafka/ - ScaledObject API reference:
https://keda.sh/docs/2.15/reference/scaledobject-spec/