عمليات Kubernetes المتقدمة

استكشاف أعطال الكلاستر وإصلاحها

18 دقيقة الدرس 8 من 30

استكشاف أعطال الكلاستر وإصلاحها

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

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

عقلية الفرز: النطاق والطبقة والإشارة

قبل تشغيل أي أمر، ضع نفسك في سياق ثلاثة محاور:

  • النطاق: هل يشمل التأثير الكلاستر بأكمله، أم منطقةً بعينها، أم أنه محصور في نيمسبيس أو عبء عمل واحد؟ التأثير على مستوى الكلاستر يشير إلى مستوى التحكم أو مكوّن شبكي مشترك، أما العزل في نيمسبيس محدد فيشير إلى RBAC أو حصص أو ويب هوك قبول معطوب.
  • الطبقة: يجلس Kubernetes فوق نظام التشغيل وبيئة الحاويات وإضافة CNI وبنية مزود السحابة التحتية. تتصاعد الأعطال للأعلى، لذا دائمًا ابدأ بالطبقة التي تقع أسفل العرَض مباشرةً.
  • الإشارة: يُصدر Kubernetes إشارات غنية — الأحداث، وسجلات المكوّنات، وkubectl describe، والمقاييس، وسجلات التدقيق. أسرع مسار للوصول إلى السبب الجذري هو عادةً أحدث حدث من نوع Warning في تدفق أحداث الكائن المعني.
Kubernetes cluster troubleshooting triage tree Symptom Detected Pod / Node / Service Cluster-wide or isolated? kubectl get nodes / events Node NotReady Node Triage kubelet / runtime / OS Pod Pending Scheduler Triage resources / taints / affinity Running / no traffic Network Triage DNS / kube-proxy / CNI 1. journalctl kubelet 2. crictl ps / info 3. df -h / free -m 4. iptables / routes 1. describe pod Events 2. kubectl get events 3. quota / LimitRange 4. taints / node selectors 1. nslookup svc.ns 2. curl ClusterIP:port 3. iptables-save | grep svc 4. cni plugin logs Mitigate → Root Cause → Fix always document in incident log
شجرة الفرز: حدّد النطاق أولًا، ثم تعمّق في إشارات العقدة أو المُجدول أو الشبكة.

مشكلات العقد

تتوقف العقدة في حالة NotReady عن استقبال جدولة البودات الجديدة وتبدأ في إخلاء البودات الموجودة بعد انقضاء فترة التحمّل المضبوطة لـnode.kubernetes.io/not-ready (خمس دقائق افتراضيًا). الوقت حاسم، ابدأ من هنا.

# 1. اكتشاف العقد غير الجاهزة فورًا kubectl get nodes -o wide # 2. فحص حالات العقدة وأحداثها الأخيرة kubectl describe node <node-name> # ابحث عن: MemoryPressure, DiskPressure, PIDPressure, NetworkUnavailable # 3. SSH إلى العقدة وتحقق من صحة kubelet systemctl status kubelet journalctl -u kubelet --since "10 minutes ago" --no-pager | tail -80 # 4. تحقق من بيئة تشغيل الحاويات (containerd هي الافتراضية) systemctl status containerd crictl info # صحة بيئة التشغيل crictl ps -a # جميع الحاويات بما فيها المتعطلة # 5. فحص استنفاد الموارد df -h # القرص — استنفاد inode هاجس صامت free -m # الذاكرة cat /proc/sys/kernel/pid_max # حدود PID
استنفاد Inode: يمكن للعقدة أن تستنفد inodes قبل امتلاء سعة القرص. عند حدوث ذلك، لا يستطيع kubelet كتابة emptyDir أو ملفات السجل الخاصة بالبود وتنتقل العقدة إلى حالة DiskPressure. يُظهر df -i استخدام inode — هذا الفحص غائب دائمًا تقريبًا من روابط التشغيل المبتدئة.

أشيع الأسباب الجذرية لحالة NotReady: فشل تجديد شهادة kubelet (تحقق من /var/lib/kubelet/pki/)، وأذونات مقبس containerd، وعملية kubelet التي قتلها OOM، وانجراف NTP عند مزود السحابة مما يُسبب فشل التحقق من صحة الرمز المميز.

