مشروع التخرج: منصة إنتاج بمستوى الشركات الكبرى

منصة Kubernetes

22 دقيقة الدرس 4 من 30

منصة Kubernetes

بعد الانتهاء من إعداد الحسابات والشبكة، تُشكّل طبقة Kubernetes المجال الذي تستثمر فيه فرق المنصة أكبر قدر من رأس المال الهندسي. في Google وNetflix وAirbnb وShopify، قرارات بنية الكلاستر التي اتُّخذت في الشهر الأول لا تزال تشتغل—ويُحيَّل إليها مراراً—بعد ثلاث سنوات. يتناول هذا الدرس ثلاثة قرارات بالغة الأهمية على مستوى المؤسسات الكبرى: كيفية هندسة Control Plane، وكيفية تصميم أسطول الـ Nodes، وكيفية رسم خريطة طوبولوجية لبيئات متعددة عبر الكلاسترات.

بنية Control Plane

في الخدمات المُدارة—EKS وGKE وAKS—يكون الـ Control Plane مُدار من قِبل مزود السحابة، لكنه لا يعني انعدام الإعدادات. القرارات التي تبقى بيدك ذات أثر بالغ:

  • Endpoint خاص فقط. تستخدم شركات التقنية الكبرى نقاط نهاية API خاصة حصراً للكلاسترات الإنتاجية. لا يمكن الوصول إلى API Server إلا من داخل الـ VPC. يتصل مهندسو CI/CD عبر Bastion أو AWS Systems Manager Session Manager. أي Endpoint عام هو سطح هجوم لا فائدة منه متى توفّر لديك VPN أو سياسة SSM صحيحة.
  • دورة إصدارات الكلاستر. يدعم EKS إصدارين فرعيين سابقين؛ تحتاج إلى مسار ترقية مُختبَر كل 14 أسبوعاً. استخدم ترقية Blue/Green للكلاستر—تهيئة نسخة جديدة، نقل الحركة عبر DNS موزون، ثم تصفية النسخة القديمة—بدلاً من الترقية في الموضع. الترقيات في الموضع على الكلاسترات الكبيرة تُظهر باستمرار Admission Webhooks غير متوافقة وAPIs مهجورة.
  • إدارة الإضافات عبر IaC. كل إضافة جوهرية—CoreDNS وkube-proxy وVPC CNI وcluster autoscaler وcert-manager وexternal-dns—يجب إدارتها من خلال طبقة IaC، وليس بأوامر kubectl apply المتفرقة. الإضافات غير المُدارة تنحرف عن حالتها المطلوبة في غضون أسابيع.
ETCD هو الكلاستر بحد ذاته. في Control Planes المُدارة ذاتياً، يجب تشغيل ETCD على Nodes مخصصة بتخزين SSD، وخمسة أعضاء على الأقل لتحقيق النصاب، ونسخ احتياطية آلية إلى تخزين كائنات دائم كل 30 دقيقة. على EKS/GKE هذا مُجرَّد، لكن لا تزال بحاجة إلى معرفة أن الكلاسترات الكبيرة تصطدم بحدود حجم ETCD. احذف الأحداث بانتظام باستخدام kubectl delete events --all -A وراقب تضخم تخزين CRD.

استراتيجية الـ Nodes: تركيبة الأسطول

تعامل مع اختيار الـ Nodes باعتباره مسألة تحسين بين التكلفة والأداء، لا مجرد توفير سعة. الهدف هو تقليل إجمالي ساعات vCPU المستهلكة لإنتاجية عمل معطاة. يُرتِّب تصميم أسطول الإنتاج عادةً طبقات متعددة من الـ Nodes:

  • General Purpose (m6i, n2-standard): منطقة الهبوط الافتراضية للخدمات الدقيقة المتنوعة. النسبة 4:1 بين الذاكرة والمعالج تناسب معظم الـ Pods. استخدمها كقاعدة on-demand يهيئها Karpenter أولاً.
  • Compute-Optimised (c6i, c2-standard): بوابات API المرهِقة للمعالج، إنهاء TLS، المهام الدُفعية كثيفة الضغط. يتجنب دفع تكلفة ذاكرة لن تُستخدم.
  • Memory-Optimised (r6i, m1-ultramem): الخدمات الثقيلة على JVM، وسطاء Kafka داخل الكلاستر، ذاكرة التحليلات الـ In-Memory.
  • Spot / Preemptible: الأحمال عديمة الحالة ومجموعات مشغّلات CI—توفير يصل إلى 80%. اقرن Nodes الـ Spot بـ PodDisruptionBudget وtopologySpreadConstraints لمنع استرداد الـ Spot من إخلاء جميع النسخ في آنٍ واحد.

