الوحدة 5 · التشغيل في الإنتاج
الـ Probes والإيقاف الآمن
20 دقراءة 4 داختر بحكمة
الدرس في لمحة
أضف إلى notes-api فحوص startup وreadiness وliveness تخبر Kubernetes بالحقيقة، وأوقف الـ Pods دون إسقاط الطلبات باستخدام توقّف preStop ومعالج SIGTERM.
- 1ثلاثة probes، وثلاث عواقب
- 2الـ Readiness مفتاح للحركة
- 3البدء دون حلقات إعادة تشغيل
- 4الإيقاف دون إسقاط الطلبات
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
لا يستطيع Kubernetes رؤية ما بداخل تطبيقك. إن تُرك وشأنه، يعدّ الـ container سليمًا ما دامت عمليته تعمل، حتى لو كانت تلك العملية عالقة في deadlock، أو ما زالت تسخّن، أو عاجزة عن الوصول إلى قاعدة البيانات. الـ probes (الفحوص) هي طريقة التطبيق لقول الحقيقة عن نفسه. لكن يجب صياغة هذه الحقيقة بعناية، لأن Kubernetes يتصرّف بناءً عليها تلقائيًا وعلى نطاق واسع.
ثلاثة probes، وثلاث عواقب
| الـ Probe | السؤال الذي يجيب عنه | حين يفشل |
|---|---|---|
| startup | هل انتهى التطبيق من البدء؟ | واصل الانتظار حتى الحدّ، ثم أعِد التشغيل |
| readiness | هل يجب أن يستقبل هذا الـ Pod حركة الآن؟ | أزِل الـ Pod من endpoints الـ Service؛ دون إعادة تشغيل |
| liveness | هل العملية عالقة بلا أمل في التعافي؟ | أعِد تشغيل الـ container |
ما دام startup probe مضبوطًا ولم ينجح بعد، لا يعمل الاثنان الآخران. وحالما ينجح، لا يعمل مرة أخرى أبدًا لذلك الـ container.
لدى notes-api أصلًا المساران (endpoints) اللذان يحتاج إليهما هذا. /healthz يُرجع ok إذا كانت حلقة الأحداث (event loop) تستطيع الإجابة عن طلب أصلًا. و/readyz يفحص أيضًا مجمّع اتصالات قاعدة البيانات ويُرجع 503 إذا تعذّر الوصول إلى Postgres.
- name: api
image: ghcr.io/skylane/notes-api:1.4.0
ports:
- name: http
containerPort: 3000
startupProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 2
failureThreshold: 30 # up to 60 s to start
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
failureThreshold: 2
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3أي حالة HTTP من 200 إلى 399 تُعدّ نجاحًا. وإلى جانب httpGet توجد probes من نوع tcpSocket وgrpc وexec؛ فضّل HTTP لخدمات الويب، لأنه يختبر المسار الذي تسلكه الحركة الحقيقية. القيم الافتراضية، إن تركت الحقول، هي periodSeconds: 10 وtimeoutSeconds: 1 وfailureThreshold: 3. مهلة الثانية الواحدة تفاجئ التطبيقات البطيئة، فاضبطها عن قصد.
الـ Readiness مفتاح للحركة
الـ readiness هو الـ probe الأهمّ في العمل اليومي. الـ Pod الذي يبدأ، أو المُثقل، أو المقطوع عن قاعدة بياناته يُبلغ بأنه غير جاهز ويخرج من endpoints الـ Service حتى يتعافى. وهو أيضًا بوّابة للـ rollouts: لا يُحسب الـ Pod الجديد متاحًا إلا حين يصبح جاهزًا، ولا يزيل التحديث المتدرّج الـ Pods القديمة إلا بقدر ما تصبح الجديدة متاحة. الإصدار الذي لا يصل إلى قاعدة البيانات لا يأخذ الحركة أبدًا من إصدار يصل إليها. لهذا فإن الـ Deployment الذي لا readiness probe فيه هو أول ما أنبّه عليه في كل مراجعة.
البدء دون حلقات إعادة تشغيل
قبل وجود الـ startup probes، كانت التطبيقات بطيئة البدء تُحمى بـ initialDelaySeconds على الـ liveness. هذا يؤخّر الفحص الأول في كل بدء بمقدار أسوأ زمن بدء، وينكسر مجددًا حين يصبح البدء أبطأ. أما الـ startup probe فيعطي التطبيق periodSeconds × failureThreshold (هنا 2 × 30 = 60 ثانية) لينهض، مع فحص متكرّر حتى يُكتشف البدء السريع بسرعة، ثم يسلّم الأمر إلى الـ liveness بوتيرته العادية.
الإيقاف دون إسقاط الطلبات
حين يُحذف Pod، أثناء rollout أو تقليص للنسخ أو تفريغ لـ node، يبدأ شيئان في الوقت نفسه: يُزال الـ Pod من endpoints الـ Service، ويبدأ الـ kubelet في إنهاء الـ container. إزالة الـ endpoints يجب أن تنتشر إلى قواعد الشبكة في كل node وإلى أي موازن أحمال، وهذا يستغرق لحظة. إذا توقّف تطبيقك عن قبول الاتصالات لحظة وصول SIGTERM، فالطلبات الموجّهة إليه في تلك اللحظة تُرفض.
للحل نصفان. في مواصفات الـ Pod، توقّف قبل SIGTERM حتى يلحق التوجيه بالتغيير:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: api
lifecycle:
preStop:
sleep:
seconds: 5الإجراء sleep يعمل داخل الـ kubelet، فيعمل حتى في image من نوع distroless لا تحتوي على ملف sleep التنفيذي. وفي التطبيق، أبقِ معالج SIGTERM من الدرس 1.4: توقّف عن قبول اتصالات جديدة، وأنهِ الطلبات الجارية، وأغلق مجمّع اتصالات قاعدة البيانات، ثم اخرج. يجب أن يتّسع توقّف preStop والتفريغ معًا داخل terminationGracePeriodSeconds، وإلا فسيرسل الـ kubelet إشارة SIGKILL عند انتهاء المهلة.
يعرف Kubernetes الآن متى يكون notes-api سليمًا وكيف يوقفه بأمان. التالي: يحتاج إلى معرفة كم يستهلك notes-api من المعالج والذاكرة، وكيف يضيف Pods حين تنمو الحركة.
أهم النقاط
- فشل الـ readiness probe يزيل الـ Pod من endpoints الـ Service؛ وفشل الـ liveness probe يعيد تشغيل الـ container؛ والـ startup probe يؤجّل الاثنين حتى يكتمل بدء التطبيق.
- الـ liveness يفحص فقط هل العملية نفسها عالقة؛ لا تجعله أبدًا يعتمد على قاعدة بيانات أو خدمة أخرى.
- الـ readiness بوّابة للتحديثات المتدرّجة، فالإصدار الجديد العاجز عن الخدمة لا يحلّ أبدًا محلّ إصدار يعمل.
- عند الحذف، تحدث إزالة الـ endpoints وإرسال SIGTERM في الوقت نفسه؛ وتوقّف
preStopقصير يتيح للحركة أن تنصرف عن الـ Pod قبل أن يبدأ التطبيق بالإيقاف. - يجب أن ينهي التطبيق العمل الجاري خلال
terminationGracePeriodSeconds(30 افتراضيًا)، وإلا فسيُقتل.