One self-hosted console to run your entire business — commerce, ERP, HRM, CRM & manufacturing

हाई-वॉल्यूम सिस्टम की observability: काम आने वाले log, metric और trace

एक request ID, OpenSearch में स्ट्रक्चर्ड log, चार golden signal, sampling वाले trace और यूज़र-असर से जुड़े alert: धीमी लेयर को मिनटों में पकड़ने वाला छोटा सेट, साथ में load test और इवेंट के दिन नज़र रखने का तरीक़ा।

Author

Anichur Rahaman

6 दिन पहले12 min read2 views
हाई-वॉल्यूम सिस्टम की observability: काम आने वाले log, metric और trace

"ग्राहक कह रहे हैं checkout अटक रहा है।" release की सुबह 10:01 बजे एक टिकटिंग साइट के platform लीड के पास सपोर्ट डेस्क का यह संदेश पहुँचता है। बिक्री 10:00 बजे खुली थी, क़रीब एक हज़ार लोगों ने एक ही मिनट में क्लिक किया, और योजना 440 request प्रति सेकंड की थी। dashboard पर CPU 40% है और कुछ भी लाल नहीं।

कौन-सी लेयर धीमी है? कोई एक route, एक नोड या एक ग्राहक? कंसोल और अंदाज़े के भरोसे रहें तो अगले दस मिनट बहस में निकल जाते हैं। एक request ID और चुने हुए कुछ नंबर हों तो वही दस मिनट एक सर्च में। (यह दृश्य काल्पनिक उदाहरण है, कोई असली घटना नहीं।)

इसके लिए बड़ा platform नहीं चाहिए। चाहिए एक request ID, स्ट्रक्चर्ड log, चार golden signal और user-असर से जुड़े alert। एक ऐसे platform को मैंने तय स्पाइक के लिए तैयार किया था जिसमें एक ही मिनट में क़रीब एक हज़ार user आने वाले थे। server ठीक थे; लगभग नुक़सान इस बात से हुआ कि कोई जल्दी नहीं बता पा रहा था कि कौन-सी लेयर धीमी है, या डरावना नंबर सच है या नहीं। यह लेख वही छोटा सेट बनाता है जो इन सवालों का जवाब दे, और फिर दिखाता है कि उससे load test और event के दिन पर नज़र कैसे रखें।

यह "हाई-वॉल्यूम system की इंजीनियरिंग" सीरीज़ के पाँच भागों में से चौथा भाग है। पहले भाग में असल में क्या स्केल होता है, दूसरे में queue और worker, और तीसरे में data टियर है। इस भाग का विषय है: यह सब कुछ आँखों से देख पाना।

एक अनुच्छेद में observability

Monitoring बताती है कि कुछ गड़बड़ है। Observability आपको यह पूछने देती है कि गड़बड़ क्यों है, वह भी बिना नया कोड भेजे। इसकी बुनियाद तीन तरह का data है, जिन्हें सिग्नल कहा जाता है:

  • Log: हर घटना का एक रिकॉर्ड, पूरे ब्योरे के साथ। "इस request के साथ ठीक-ठीक क्या हुआ?" जैसे सवाल के लिए सबसे अच्छा।
  • Metric: समय के साथ ली गई संख्याएँ। "क्या हालत बिगड़ रही है, और कितनी तेज़ी से?" जैसे सवाल के लिए सबसे अच्छा।
  • Trace: एक request का हर सर्विस से गुज़रता रास्ता, हर क़दम पर लगे समय के साथ। "वक़्त गया कहाँ?" जैसे सवाल के लिए सबसे अच्छा।

पहले ही दिन तीनों की ज़रूरत नहीं, और महँगे platform की भी नहीं। ज़रूरत इस बात की है कि तीनों आपस में जुड़े हों, ताकि लाल हुए किसी metric से एक धीमी request के log और trace तक सीधे पहुँचा जा सके। इस जोड़ का काम request ID करता है।

शुरुआत ऐसे request ID से करें जो हर पड़ाव पार करे

मेरी जानकारी में सबसे सस्ता सुधार एक ऐसा आइडेंटिफ़ायर है जो एज से लेकर आख़िरी queue-जॉब तक request के साथ चलता है। मैंने जो सेटअप चलाया, उसमें nginx आने वाले X-Request-Id हेडर को मानता है या नया बना देता है, उसे PHP-FPM को देता है, और दोनों अपने log में उसे लिखते हैं। फिर app हर log लाइन में ID जोड़ता है और जो जॉब डिस्पैच करता है उसके payload में भी कॉपी कर देता है, ताकि बैकग्राउंड टास्क को उस क्लिक से जोड़ा जा सके जिसने उसे शुरू किया।

वही ID response हेडर में भी लौटाएँ। जब कोई ग्राहक या सपोर्ट agent समस्या बताए, तो वही एक स्ट्रिंग पूरी कहानी निकाल देती है।

CDN, लोड बैलेंसर, nginx, PHP-FPM, app और queue जॉब से गुज़रती एक request को उसकी request ID से OpenSearch की एक ही सर्च में देखने का डायग्राम
हर लेयर एक ही request ID लिखे तो पाँच अलग log फ़ाइलें एक टाइमलाइन बन जाती हैं।

ID कहाँ-कहाँ दिखनी चाहिए

लेयरक्या करें
एज / CDNआई हुई ID आगे भेजें, या अगली लेयर को बनाने दें
होस्ट nginx और container nginxआई हुई ID इस्तेमाल करें, न हो तो बनाएँ, access log में लिखें
PHP-FPM और appहेडर से पढ़ें, हर log लाइन में साझा संदर्भ के रूप में जोड़ें
queue जॉबजॉब के payload में रखें और जॉब चलते समय वापस निकालें
बाहर जाने वाली HTTP कॉलआगे भेजें, ताकि पार्टनर और आंतरिक सर्विस भी इसे दर्ज कर सकें
Responseहेडर के रूप में लौटाएँ ताकि सपोर्ट इसे माँग सके

स्ट्रक्चर्ड log से ELK या OpenSearch स्टैक तक

सादे टेक्स्ट log इंसान के एक-एक करके पढ़ने के लिए लिखे जाते हैं। हाई वॉल्यूम पर आपको उन्हें खोजना, गिनना और छानना पड़ता है, इसलिए चाहिए हर लाइन में एक JSON ऑब्जेक्ट, नाम वाले फ़ील्ड के साथ। शुरू करने के लिए फ़ील्ड का एक छोटा, स्थिर सेट काफ़ी है।

फ़ील्डइसकी जगह क्यों बनती है
timeक्रम तय करना और metric से मिलान
request_idसारी लेयर को जोड़ने वाला धागा
route या path (query string के बिना)धीमी request को कच्चे URL से नहीं, endpoint से समूहित करना
statusendpoint के हिसाब से एरर रेट
durationp95 और p99 का कच्चा माल
upstream time"nginx धीमा है" और "PHP धीमा है" को अलग करता है
user या tenant id (आंतरिक ID, कभी email नहीं)पता चलता है कि समस्या किसी एक ग्राहक की है या नहीं

नाम के बारे में: ELK तीन टूल का पुराना उपनाम है: Elasticsearch, Logstash और Kibana। बाद में Elastic Stack में Beats नाम के हल्के शिपर जुड़े। OpenSearch 2021 में AWS के बनाए Elasticsearch और Kibana के Apache-लाइसेंस वाले फ़ोर्क के रूप में शुरू हुआ, और सितंबर 2024 में यह Linux Foundation के तहत OpenSearch Software Foundation के पास चला गया। अब दोनों हूबहू एक नहीं हैं, पर log के लिए काम का तरीक़ा वही है: भेजो, index करो, खोजो, चार्ट बनाओ। इसीलिए लोग "ELK-compatible" कहते हैं।

बड़ा क्लस्टर नहीं चाहिए। मेरे सेटअप में 2 GB मेमोरी और एक vCPU वाले छोटे OpenSearch नोड ने एक तीखा स्पाइक झेलने वाले platform के log सँभाल लिए। यह एक ख़ास सेटअप है, बेंचमार्क नहीं, लेकिन इससे दिखता है कि लागत उस पहले आउटेज के मुक़ाबले बहुत छोटी है जिसे आप घंटों की जगह मिनटों में समझ लेते हैं।

क्या log न करें

Log कॉपी होते हैं, index होते हैं और महीनों रखे जाते हैं, इसलिए ये data-सुरक्षा का जोखिम हैं। कोई भी लाइन app से निकलने से पहले password, token, कार्ड data और पूरे निजी ब्योरे हटा दें। नाम, email या फ़ोन नंबर की जगह आंतरिक ID लिखें। Path से query string हटा दें, क्योंकि उनमें अक्सर token होते हैं।

Retention और index लाइफ़साइकल

रोज़ाना index बदलना और उस पर अपने-आप चलने वाला लाइफ़साइकल रखना छोटे नोड को सेहतमंद रखता है: हाल के दिन खोजे जा सकते हैं, पुराने index सिकोड़े या हटाए जाते हैं, और retention सीमा के पार की चीज़ें मिट जाती हैं। सीमा अंदाज़े से तय न करें; प्राइवेसी और सुरक्षा देखने वालों के साथ तय करें। विस्तृत log तीस दिन रखना एक आम शुरुआत है, पर सही जवाब आपके नियमों पर निर्भर करता है।

Metric: चार golden signal

Google की Site Reliability Engineering किताब में distributed system की monitoring वाले अध्याय में चार सिग्नल बताए गए हैं, जो user-फ़ेसिंग लगभग किसी भी सर्विस को कवर करते हैं। अगर आप सिर्फ़ चार चीज़ें माप सकते हैं, तो यही माप लें।

सिग्नलकिस सवाल का जवाब देता हैवेब और worker platform में क्या मापें
Latencyrequest में कितना समय लगता है?हर route पर p50, p95 और p99; फ़ेल हुई request अलग से देखें
Trafficमाँग कितनी है?एज पर प्रति सेकंड request, प्रति सेकंड डिस्पैच हुए जॉब
Errorsकितना हिस्सा फ़ेल हो रहा है?5xx रेट, timeout, फ़ेल हुए जॉब
Saturationसबसे तंग रिसोर्स कितना भरा है?FPM के व्यस्त worker, queue depth और age, database connection, Redis मेमोरी

दो चेतावनियाँ। पहली, औसत के आधार पर कभी alert या योजना न बनाएँ: सेहतमंद दिखता औसत भयानक पूँछ छिपा लेता है। server-रेंडर्ड फ़्रंटएंड पर मेरे चलाए एक load test में एक प्रोसेस क़रीब 220 से 250 request प्रति सेकंड पर संतृप्त हो गया, p95 क़रीब 260 ms से उछलकर क़रीब 3 सेकंड पर पहुँच गया, जबकि होस्ट के कोर अब भी ख़ाली थे। औसत कुछ देर तक ठीक दिखता रहता। सच percentile ने बताया।

दूसरी, मुसीबत saturation से शुरू होती है, और वह शायद ही कभी CPU होता है। इस तरह के platform पर मैं सबसे पहले ये देखता हूँ:

  • PHP-FPM के व्यस्त worker, पूल की अधिकतम सीमा के मुक़ाबले। सीमा छूते ही request nginx में कतार लगाने लगती हैं।
  • हर queue की queue depth और queue age। Depth बताती है कितना इंतज़ार में है; age बताती है आप पहले ही कितना पिछड़ चुके हैं।
  • database connection और write throughput, सिर्फ़ CPU नहीं।
  • भूमिका के हिसाब से (cache, session, queue) Redis मेमोरी और eviction।
  • हर container की मेमोरी, क्योंकि out-of-memory kill बाहर से किसी बेतरतीब एरर जैसा दिखता है।

OpenTelemetry से tracing

Log और metric बताते हैं कि request धीमी थी। Trace बताता है कि उसने nginx में 40 ms, PHP में 90 ms, एक query के इंतज़ार में 1.2 सेकंड और Redis में 30 ms बिताए। फ़्रंटएंड टियर, बैकएंड टियर और worker वाले system में दोषी पड़ाव पकड़ने का यह सबसे तेज़ तरीक़ा है।

यह data बनाने का vendor-निरपेक्ष स्टैंडर्ड OpenTelemetry है। इसके specification status पेज के मुताबिक़ tracing और logs सिग्नल stable हैं, और metrics का API, protocol और data model भी stable है, जबकि अलग-अलग भाषाओं के SDK की परिपक्वता में फ़र्क़ है। PHP प्रोजेक्ट trace, metric और log तीनों के उपलब्ध होने का दस्तावेज़ देता है, और ऑटोमैटिक instrumentation के लिए एक वैकल्पिक extension भी। कोई फ़ैसला करने से पहले जिन लाइब्रेरी का आप इस्तेमाल करेंगे, उनकी अपनी स्थिति जाँच लें।

मेरी सलाह है कि इसे इसी क्रम में अपनाएँ। पहले पक्का करें कि request ID मौजूद है। फिर सिर्फ़ critical path को trace करें, जैसे checkout या सबमिशन, और उसमें sampling रखें: हाई वॉल्यूम पर हर trace रखने की क़ीमत उससे मिलने वाली सीख से ज़्यादा होती है। एरर और धीमी request के सारे trace रखें, बाक़ी का थोड़ा-सा हिस्सा। Trace ID को भी log लाइन में request ID के पास रखें, ताकि दोनों टूल एक-दूसरे से जुड़ सकें।

SLO और ऐसे alert जिनका कुछ मतलब हो

किसी alert को एक ही सवाल का जवाब देना चाहिए: क्या किसी को अभी कुछ करना है? CPU 85% होने पर शायद ही। "2% user का checkout फ़ेल हो रहा है" पर हमेशा। Service level objective इसे ठोस बना देता है: जैसे "30 दिनों में 99% checkout request 2 सेकंड के भीतर सफल हों", और बाक़ी के लिए एक error budget।

इनके लिए किसी को जगाएँ (page)इनके लिए टिकट या चैट काफ़ी है
Checkout error rate SLO burn threshold से ऊपरडिस्क धीरे-धीरे भर रही है
critical route का p95 कई मिनट से लक्ष्य के ऊपरएक नोड पर CPU ज़्यादा, user अप्रभावित
submissions queue की age बढ़ रही हैएक जॉब retry होकर पास हो गया
फ़ेल हुए जॉब तेज़ी से बढ़ रहे हैंसर्टिफ़िकेट तीन हफ़्ते बाद ख़त्म होगा

एक हिसाब से बात साफ़ होती है। मान लीजिए checkout पर 30 दिन में 30,00,000 request आती हैं और लक्ष्य 99% सफलता है, तो error budget 30,000 फ़ेल या बेहद धीमी request का है। 440 request प्रति सेकंड वाले दस मिनट के स्पाइक में आप 2,64,000 request सँभालते हैं। उनमें से 2% फ़ेल हों तो 5,280 फ़ेल हुईं: पूरे महीने के budget का क़रीब 18% दस मिनट में ख़त्म। CPU कुछ भी कहे, यह page करने लायक़ बात है। (काल्पनिक संख्याएँ।)

हर वह alert जो बेवजह किसी को जगाता है, टीम को अगला alert अनदेखा करना सिखा देता है। हर event के बाद alert की समीक्षा करें, शोर करने वालों को हटाएँ या दर्जा घटा दें।

event-डे का dashboard

तय स्पाइक के लिए मैं एक ही screen बनाता हूँ। ऊपर critical route के golden signal, उनके नीचे saturation की संख्याएँ, बग़ल में queue depth और age, और लक्ष्य request रेट की एक रेखा। बस इतना। जिस dashboard को स्क्रॉल करना पड़े, उसे पीक मिनट में कोई नहीं पढ़ेगा।

latency, traffic, errors और saturation panel, तथा queue depth और age, database connection और Redis मेमोरी वाले event-डे dashboard का वायरफ़्रेम
पीक मिनट के लिए एक screen: पहले चार golden signal, फिर वे रिसोर्स जो ख़त्म होते हैं।

हर चार्ट पर deploy marker लगाएँ। आधे "रहस्यमय regression" दरअसल दस मिनट पहले उतरी कोई release होते हैं।

Load test को देखना

Load test सबसे सस्ती रिहर्सल है जो आपको मिलेगी, और अगर आप उसके भीतर झाँक न सकें तो बेकार जाती है। किसी एक URL पर चोट करने की जगह पिछले पीक का असली request मिश्रण रीप्ले करें। मेरे test में मैंने k6 को abort threshold के साथ इस्तेमाल किया: p95 3 सेकंड से ऊपर जाए, या कोई भी 5xx या timeout आए, तो रुक जाओ। एक छोटी guard स्क्रिप्ट CPU सीमा छूते ही रन रोक देती थी, ताकि test कभी आउटेज न बन सके।

  1. पहले रन से पहले abort threshold तय करके लिख लें।
  2. वही dashboard चलाएँ जो event के दिन इस्तेमाल करेंगे, अलग test-व्यू नहीं।
  3. test traffic पर एक हेडर लगाएँ, ताकि log में उसे अलग से छाना जा सके।
  4. अपने metric को cloud प्रोवाइडर के metric से मिलाएँ। मेरे रन में वे कुछ प्रतिशत के भीतर मिले; आपके न मिलें तो एक ग़लत है।
  5. सबसे पहले संतृप्त होने वाला रिसोर्स ढूँढें, उसे ठीक करें, फिर लक्ष्य रेट पर दोबारा चलाएँ।
  6. नतीजे कोड के पास रखें, ताकि अगला event एक बेसलाइन से शुरू हो।