إخفاقات الجدولة

البود العالق في حالة Pending قَبِله خادم API لكن المُجدول لا يستطيع وضعه. يُسجّل المُجدول قراره في تدفق أحداث البود — هذا دائمًا أول مكان تبحث فيه.

# الأمر الأكثر فائدة لبود في حالة Pending kubectl describe pod <pod-name> -n <ns> # اقرأ قسم Events في الأسفل بعناية # الأحداث الأخيرة على مستوى الكلاستر (مرتبة بالتوقيت) kubectl get events --sort-by='.lastTimestamp' -A | grep -i warning # فحص قرارات المُجدول لبود محدد kubectl get events -n <ns> --field-selector involvedObject.name=<pod-name> # فحص حصص الموارد والاستخدام الحالي في نيمسبيس kubectl describe resourcequota -n <ns> kubectl describe limitrange -n <ns> # إيجاد العقد القادرة فعليًا على تلبية متطلبات البود kubectl get nodes -o json | \ kubectl-neat | jq '.items[] | {name:.metadata.name, allocatable:.status.allocatable}' # فحص الـ Taint — لماذا لا يُتسامح مع البود؟ kubectl describe node <node> | grep -A5 Taints

أكثر رسائل إخفاق الجدولة شيوعًا وأسبابها:

  • 0/5 nodes are available: insufficient memory — الطلبات تتجاوز السعة القابلة للتخصيص. إما تصحيح حجم الطلبات أو إضافة عقد.
  • 0/5 nodes are available: node(s) had untolerated taint — البود يفتقر لمدخل tolerations لـ taint مثل dedicated=gpu:NoSchedule.
  • 0/5 nodes are available: node(s) didn't match Pod's node affinity/selector — قواعد nodeSelector أو requiredDuringSchedulingIgnoredDuringExecution لا تتطابق مع تسميات أي عقدة.
  • 0/5 nodes are available: pod has unbound immediate PersistentVolumeClaims — قيود StorageClass أو المنطقة الجغرافية تمنع ربط PVC.
استخدم kubectl describe بدلًا من kubectl get -o yaml للأحداث. لا يحافظ YAML على ترتيب الأحداث ولا يتضمن السلاسل النصية المقروءة التي يُعبئها المُجدول. يجمع describe الأحداث والحالات في مخرج واحد قابل للقراءة، مما يجعل المسح أسرع بكثير خلال الحوادث.

أعطال الشبكة

تُعدّ أعطال الشبكة في Kubernetes أعقد الفئات لأنها تمتد عبر طبقات متعددة: إضافة CNI (Calico أو Cilium أو AWS VPC CNI)، وkube-proxy (أو بدائله المبنية على eBPF)، وCoreDNS، وجداول توجيه مزود السحابة. البود الذي يعمل لكن لا يمكن الوصول إليه — أو لا يمكنه الوصول إلى التبعيات — هو في الغالب مشكلة شبكية.

# الخطوة 1: التحقق من دقة DNS من داخل بود kubectl run netdebug --image=nicolaka/netshoot --rm -it --restart=Never -- \ nslookup kubernetes.default.svc.cluster.local # الخطوة 2: اختبار الاتصال المباشر بـ ClusterIP kubectl run netdebug --image=nicolaka/netshoot --rm -it --restart=Never -- \ curl -sv http://<ClusterIP>:<port>/healthz # الخطوة 3: التحقق من وجود قواعد iptables لـ kube-proxy للخدمة # (نفّذ على عقدة عبر kubectl أو SSH) iptables-save | grep <service-name> # يجب أن تظهر قواعد DNAT لكل Endpoint # الخطوة 4: فحص Endpoints التي تدعم الخدمة kubectl get endpoints <service-name> -n <ns> # Endpoints فارغة = عدم تطابق محدد التسمية بين الخدمة والبودات # الخطوة 5: فحص بودات CoreDNS kubectl get pods -n kube-system -l k8s-app=kube-dns kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50 # الخطوة 6: صحة إضافة CNI (مثال: Calico) kubectl get pods -n calico-system calicoctl node status # حالات نظير BGP على كل عقدة
Endpoints الفارغة هي السبب الجذري الأكثر شيوعًا لـ "الخدمة غير قابلة للوصول". تعني أن selector الخدمة لا يتطابق مع تسميات أي بود يعمل. يحدث هذا بعد أخطاء كتابة التسميات، أو عدم تطابق النيمسبيس، أو إطلاق يستخدم مخطط تسمية مختلفًا. تحقق دائمًا: kubectl get endpoints <svc> قبل الغوص في iptables أو تشخيصات CNI.

