مستوى المقال: مبتدئ — مناسب لك لو لسه في أول طريقك مع Kubernetes وحاويات Docker.
لو لقيت الـ Pod بتاعك بيموت ويرجع تاني كل شوية، وفي حالته مكتوب OOMKilled، المقال ده هيوريك السبب بالظبط، وإزاي تمنعه بإعداد من 4 أسطر. الافتراض إن عندك صلاحية تعدّل ملفات الـ Deployment وتشغّل أوامر kubectl على الـ cluster.
ليه الـ Pod بيموت بـ OOMKilled في Kubernetes وإزاي تمنعه
المشكلة باختصار
تخيّل خدمة تقارير في شركة fintech، شغّالة بـ 6 نسخ على نود ذاكرتها 8Gi، ومحدّش حاطط لها سقف ذاكرة. نسخة واحدة فيها تسريب ذاكرة بسيط. مع الوقت بتاكل لحد 6.8Gi على النود لوحدها.
اللي بيحصل فعلاً وقتها مش إنها هي بس اللي تقع. الـ kernel على مستوى النود بيبدأ يقتل عمليات تانية علشان يفضّي ذاكرة، فبتلاقي 4 خدمات تانية اترمت معاها بدون أي ذنب. النتيجة: حوالي 9 دقايق downtime و~12,000 طلب رجّع 5xx. المشكلة مش في حجم النود، المشكلة إن مفيش حد قايل للحاوية "متعديش الحد ده".
الذاكرة في Kubernetes: الفكرة بمثال، وبعدين بالتعريف الدقيق
تخيّل مطعم. لما تحجز ترابيزة، المطعم بيضمنلك إنها متاحة مهما اتأخرت — ده الـ request. لكن في حد أقصى لعدد الكراسي اللي تقدر تزوّدها على الترابيزة قبل ما الإدارة توقفك — ده الـ limit. لو فضلت تجيب كراسي أكتر من الحد، الأمن بيطلعك بره على طول، مش بيتفاوض معاك.
الحاوية بالظبط كده. الـ request ذاكرة محجوزة ليها مهما حصل، والـ limit سقف لو عدّاه، النظام بيقتل العملية فورًا.
بالتفاصيل دلوقتي: Kubernetes بيركّب على آلية في نواة لينكس اسمها cgroups (control groups). لكل حاوية بيتعمل cgroup بسقف ذاكرة يساوي الـ limit. أول ما الذاكرة المستخدمة فعليًا (الـ RSS أساسًا) تعدّي السقف، الـ OOM killer بتاع الـ kernel بيقتل العملية بإشارة SIGKILL. علشان كده بتلاقي كود الخروج 137 بالظبط، وهو 128 + 9، والـ 9 هي رقم إشارة SIGKILL.
أما الـ request فمالوش علاقة بالقتل خالص. الـ scheduler بيستخدمه علشان يقرر الـ Pod يتحط على أنهي نود — يعني هو "حجز" بيضمن مكان، مش سقف.
الحل: اضبط requests و limits على الحاوية
- اعرف الاستهلاك الطبيعي للخدمة تحت حمل حقيقي (الخطوة دي قبل أي رقم).
- حط
requests.memory= الاستهلاك الطبيعي تقريبًا. - حط
limits.memory= حوالي 1.5 ضعف الـ request علشان تسيب مساحة للـ burst المؤقت. - طبّق وراقب. لو شفت OOMKilled تحت الحمل العادي، الـ limit واطي.
apiVersion: apps/v1
kind: Deployment
metadata:
name: reports
spec:
replicas: 6
template:
spec:
containers:
- name: app
image: reports:1.4
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
وبعد ما تطبّق، اتأكد من سبب أي موت بالأمر ده:
# شوف آخر سبب لإنهاء الحاوية
kubectl describe pod reports-xxxx | grep -A3 "Last State"
# المتوقع لو حصل OOM:
# Last State: Terminated
# Reason: OOMKilled
# Exit Code: 137
# راقب الاستهلاك الحقيقي تحت الحمل
kubectl top pod -l app=reports
الأرقام: قبل وبعد على الخدمة الحقيقية
قبل الضبط (بدون limits): نسخة واحدة وصلت 6.8Gi، النود عمل OOM على مستواه، 4 خدمات اترمت، ~12,000 طلب 5xx، و9 دقايق downtime.
بعد ما حطينا requests: 256Mi و limits: 512Mi: لما النسخة المعطوبة كرّرت المشكلة، النظام قتلها هي بس عند 512Mi ورجّعها، الجيران فضلوا ثابتين، والـ 5xx في الحادثة التالية نزل لـ ~40 طلب. ده الفرق بين خدمة بتعزل عطلها وخدمة بتنشره على الكل.
الـ trade-offs اللي لازم تاخد بالك منها
- limit واطي جدًا: بتكسب حماية الجيران، بتخسر استقرار الخدمة نفسها لو الرقم أقل من الـ peak الطبيعي، فتلاقي OOMKilled متكرر.
- request عالي جدًا: بتكسب أولوية واستقرار في الجدولة، بتخسر كثافة على النود — بتحجز ذاكرة ممكن تفضل فاضية، يعني فاتورة أعلى.
- requests = limits: بيدّيك QoS class اسمها Guaranteed (أعلى أولوية وأقل احتمال للترحيل)، مقابل صفر مرونة لأي burst مؤقت.
- غلطة شائعة في الـ CPU: الـ trade-off هنا مختلف تمامًا. limit الـ CPU بيعمل throttling (تبطيء) مش قتل. ناس كتير بتحط CPU limit واطي وتلاقي الخدمة بطيئة وتدوّر على السبب في كل حتة غير الصح.
متى متحطش limit على الذاكرة
لو عندك workload بـ burst ذاكرة مؤقت وآمن (مثلاً معالجة دفعة كل ساعة) وانت متأكد من المراقبة إن النود فيه متسع، الـ memory limit الصارم ممكن يقتلها بدون أي داعي حقيقي. الافتراض إن عندك observability بتوريك الـ peak الفعلي قبل ما تقرر. وعلى نود مخصص لخدمة واحدة بس، الـ limit بيبقى أقل أهمية بكتير. أما الـ CPU limit تحديدًا فكتير من فرق الإنتاج بتشيله وتسيب الـ CPU request بس، لأن الـ throttling في الخدمات الحساسة للـ latency بيضر أكتر ما بينفع.
الخطوة التالية
شغّل kubectl top pod على أهم خدمة عندك تحت حمل حقيقي ليوم كامل، سجّل أعلى رقم ذاكرة، حط requests = الرقم الطبيعي و limits = 1.5 ضعفه، طبّق، وبعد كام يوم اعمل kubectl describe pod وشوف فيه OOMKilled ولا لأ. لو فيه، ارفع الـ limit شوية وكرّر.