خط أساس إنتاجي لأحمال عمل Kubernetes
الطلبات والمجسّات وميزانيات التعطيل والإيقاف المنظّم وأمان الحاويات: الإعدادات التي ينبغي أن يملكها أي Deployment إنتاجي قبل أول حادثة.
الأمثلة عامة وتوضيحية. لم تُؤخذ من بيئة أي عميل: اضبط العتبات والإعدادات وفق نظامك.
لماذا خط الأساس
يشغّل Kubernetes أي شيء تقريبًا بلا إعدادات تقريبًا، وهنا تكمن المشكلة: القيم الافتراضية مختارة للبدء، لا للاستمرار في العمل. الإعدادات التالية رخيصة إن أُضيفت في اليوم الأول ومكلفة إن اكتُشفت أثناء عطل. وهي تخص Deployment عديمة الحالة؛ أما أحمال العمل ذات الحالة فتحتاج أكثر.
الطلبات والحدود
يضع المجدول الحاويات وفق طلباتها (requests)، ويقيس Horizontal Pod Autoscaler الاستخدام نسبةً إليها. والحاوية بلا طلبات توضع عشوائيًا ولا يمكن توسيعها آليًا وفق المعالج.
الذاكرة غير قابلة للضغط: الحاوية التي تتجاوز حد ذاكرتها تُنهى. اجعل حد الذاكرة مساويًا للطلب أو قريبًا منه، حتى لا تُجدوَل الحاوية على ذاكرة لا تستطيع الاحتفاظ بها. أما المعالج فمختلف — الحاوية التي تتجاوز حد المعالج تُخنق، ويظهر ذلك زيادةً في زمن الاستجابة. كثير من الفرق تحدد طلبات المعالج وتترك حدوده للخدمات الحساسة لزمن الاستجابة؛ وأيًّا كان اختيارك، قِس الخنق.
ثلاثة مجسّات وثلاث مهام
- مجسّ الجاهزية (readiness) يقرر هل تستقبل الحاوية حركة. احذر الاعتماديات المشتركة: إن فشلت كل الحاويات في الجاهزية معًا، بقيت الخدمة بلا أي نقطة وصول.
- مجسّ الحيوية (liveness) يقرر هل يُعاد تشغيل الحاوية. اقصره على العملية نفسها: إن كان يفحص قاعدة البيانات، فإن عطلها يعيد تشغيل كل الحاويات دفعةً واحدة.
- مجسّ البدء (startup) يؤجّل المجسّات الأخرى ريثما يقلع تطبيق بطيء، بدلًا من إطالة تأخير مجسّ الحيوية.
الصمود أمام تفريغ العُقد وتعطّل المناطق
ترقيات العُقد والتوسع الآلي تُخلي الحاويات باستمرار. يحدّ PodDisruptionBudget من عدد النسخ التي يمكن لتعطيل طوعي إيقافها في آن واحد، وتوزّع قيود انتشار الطوبولوجيا (topology spread constraints) النسخ على مناطق مختلفة، حتى لا يُسقط تعطّل منطقة واحدة الخدمة كلها.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api
spec:
maxUnavailable: 1
selector:
matchLabels: { app: api }
الإيقاف المنظّم
عند حذف حاوية، يبدأ في الوقت نفسه إخراجها من نقاط وصول الخدمة وإيقاف حاوياتها (خطاف preStop ثم إشارة SIGTERM). والتطبيق الذي يتوقف فورًا يُضيّع طلبات ما زالت توجَّه إليه. أضف توقفًا قصيرًا في preStop لكي تلحق موازنات الحمل بالتغيير، وتعامل مع SIGTERM بإنهاء العمل الجاري، واجعل terminationGracePeriodSeconds أطول من مجموع التوقف ومدة التفريغ.
سياق الأمان والصور
شغّل بمستخدم غير جذري، وأزل كل صلاحيات Linux، وامنع تصعيد الامتيازات، واربط نظام الملفات الجذري للقراءة فقط؛ ومعيار Pod Security المقيّد (restricted) يفرض معظم ذلك على مستوى مساحة الأسماء. أشر إلى الصور بالبصمة (digest) لا بوسم قابل للتغيير، ليعمل بالضبط ما اختُبر.
الصورة الكاملة
Deployment مطبَّق عليه خط الأساس. القيم توضيحية: حدد الطلبات من الاستخدام المقيس، لا من هذا المثال.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels: { app: api }
template:
metadata:
labels: { app: api }
spec:
terminationGracePeriodSeconds: 45
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels: { app: api }
securityContext:
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
containers:
- name: api
image: registry.example.com/api@sha256:<digest>
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { memory: 256Mi }
readinessProbe:
httpGet: { path: /ready, port: 8080 }
periodSeconds: 5
livenessProbe:
httpGet: { path: /live, port: 8080 }
periodSeconds: 10
failureThreshold: 3
startupProbe:
httpGet: { path: /live, port: 8080 }
periodSeconds: 5
failureThreshold: 30
lifecycle:
preStop:
sleep: { seconds: 5 } # native sleep action, Kubernetes 1.30+
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }