الأمن والامتثالتأمين الأنظمة عالية الحمل وإطلاقها: حافة محصَّنة ونشر بلا توقف
طبقات الأمان من CDN وWAF إلى الشبكة الخاصة، وخط تسليم يعتمد النشر التدريجي وblue/green وترحيلات expand and contract، وينتهي بقائمة جاهزية لليوم الذي تأتي فيه الذروة.
معرّف طلب، وسجلات منظّمة في OpenSearch، وأربع إشارات ذهبية، وتتبّع بالعيّنات، وتنبيهات مرتبطة بأثر المستخدم: المجموعة الصغيرة التي تكشف الطبقة البطيئة في دقائق، مع طريقة مراقبة اختبار الحمل ويوم الحدث.
Author
Anichur Rahaman

«العملاء يقولون إن صفحة الدفع تتجمد». هكذا كتب فريق الدعم لمسؤول المنصة في موقع لبيع التذاكر عند الساعة 10:01 صباح يوم الإطلاق. بدأ البيع في العاشرة، ونقر نحو ألف شخص في الدقيقة نفسها، وكانت الخطة تفترض 440 طلبًا في الثانية. لوحة المتابعة تُظهر المعالج عند 40% ولا شيء أحمر.
أي طبقة هي البطيئة؟ مسار واحد أم عقدة واحدة أم عميل واحد؟ إن اعتمدت على واجهة إدارة وتخمين، ذهبت الدقائق العشر التالية في الجدل. وإن كان لديك معرّف طلب وبضعة أرقام مختارة بعناية، ذهبت في بحث واحد. (هذا المشهد توضيحي وليس حادثة حقيقية.)
لا تحتاج إلى منصة ضخمة لذلك. تحتاج إلى معرّف طلب، وسجلات منظّمة، والإشارات الذهبية الأربع، وتنبيهات مرتبطة بأثر المستخدم. في منصة جهّزتها لذروة مجدولة يتحرك فيها نحو ألف مستخدم في الدقيقة نفسها، كانت الخوادم بخير؛ وما كاد يؤذينا هو أن أحدًا لم يستطع أن يقول بسرعة أي طبقة هي البطيئة، ولا إن كان الرقم المخيف حقيقيًا. يبني هذا المقال المجموعة الصغيرة التي تجيب عن هذه الأسئلة، ثم يوضح كيف تراقب بها اختبار حمل ويوم الحدث.
هذا هو الجزء الرابع من سلسلة من خمسة أجزاء بعنوان «هندسة الأنظمة عالية الحمل». الجزء الأول عن ما الذي يتوسع فعلًا، والثاني عن الطوابير والعمّال، والثالث عن طبقة البيانات. أما هذا الجزء فموضوعه رؤية كل ذلك بالعين.
المراقبة التقليدية تخبرك أن شيئًا ما معطوب. أما الـ observability فتتيح لك أن تسأل «لماذا؟» دون أن تنشر كودًا جديدًا أولًا. وهي تقوم على ثلاثة أنواع من البيانات تُسمّى إشارات:
لا تحتاج إلى الثلاثة من اليوم الأول، ولا إلى منصة باهظة. ما تحتاجه هو أن تكون مترابطة، فتنتقل من metric أحمر إلى سجلات وtrace طلب بطيء بعينه. والخيط الذي يربطها هو معرّف الطلب (request ID).
أرخص تحسين أعرفه هو معرّف واحد يرافق الطلب من الحافة إلى آخر مهمة في الطابور. في الإعداد الذي شغّلته، يحترم nginx ترويسة X-Request-Id الواردة أو ينشئ واحدة، ويمررها إلى PHP-FPM، ويكتبها كلاهما في سجلاتهما. ثم يضيفها التطبيق إلى كل سطر سجل، وينسخها إلى حمولة أي مهمة يرسلها، فيمكن ربط مهمة في الخلفية بالنقرة التي أطلقتها.
أعد المعرّف نفسه في ترويسة الاستجابة. وعندما يبلّغ عميل أو موظف دعم عن مشكلة، تكشف تلك السلسلة وحدها القصة كاملة.