Node Groups المُدارة مقابل Karpenter. Node Groups المُدارة متوقعة لكنها بطيئة: 2-3 دقائق لتهيئة Node جديد. يهيئ Karpenter Nodes في نحو 30 ثانية باستدعاء EC2 API مباشرةً. تقوم خوارزمية الدمج (Consolidation) بتصغير حجم الـ Nodes الأقل استخداماً، موفّرةً 15-25% من تكلفة الحوسبة دون أي تحسين يدوي.

# Karpenter NodePool — طبقة General-Purpose للإنتاج apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: general spec: template: metadata: labels: node-class: general spec: nodeClassRef: apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["on-demand", "spot"] - key: node.kubernetes.io/instance-type operator: In values: ["m6i.xlarge","m6i.2xlarge","m6i.4xlarge", "m6a.xlarge","m6a.2xlarge","c6i.2xlarge"] - key: topology.kubernetes.io/zone operator: In values: ["us-east-1a","us-east-1b","us-east-1c"] disruption: consolidationPolicy: WhenUnderutilized consolidateAfter: 30s limits: cpu: "800" memory: 3200Gi --- # GPU Pool — مُقيَّد؛ فقط Pods التي تتحمله تهبط هنا apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-batch spec: template: spec: nodeClassRef: apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass name: default requirements: - key: node.kubernetes.io/instance-type operator: In values: ["g5.2xlarge","g5.4xlarge","g5.8xlarge"] - key: karpenter.sh/capacity-type operator: In values: ["spot"] taints: - key: nvidia.com/gpu effect: NoSchedule limits: cpu: "192" memory: 768Gi

طوبولوجيا البيئات المتعددة

تتقارب فرق المنصة في شركات التقنية الكبرى نحو أحد نمطين للبيئات المتعددة:

النمط أ — كلاستر لكل بيئة. كلاسترات منفصلة لـ dev وstaging وprod. هذا هو النمط السائد في الشركات ذات المتطلبات الأمنية الصارمة (SOC2، PCI-DSS، HIPAA). عزل نطاق الضرر يكون في أقصى درجاته؛ إعداد RBAC خاطئ أو Operator مارق في بيئة dev لا يمكنه لمس الإنتاج. التكلفة هي العبء التشغيلي: ثلاث دورات ترقية للكلاستر، وثلاث مجموعات إضافات للصيانة.

النمط ب — عزل بالـ Namespace داخل كلاستر واحد. بيئات متعددة تتشارك كلاستراً واحداً، مفصولة بـ Namespaces وNetworkPolicy. هذا ممكن لبيئات dev وstaging، لكن أحمال الإنتاج التي تعالج بيانات العملاء أو المدفوعات يجب أن تعيش في كلاستر مخصص بسبب المتطلبات التنظيمية.

الطوبولوجيا المعيارية لفريق مكوّن من 10-50 مهندساً هي هجينة: كلاستر الخدمات المشتركة (أدوات داخلية، مشغّلات CI، مكدس المراقبة، ArgoCD)، وكلاستر غير الإنتاج (dev + staging بعزل Namespace)، وكلاستر إنتاج واحد أو أكثر (واحد لكل منطقة).

سمِّ الكلاسترات بحسب الغرض، لا رقم الإصدار. استخدم أسماء مثل platform-shared-use1 وworkloads-nonprod-use1 وworkloads-prod-use1. الكلاسترات المُسمّاة بالإصدار (eks-v127-prod) تخلق ارتباكاً خلال ترقيات Blue/Green حين تتواجد كلتا النسختين في kubeconfig في آنٍ واحد.
Multi-environment Kubernetes cluster topology Shared Services Non-Prod Production platform-shared-use1 ArgoCD Prometheus / Thanos Grafana Vault (external secrets) CI Runners (Karpenter spot) cert-manager / external-dns workloads-nonprod-use1 ns: dev (loose ResourceQuota) ns: staging (mirrors prod config) NetworkPolicy: ns-isolation Karpenter (spot-first) Argo Rollouts (canary) workloads-prod-use1 Private API endpoint only Karpenter (on-demand base) OPA Gatekeeper policies Argo Rollouts (blue/green) PodDisruptionBudgets VPA + HPA (dual autoscaling) GitOps deploy
طوبولوجيا ثلاثة كلاسترات: مركز الخدمات المشتركة ينشر عبر GitOps إلى كلاستري غير الإنتاج والإنتاج.

عزل الأحمال: Taints وTopology Spread

Node Pools وحدها لا تكفي. داخل الكلاستر، يجب التحكم في أي Pods تهبط على أي Nodes وكيف تنتشر عبر نطاقات الفشل. تعمل ثلاثة عناصر أولية معاً:

  • Taints + Tolerations — إبعاد الأحمال عن Nodes غير المُهيَّأة لها (مثال GPU Pool أعلاه).
  • Node Affinity — قواعد ناعمة أو صارمة لتوجيه Pods نحو فئات Node معينة.
  • TopologySpreadConstraints — العنصر الأكثر إهمالاً في الإنتاج. يضمن قيد maxSkew: 1 عبر topology.kubernetes.io/zone توزيع النسخ على مناطق التوفر. بدونه، يضع المُجدوِل الافتراضي Pods على Nodes المتاحة وقد تنتهي جميع النسخ في منطقة توفر واحدة—مما يجعل الخدمة غير متاحة عند فشل منطقة.
# Deployment للإنتاج — قيود الانتشار + Node Affinity apiVersion: apps/v1 kind: Deployment metadata: name: payment-api namespace: prod spec: replicas: 6 template: spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: payment-api - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: payment-api affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: node-class operator: In values: [general] containers: - name: payment-api resources: requests: cpu: "500m" memory: "512Mi" limits: memory: "1Gi" # لا حد للـ CPU — يتجنب التقنين
لا تضع حدوداً للـ CPU في الإنتاج أبداً. حدود CPU تُطبِّق التقنين (Throttling) على مستوى cgroup حتى حين تكون هناك سعة فائضة في الـ Node. Pod مقيَّد بـ limits.cpu: 500m سيُقنَّن عندما يرتفع الحِمل إلى 600m بغض النظر عن حِمل الـ Node—يظهر هذا على شكل ارتفاع في زمن الاستجابة يصعب تشخيصه. ضع requests للـ CPU لأغراض الجدولة واترك الـ Pod يزيد استهلاكه بحرية. ضع حدوداً للذاكرة فقط لأن الذاكرة غير قابلة للضغط.

التحجيم التلقائي: VPA وHPA وKEDA

تستخدم كلاسترات المؤسسات الكبرى الأدوات الثلاثة لأغراض مختلفة؛ فهي ليست بدائل لبعضها بل تكاملية:

  • HPA: تحجيم النسخ بناءً على CPU أو الذاكرة أو مقاييس مخصصة. المعيار للخدمات عديمة الحالة. استهدف 65-70% استخدام CPU؛ الاستهداف الأعلى يسبب تذبذباً في ظل حركة مرور متقطعة.
  • VPA: يعمل في وضع التوصية فقط في الإنتاج. لا تُفعِّل التطبيق التلقائي؛ فهو يُخلي Pods لإعادة تحجيمها. استخدم توصيات VPA لتحديث طلبات الموارد الثابتة في دورة النشر التالية.
  • KEDA: التحجيم بناءً على مصادر أحداث خارجية: عمق قائمة انتظار SQS، تأخر مستهلك Kafka، نتائج استعلام Prometheus. لا غنى عنه للأحمال غير المتزامنة حيث لا تعكس CPU والذاكرة الحِمل الفعلي.
هيئ الـ HPA بصواب منذ البداية. اضبط --horizontal-pod-autoscaler-initial-readiness-delay=30s و--horizontal-pod-autoscaler-cpu-initialization-period=5m في إعدادات Controller Manager. بدون ذلك، يحتسب HPA Pods المُشغَّلة حديثاً—التي لم تُدفّئ بعد JVM أو connection pools الخاصة بها—كأحمال منخفضة ويُقلِّص الحجم مبكراً ثم يعود فيوسّعه، مسبّباً حلقة تذبذب تظهر كـ 503s دورية.