डरावने alert पर हाथ डालने से पहले जाँच लें

दबाव में जो भी लाल दिखे, उसी पर टूट पड़ने का मन करता है। पहले जाँचें। कुछ कंसोल containers के नाम ग़लत दिखाते हैं, और "100% CPU" alert उस प्रोसेस से अलग किसी प्रोसेस की ओर इशारा कर सकता है जिसे आप रीस्टार्ट करने जा रहे हैं। असली प्रोसेस लिस्ट देखें, दूसरे स्रोत से मिलाएँ, फिर फ़ैसला करें।

फ़्लोचार्ट: alert बजता है; user-फ़ेसिंग कोई सिग्नल सहमत न हो तो असली प्रोसेस और दूसरे स्रोत से जाँच करें; सहमत हो तो पिछले 15 मिनट में deploy हुआ हो तो रोलबैक, वरना पहला संतृप्त रिसोर्स ढूँढकर ठीक करें और dashboard दोबारा देखें
लाल alert आगे कहाँ जाता है: ज़्यादातर काम यह तय करना है कि वह सच्चा है या नहीं।

अपने बैकग्राउंड काम पर भी यही बात लागू होती है। Health check और scheduler भी रिसोर्स खाते हैं। एक बार ऐसा हुआ कि हर बार नई प्रोसेस शुरू करने वाला health check लोड में अनाथ प्रोसेस छोड़कर database container को अस्थिर कर बैठा। निगरानी के अपने metric रखना वहम नहीं है।

अब वापस 10:01 पर। यह इंतज़ाम हो तो सपोर्ट का मैसेज request ID के साथ आता है। एक सर्च दिखा देती है कि nginx ने PHP के लिए 1.31 सेकंड इंतज़ार किया, और app ने उसी request पर एक धीमी query log की। dashboard भी यही कहता है: p95 चढ़ रहा है और 60 में से 48 FPM worker व्यस्त हैं। इकलौता लाल CPU alert किसी और container का निकलता है, इसलिए कोई कुछ रीस्टार्ट नहीं करता। लीड को वजह दस मिनट की जगह क़रीब दो मिनट में मिल जाती है, और फ़िक्स सही जगह जाता है। (यह भी काल्पनिक है।)

इस सीरीज़ के आख़िरी भाग में चालू रहने का दूसरा आधा हिस्सा है: एज को सुरक्षित करना और zero-downtime में बदलाव शिप करना।

मुख्य बातें

  • एज पर request ID जोड़ें और उसे nginx, PHP-FPM, app के log और queue जॉब तक ले जाएँ। यह सबसे सस्ता बड़ा फ़ायदा है।
  • फ़ील्ड के छोटे, स्थिर सेट के साथ स्ट्रक्चर्ड JSON log लिखें, निजी data हटाएँ, और retention सीमा सोच-समझकर तय करें।
  • चार golden signal मापें, औसत की जगह percentile इस्तेमाल करें, और saturation पर नज़र रखें: worker, queue age, connection, मेमोरी।
  • OpenTelemetry tracing सिर्फ़ critical path पर अपनाएँ, sampling के साथ, और trace ID को log से जोड़ें।
  • लोगों को सिर्फ़ SLO से जुड़े user-असर पर जगाएँ, और हर event के बाद शोर वाले alert छाँट दें।
  • abort threshold के साथ रिहर्सल करें, प्रोवाइडर के metric से मिलाएँ, और डरावने alert पर कार्रवाई से पहले उसे जाँच लें।

अनिचुर रहमान सॉफ़्टवेयर आर्किटेक्ट और StoreConsole के संस्थापक हैं। वे बढ़ते कारोबारों के लिए कॉमर्स और ERP सिस्टम डिज़ाइन करते हैं — ख़ास ध्यान event-driven आर्किटेक्चर, डेटा की शुद्धता और अपने सर्वर पर चलने वाले सिस्टम पर।

About the Author

Anichur Rahaman

Continue Reading