| الطبقة | ما العمل |
|---|---|
| الحافة / CDN | مرّر المعرّف الوارد، أو اترك الطبقة التالية تنشئه |
| nginx على المضيف وnginx داخل الحاوية | أعد استخدام المعرّف الوارد، وأنشئ واحدًا إن لم يوجد، واكتبه في access log |
| PHP-FPM والتطبيق | اقرأه من الترويسة وأرفقه بكل سطر سجل كسياق مشترك |
| مهام الطابور | خزّنه في حمولة المهمة واستعده عند تشغيلها |
| استدعاءات HTTP الصادرة | أرسله إلى الجهة التالية ليسجّله الشركاء والخدمات الداخلية أيضًا |
| الاستجابة | أعده كترويسة ليطلبه الدعم الفني |
السجلات النصية العادية كُتبت ليقرأها إنسان واحدًا تلو الآخر. أما عند الحمل العالي فتحتاج إلى البحث فيها وعدّها وتصفيتها، وهذا يعني كائن JSON واحدًا في كل سطر بحقول مسمّاة. تكفي في البداية مجموعة قصيرة وثابتة من الحقول.
| الحقل | لماذا يستحق مكانه |
|---|---|
| time | الترتيب والمطابقة مع المقاييس |
| request_id | الخيط الذي يربط كل الطبقات |
| route أو path (من دون query string) | تجميع الطلبات البطيئة حسب نقطة النهاية لا حسب الرابط الخام |
| status | نسبة الأخطاء لكل نقطة نهاية |
| duration | المادة الخام لحساب p95 وp99 |
| upstream time | التمييز بين «nginx بطيء» و«PHP بطيء» |
| user أو tenant id (معرّف داخلي، وليس بريدًا إلكترونيًا أبدًا) | معرفة ما إذا كان عميل واحد هو سبب المشكلة |
عن التسمية: ELK هو الاسم الشائع لثلاث أدوات هي Elasticsearch وLogstash وKibana، ثم أضافت حزمة Elastic Stack الأوسع ناقلات خفيفة تُسمّى Beats. وقد بدأ OpenSearch عام 2021 كنسخة متفرعة (fork) مرخّصة بـ Apache من Elasticsearch وKibana أطلقتها AWS، وفي سبتمبر 2024 انتقل إلى مؤسسة OpenSearch Software Foundation تحت مظلة Linux Foundation. لم يعد الاثنان متطابقين، لكن مسار العمل مع السجلات واحد: أرسِل، فهرِس، ابحث، ارسم. ولهذا يقول الناس «متوافق مع ELK».
لا تحتاج إلى عنقود كبير. في إعدادي، احتفظت عقدة OpenSearch صغيرة بذاكرة 2 غيغابايت ومعالج افتراضي واحد بسجلات منصة تتعامل مع ذروة حادة. هذا إعداد واحد وليس معيارًا عامًا، لكنه يوضح أن الكلفة صغيرة مقارنة بأول عطل تشخّصه في دقائق بدل ساعات.
السجلات تُنسخ وتُفهرس وتُحفظ شهورًا، ولذلك فهي خطر على حماية البيانات. أخفِ كلمات المرور والرموز وبيانات البطاقات والتفاصيل الشخصية الكاملة قبل أن يغادر أي سطر التطبيق. سجّل المعرّفات الداخلية بدل الأسماء أو البريد أو أرقام الهواتف. واحذف الـ query string من المسارات لأنها كثيرًا ما تحمل رموزًا.
تدوير الفهارس يوميًا مع دورة حياة تلقائية يُبقي العقدة الصغيرة سليمة: تبقى الأيام الأخيرة قابلة للبحث، وتُضغط الفهارس الأقدم أو تُنقل، ويُحذف كل ما تجاوز مدة الاحتفاظ. حدّد المدة مع من يملكون ملف الخصوصية والأمن، ولا تخمّنها. ثلاثون يومًا من السجلات المفصلة نقطة بداية شائعة، والجواب الصحيح يعتمد على لوائحك.
يسمّي كتاب Google «Site Reliability Engineering»، في فصله عن مراقبة الأنظمة الموزعة، أربع إشارات تغطي تقريبًا أي خدمة موجهة للمستخدمين. إن لم تستطع قياس غير أربعة أشياء، فقِس هذه.
| الإشارة | السؤال الذي تجيب عنه | في منصة ويب وعمّال |
|---|---|---|
| Latency (زمن الاستجابة) | كم يستغرق الطلب؟ | p50 وp95 وp99 لكل route، مع تتبع الطلبات الفاشلة على حدة |
| Traffic (الحركة) | كم حجم الطلب؟ | طلبات في الثانية عند الحافة، ومهام مرسلة في الثانية |
| Errors (الأخطاء) | ما نسبة الفشل؟ | نسبة 5xx، والمهل المنتهية، والمهام الفاشلة |
| Saturation (الإشباع) | ما مدى امتلاء أضيق مورد؟ | عمّال FPM المشغولون، وعمق الطابور وعمره، واتصالات قاعدة البيانات، وذاكرة Redis |
تحذيران. الأول: لا تضبط التنبيهات ولا تخطط على المتوسطات، فالمتوسط السليم يخفي ذيلًا مرعبًا. في اختبار حمل أجريته على واجهة أمامية تُرسم من جهة الخادم، تشبّعت عملية واحدة عند نحو 220 إلى 250 طلبًا في الثانية، وقفز p95 من نحو 260 ms إلى نحو 3 ثوانٍ، بينما ظلت أنوية المضيف خاملة. كان المتوسط سيبدو مقبولًا لفترة. أما الـ percentile فقال الحقيقة.
الثاني: الإشباع هو نقطة بداية المتاعب، وهو نادرًا ما يكون المعالج. في منصة كهذه أراقب هذه الأمور أولًا:
السجلات والمقاييس تخبرك أن الطلب كان بطيئًا. أما الـ trace فيخبرك أنه قضى 40 ms في nginx و90 ms في PHP و1.2 ثانية ينتظر استعلامًا واحدًا و30 ms في Redis. وفي نظام فيه طبقة واجهة أمامية وطبقة خلفية وعمّال، هذه أسرع طريقة لتحديد المحطة المسؤولة.
OpenTelemetry هو المعيار المحايد تجاه الموردين لإنتاج هذه البيانات. وبحسب صفحة حالة المواصفة الخاصة به، فإشارتا التتبع والسجلات مستقرتان، وكذلك واجهة المقاييس وبروتوكولها ونموذج بياناتها، بينما يتفاوت نضج حزم SDK الخاصة بكل لغة. ويوثّق مشروع PHP توفر التتبع والمقاييس والسجلات، مع امتداد اختياري للقياس التلقائي. تحقق من حالة المكتبات التي ستستخدمها بالتحديد قبل أن تلتزم.
نصيحتي أن تتبنّاه بهذا الترتيب. تأكد أولًا من وجود معرّف الطلب. ثم تتبّع المسار الحرج فقط، مثل الدفع أو تقديم النموذج، مع أخذ عينات: الاحتفاظ بكل trace عند الحمل العالي يكلّف أكثر مما يعلّم. احتفظ بكل traces الأخطاء والطلبات البطيئة، وبجزء صغير من الباقي. وضع معرّف الـ trace في أسطر السجل بجوار معرّف الطلب، لتتصل الأداتان ببعضهما.
ينبغي أن يجيب التنبيه عن سؤال واحد: هل يحتاج شخص إلى التحرك الآن؟ استخدام المعالج 85% نادرًا ما يستدعي ذلك. أما «الدفع يفشل عند 2% من المستخدمين» فيستدعيه دائمًا. هدف مستوى الخدمة يجعل ذلك محددًا: هدف مثل «99% من طلبات الدفع تنجح خلال أقل من ثانيتين على مدى 30 يومًا»، مع ميزانية أخطاء للباقي.
| نوقظ شخصًا من أجل | تذكرة أو رسالة دردشة تكفي من أجل |
|---|---|
| نسبة أخطاء الدفع تتجاوز حد استهلاك SLO | قرص يمتلئ ببطء |
| p95 لمسار حرج فوق الهدف لعدة دقائق | عقدة واحدة بمعالج مرتفع دون تأثر المستخدمين |
| عمر طابور التقديمات في ازدياد | مهمة واحدة فشلت ثم نجحت بعد إعادة المحاولة |
| المهام الفاشلة ترتفع بسرعة | شهادة تنتهي بعد ثلاثة أسابيع |
مثال محسوب يوضح السبب. لنفترض أن الدفع يتلقى 3,000,000 طلب في 30 يومًا وأن الهدف نجاح 99%، فتكون ميزانية الأخطاء 30,000 طلب فاشل أو بطيء جدًا. خلال ذروة مدتها عشر دقائق بمعدل 440 طلبًا في الثانية تخدم 264,000 طلب. إن فشل 2% منها فهذا يعني 5,280 إخفاقًا، أي نحو 18% من ميزانية الشهر كله تُستهلك في عشر دقائق. هذا تنبيه يستدعي إيقاظ شخص، مهما قال المعالج. (أرقام توضيحية.)
كل تنبيه يوقظ أحدًا بلا سبب يعلّم الفريق تجاهل التنبيه التالي. راجع التنبيهات بعد كل حدث، واحذف الصاخب منها أو خفّض مرتبته.
لأي ذروة مجدولة أبني شاشة واحدة فقط. في الأعلى الإشارات الذهبية للمسارات الحرجة، وتحتها أرقام الإشباع، وبجانبها عمق الطابور وعمره، وخط يبيّن معدل الطلبات المستهدف. لا شيء غير ذلك. لوحة تحتاج إلى تمرير لن يقرأها أحد في دقيقة الذروة.