للأعطال المتقطعة — حيث ينجح بعض الطلبات ويفشل بعضها — تكون المشكلة عادةً بودًا واحدًا غير سليم خلف خدمة ذات نسخ متعددة. تحقق من استعداد كل بود وعمليات إعادة التشغيل الأخيرة:

# إظهار عدد إعادات التشغيل والاستعداد عبر نشر kubectl get pods -n <ns> -l app=<label> -o wide # متابعة سجلات جميع النسخ في آنٍ واحد kubectl logs -n <ns> -l app=<label> --prefix --tail=100 # وصف بود يتعطل باستمرار (OOMKilled, CrashLoopBackOff) kubectl describe pod <pod> -n <ns> # سجلات الحاوية السابقة (بعد التعطل) kubectl logs <pod> -n <ns> --previous

إشارات مستوى التحكم وetcd

إذا كان زمن الاستجابة لخادم API مرتفعًا، أو تستغرق الموارد عشرات الثواني للظهور بعد kubectl apply، أو تتأخر المتحكمات، فالمشكلة فوق طبقة العقدة. تحقق من مكوّنات مستوى التحكم مباشرةً:

  • kubectl get --raw /healthz — حيوية خادم API.
  • kubectl get --raw /readyz — استعداد خادم API (يتضمن فحص etcd).
  • في الكلاسترات المُدارة (EKS وGKE وAKS)، تُعرض مقاييس مستوى التحكم في وحدة تحكم السحابة؛ أما في الكلاسترات ذاتية الإدارة فاستخدم etcdctl endpoint health --cluster للتحقق من النصاب.
  • سجلات التدقيق لخادم API تلتقط كل تعديل — لا غنى عنها في مراجعات ما بعد الحادثة من نوع "من حذف ذلك المورد".
عادة الإنتاج: احتفظ بقالب بود تصحيح netshoot أو busybox في روابط التشغيل الخاصة بك. تتيح لك الحاويات المؤقتة (kubectl debug -it <pod> --image=nicolaka/netshoot --target=<container>) إرفاق مجموعة أدوات شبكية كاملة ببود يعمل دون تعديل النشر — ضرورية حين تحتاج للتصحيح داخل نفس نيمسبيس الشبكة للحاوية المعطوبة.

قائمة التحقق المنهجية للحل

  1. حدّد النطاق: بود واحد، أو عقدة واحدة، أو نيمسبيس واحد، أو على مستوى الكلاستر.
  2. اقرأ أحدث أحداث التحذير (kubectl get events -A --sort-by='.lastTimestamp').
  3. لمشكلات العقد: سجلات kubelet ← صحة بيئة التشغيل ← ضغط القرص والذاكرة وinode ← مسارات الشبكة.
  4. لمشكلات الجدولة: أحداث describe pod ← حصص الموارد ← taints/tolerations ← قواعد الانجذاب ← مخطط PVC.
  5. لأعطال الشبكة: تطابق محدد Endpoints ← CoreDNS ← قواعد kube-proxy ← إضافة CNI ← توجيه السحابة.
  6. خفّف أولًا (أعِد الجدولة، أضِف طاقة، أعِد تشغيل المكوّن) لاستعادة الخدمة، ثم ابحث عن السبب الجذري.
  7. اكتب وثيقة الحادثة: الجدول الزمني، والفرضيات المختبرة، والإصلاح المطبّق، وبنود المتابعة. هذا غير قابل للتفاوض على نطاق كبرى شركات التقنية لأن نفس فئة العطل ستتكرر لا محالة.