ملف swap المفقود الذي جمّد خوادمنا
في 24 سبتمبر توقف أحد خوادم الإنتاج لدينا عن الاستجابة. وازن الحمل وضع ذلك الخادم في حالة غير صحية، وأداة الأوامر عن بُعد لدينا لم تستطع الوصول إليه، لكن لم ينهر أي شيء ولم تُقتل أي عملية لاستهلاكها ذاكرة زائدة. كان الخادم يتخبط: دون مساحة swap، وبحاويات مسموح لها باستخدام ذاكرة أكثر مما يملك الجهاز، كان Linux يقضي وقته في إخلاء الملفات التي يحتاجها للعمل ثم إعادة قراءتها، ولا يحقق تقدماً يُذكر. إضافة ملف swap بحجم 2 GB على كل خادم حلّت المشكلة. أما الدرس الأهم فكان عن فحوصات الصحة.
كيف بدا «التعليق»
- فشلت فحوصات الصحة من موازن الحمل على ذلك الخادم بسبب انتهاء المهلة.
- الأوامر المرسلة عبر AWS Systems Manager بقيت في حالة «Pending» ولم تُنفذ أبداً.
- لم يكن هناك أي قتل بسبب نفاد الذاكرة في السجلات. لم تمت أي عملية.
قبل أسبوع كنا قد نقلنا الإنتاج إلى خوادم ARM أصغر بذاكرة 2 GB لكل منها. وخادم الاختبار لدينا، وهو أصغر منها، كان قد تجمّد بالطريقة نفسها أثناء فك حزمة صورة حاوية خلال عملية نشر.
لماذا لا يوجد خطأ ذاكرة؟
لا ينهي Linux العملية إلا عندما ينفد فعلاً من الذاكرة التي يستطيع استعادتها. قبل تلك النقطة، يستعيد الذاكرة من page cache، أي نسخ الملفات التي يحتفظ بها في الذاكرة الحية، بما فيها شفرة كل برنامج قيد التشغيل.
على خوادمنا، كانت حدود الذاكرة المشتركة للحاويات تصل إلى نحو 3.3 GB على جهاز يملك نحو 1.8 GB قابلة للاستخدام. وعادة لا تستخدم كلها حدودها دفعة واحدة. فعندما فعلت، ظل النواة يُخلي شفرة البرامج من الذاكرة المؤقتة ليفسح مكاناً، ثم يضطر إلى إعادة قراءتها من القرص لحظة أن تحتاجها البرامج مرة أخرى. هذه الدورة، أي التخبط، قد تجعل الجهاز شبه غير مستجيب تماماً دون أن ينفد فعلياً من الذاكرة من الناحية التقنية. فلا يُقتل شيء، ولا يتعافى شيء.
ودون swap، لا يملك النواة مكاناً يضع فيه الذاكرة المخصصة التي تُستخدم نادراً، فيضطر إلى أخذها من ذاكرة التخزين المؤقت بدلاً من ذلك.
الإصلاح
يُنشئ سكربت تهيئة الخادم الآن ملف swap بحجم 2 GB مع إعداد swappiness منخفض (10) في كل مرة يُقلع فيها خادم أو يُصلح. والإعداد المنخفض يعني أن النواة ما زال يفضّل إبقاء الذاكرة العاملة في RAM، لكن الصفحات النادرة الاستخدام يمكن أن تنتقل إلى القرص بدلاً من إجبار شفرة البرامج على الخروج من ذاكرة التخزين المؤقت. طبّقناه مباشرة على خادمي الإنتاج والاختبار، وينشر خط النشر لدينا سكربت التهيئة من جديد في كل نشر للإنتاج، حتى يحصل الخادم المستبدل عليه أيضاً.
الجزء الذي أقلقنا أكثر: الخادم غير الصحي بقي
كانت مجموعة الخوادم لدينا تستخدم فحوصات EC2 («هل الجهاز يعمل؟») لا فحوصات موازن الحمل («هل يستجيب للطلبات؟»). لذلك كان الخادم الذي تخلى عنه موازن الحمل ما زال سليماً من وجهة نظر المجموعة. ولن يُستبدل أبداً. كان الإنتاج سيعمل بهدوء على خادم واحد إلى أن يلاحظ أحد ذلك ويعيد تشغيل الآخر.
التحول إلى فحوصات موازن الحمل يبدو الحل البديهي، لكنه ليس بسيطاً عندنا: أثناء النشر تُجيب الخوادم عمداً بخطأ على نقطة فحص الصحة، حتى يسحبها الموازن من الخدمة (انظر كيف وصلنا إلى نشر بلا أخطاء 502). مجموعة تستبدل الخوادم عند هذه الإشارة ستستبدلها في منتصف كل نشر.
لذلك أضفنا الآن خطوة إلى قائمة التحقق من النشر لدينا: بعد كل نشر، نتحقق من صحة كل هدف على موازن الحمل، لا من نجاح مهمة النشر فقط. فإذا بقيت الأوامر إلى خادم في حالة «Pending»، فهو يتخبط؛ أعد تشغيله، فهو خارج التوزيع أصلاً.
ما الذي استخلصناه
- «لا ينهار» لا تعني «سليم». التخبط لا ينتج أي خطأ، بل بطء شديد جداً إلى درجة أن الجهاز يتوقف عن الاستجابة.
- الخوادم الصغيرة تحتاج إلى swap. حتى قليل منه، مع swappiness منخفض، يمنح النواة مكاناً يضع فيه الذاكرة الباردة.
- حدود الذاكرة التي يتجاوز مجموعها حجم الجهاز رهان. أحياناً تخسره. اعرف متى راهنت.
- اعرف أي فحص صحة يثق به التوسع التلقائي لديك. فحص صحة يسأل فقط «هل الجهاز الظاهري مشغّل؟» لن يستبدل خادماً توقف عن العمل.