ضع علامة نشر (deploy marker) على كل رسم بياني. نصف «التراجعات الغامضة» إصدار نزل قبل عشر دقائق.
اختبار الحمل أرخص بروفة ستحصل عليها، وتضيع قيمته إن لم تستطع أن ترى ما بداخله. أعد تشغيل مزيج الطلبات الحقيقي لذروة سابقة بدل ضرب رابط واحد. في اختباراتي استخدمت k6 مع حدود إيقاف: توقف إذا تجاوز p95 ثلاث ثوانٍ، أو عند أي خطأ 5xx أو مهلة منتهية. وكان سكربت حارس صغير يوقف التشغيل إذا بلغ المعالج حدوده، حتى لا يتحول الاختبار إلى انقطاع فعلي.
تحت الضغط يغري المرء أن يتحرك مع كل ما هو أحمر. تحقق أولًا. بعض الواجهات تسمّي الحاويات تسمية خاطئة، وقد يشير تنبيه «100% CPU» إلى عملية غير التي توشك أن تعيد تشغيلها. انظر إلى قائمة العمليات الفعلية، وقارنها بمصدر ثانٍ، ثم قرّر.

وينطبق الأمر نفسه على أعمالك الخلفية. فحوص الصحة والمجدولات تستهلك موارد أيضًا، وفي إحدى الحالات زعزع فحص صحة يطلق عملية جديدة في كل مرة حاوية قاعدة بيانات، بعدما تُركت عمليات يتيمة تحت الحمل. أن تقيس المراقبة نفسها ليس مبالغة في الحذر.
لنعد إلى 10:01. مع هذه المنظومة تصل رسالة الدعم ومعها معرّف الطلب. يُظهر بحث واحد أن nginx انتظر PHP مدة 1.31 ثانية، وأن التطبيق سجّل استعلامًا بطيئًا في الطلب نفسه. وتؤيد اللوحة ذلك: p95 في ارتفاع و48 من 60 عاملًا في FPM مشغولون. أما تنبيه المعالج الأحمر الوحيد فيخص حاوية أخرى، فلا يعيد أحد تشغيل شيء. يعرف المسؤول السبب في نحو دقيقتين بدل عشر، ويذهب الإصلاح إلى المكان الصحيح. (وهذا أيضًا توضيحي.)
في الجزء الأخير من هذه السلسلة أتناول النصف الآخر من البقاء على الخط: تأمين الحافة ونشر التغييرات دون أي توقف.
Anichur Rahaman مهندس برمجيات معماري ومؤسس StoreConsole، يصمّم أنظمة التجارة وERP للشركات النامية، ويهتم خصوصًا بالبنية القائمة على الأحداث وسلامة البيانات وتشغيل الأنظمة على خوادم الشركة نفسها.
About the Author
Anichur Rahaman
Continue Reading