APISIX: etcd ऑपरेशन को HTTP से gRPC में माइग्रेट करें
February 10, 2023
Apache APISIX के HTTP-आधारित etcd ऑपरेशन की सीमाएं
जब etcd 2.x संस्करण में था, तो इसने जो API इंटरफेस एक्सपोज़ किया था वह HTTP 1 था (हम इसे अब से HTTP कहेंगे)। etcd को 3.x संस्करण में अपग्रेड करने के बाद, इसने प्रोटोकॉल को HTTP से gRPC में बदल दिया। जो उपयोगकर्ता gRPC का समर्थन नहीं करते हैं, उनके लिए etcd gRPC-Gateway प्रदान करता है जो HTTP अनुरोधों को gRPC के रूप में प्रॉक्सी करके नए gRPC API तक पहुंच प्रदान करता है।
जब APISIX ने etcd का उपयोग शुरू किया, तो इसने etcd v2 API का उपयोग किया। APISIX 2.0 (2020) में, हमने etcd के लिए आवश्यकता को 2.x से 3.x संस्करण में अपडेट किया। etcd का HTTP के साथ संगतता ने हमें संस्करण अपडेट के लिए प्रयास बचाया। हमें केवल कॉलिंग मेथड्स और प्रतिक्रियाओं को प्रोसेस करने के कोड को संशोधित करने की आवश्यकता थी। हालांकि, इन वर्षों में, हमें etcd के HTTP API से संबंधित कुछ समस्याएं भी मिली हैं। अभी भी कुछ सूक्ष्म अंतर हैं। हमने महसूस किया कि gRPC-gateway होने का मतलब यह नहीं है कि यह HTTP एक्सेस को पूरी तरह से समर्थन कर सकता है।
यहां etcd से संबंधित कुछ मुद्दों की सूची दी गई है जो हमें पिछले कुछ वर्षों में मिली हैं:
- gRPC-gateway डिफ़ॉल्ट रूप से अक्षम। मेन्टेनर की लापरवाही के कारण, कुछ प्रोजेक्ट्स में etcd का डिफ़ॉल्ट कॉन्फ़िगरेशन gRPC-gateway को सक्षम नहीं करता है। इसलिए हमें दस्तावेज़ में निर्देश जोड़ने पड़े कि वर्तमान etcd में gRPC-gateway सक्षम है या नहीं। देखें https://github.com/apache/apisix/pull/2940।
- डिफ़ॉल्ट रूप से, gRPC प्रतिक्रियाओं को 4MB तक सीमित करता है। etcd अपने प्रदान किए गए SDK में इस प्रतिबंध को हटा देता है लेकिन gRPC-gateway में नहीं। यह पता चला कि आधिकारिक etcdctl (इसके प्रदान किए गए SDK पर बना) ठीक काम करता है, लेकिन APISIX नहीं। देखें https://github.com/etcd-io/etcd/issues/12576।
- एक ही समस्या - इस बार एक ही कनेक्शन के लिए अधिकतम अनुरोधों की संख्या के साथ। Go का HTTP2 इम्प्लीमेंटेशन में
MaxConcurrentStreamsकॉन्फ़िगरेशन होता है जो नियंत्रित करता है कि एक क्लाइंट एक साथ कितने अनुरोध भेज सकता है, डिफ़ॉल्ट रूप से 250। कौन सा क्लाइंट सामान्य रूप से एक ही समय में 250 से अधिक अनुरोध भेजेगा? इसलिए etcd ने हमेशा इस कॉन्फ़िगरेशन का उपयोग किया है। हालांकि, gRPC-gateway, जो सभी HTTP अनुरोधों को स्थानीय gRPC इंटरफेस पर प्रॉक्सी करता है, इस सीमा को पार कर सकता है। देखें https://github.com/etcd-io/etcd/issues/14185। - etcd द्वारा mTLS सक्षम करने के बाद, etcd सर्वर सर्टिफिकेट और क्लाइंट सर्टिफिकेट दोनों के रूप में एक ही सर्टिफिकेट का उपयोग करता है: सर्वर सर्टिफिकेट gRPC-gateway के लिए और क्लाइंट सर्टिफिकेट जब gRPC-gateway gRPC इंटरफेस तक पहुंचता है। यदि सर्टिफिकेट पर सर्वर ऑथ एक्सटेंशन सक्षम है, लेकिन क्लाइंट ऑथ एक्सटेंशन सक्षम नहीं है, तो सर्टिफिकेट वेरिफिकेशन में एक त्रुटि होगी। एक बार फिर, etcdctl के साथ सीधे एक्सेस करना ठीक काम करता है (क्योंकि इस मामले में सर्टिफिकेट को क्लाइंट सर्टिफिकेट के रूप में उपयोग नहीं किया जाएगा), लेकिन APISIX नहीं। देखें https://github.com/etcd-io/etcd/issues/9785।
- mTLS सक्षम करने के बाद, etcd सर्टिफिकेट्स की उपयोगकर्ता जानकारी की सुरक्षा नीतियों को कॉन्फ़िगर करने की अनुमति देता है। जैसा कि ऊपर बताया गया है, gRPC-gateway gRPC इंटरफेस तक पहुंचते समय एक निश्चित क्लाइंट सर्टिफिकेट का उपयोग करता है न कि शुरुआत में HTTP इंटरफेस तक पहुंचने के लिए उपयोग किए गए सर्टिफिकेट की जानकारी का। इस प्रकार, यह सुविधा स्वाभाविक रूप से काम नहीं करेगी क्योंकि क्लाइंट सर्टिफिकेट निश्चित है और इसे बदला नहीं जाएगा। देखें https://github.com/apache/apisix/issues/5608।
हम इन समस्याओं को दो बिंदुओं में सारांशित कर सकते हैं:
- gRPC-gateway (और शायद HTTP को gRPC में बदलने के अन्य प्रयास) एक सिल्वर बुलेट नहीं है जो सभी समस्याओं को ठीक कर दे।
- etcd के डेवलपर्स HTTP से gRPC तरीके पर पर्याप्त जोर नहीं देते हैं। और उनका सबसे बड़ा उपयोगकर्ता, Kubernetes, इस सुविधा का उपयोग नहीं करता है।
इस समस्या को हल करने के लिए, हमें etcd को सीधे gRPC के माध्यम से उपयोग करने की आवश्यकता है, ताकि हमें gRPC-Gateway के HTTP पथ से नहीं गुजरना पड़े जो संगतता के लिए आरक्षित है।
gRPC में माइग्रेट करने की चुनौतियों को पार करना
lua-protobuf में बग
माइग्रेशन प्रक्रिया के दौरान हमारी पहली समस्या एक तृतीय-पक्ष लाइब्रेरी में एक अप्रत्याशित बग थी। अधिकांश OpenResty एप्लिकेशन्स की तरह, हम lua-protobuf का उपयोग protobuf को डिकोड/एनकोड करने के लिए करते हैं।
etcd के proto फ़ाइल को इंटीग्रेट करने के बाद, हमने पाया कि Lua कोड में कभी-कभी क्रैश होता है, जो "टेबल ओवरफ्लो" की त्रुटि रिपोर्ट करता है। क्योंकि इस क्रैश को विश्वसनीय रूप से पुनरुत्पादित नहीं किया जा सकता है, हमारी पहली प्रवृत्ति एक न्यूनतम पुनरुत्पादित उदाहरण खोजने की थी। दिलचस्प बात यह है कि यदि आप etcd के proto फ़ाइल का अकेले उपयोग करते हैं, तो आप समस्या को बिल्कुल भी पुनरुत्पादित नहीं कर सकते हैं। यह क्रैश केवल तब होता है जब APISIX चल रहा होता है।
कुछ डिबगिंग के बाद, मैंने समस्या को lua-protobuf में proto फ़ाइल के oneof फ़ील्ड को पार्स करते समय पाया। lua-protobuf टेबल का आकार पूर्व-आवंटित करने का प्रयास करता है जब पार्सिंग करता है, और आवंटित आकार एक विशेष मान के अनुसार गणना की जाती है। इस मान के नकारात्मक संख्या होने की एक निश्चित संभावना थी। फिर LuaJIT आवंटित करते समय इस संख्या को एक बड़ी सकारात्मक संख्या में परिवर्तित कर देता है, जिससे "टेबल ओवरफ्लो" त्रुटि होती है। मैंने इस मुद्दे को लेखक को रिपोर्ट किया और हमने आंतरिक रूप से एक वर्कअराउंड के साथ एक फोर्क बनाए रखा।
lua-protobuf लेखक बहुत प्रतिक्रियाशील थे, अगले दिन एक फिक्स प्रदान किया और कुछ दिनों बाद एक नया संस्करण जारी किया। यह पता चला कि जब lua-protobuf उन proto फ़ाइलों को साफ करता है जो अब उपयोग में नहीं हैं, तो यह कुछ फ़ील्ड्स को साफ करने में चूक गया, जिससे oneof को बाद में प्रोसेस करते समय एक अव्यवहारिक नकारात्मक संख्या हो गई। समस्या केवल कभी-कभी होती थी, और यही कारण है कि etcd proto फ़ाइल का अकेले उपयोग करते समय इसे पुनरुत्पादित नहीं किया जा सकता था क्योंकि यह इन फ़ील्ड्स को साफ करने के चरणों को चूक गया था।
HTTP व्यवहार के साथ संरेखित करना
माइग्रेशन प्रक्रिया के दौरान, मैंने पाया कि मौजूदा API सटीक निष्पादन परिणाम नहीं लौटाता है बल्कि एक HTTP प्रतिक्रिया लौटाता है जिसमें प्रतिक्रिया स्थिति और बॉडी होती है। और फिर, कॉलर को HTTP प्रतिक्रिया को स्वयं प्रोसेस करने की आवश्यकता होती है।
यदि प्रतिक्रियाएं gRPC में होतीं, तो उन्हें प्रोसेसिंग लॉजिक के साथ संरेखित करने के लिए एक HTTP प्रतिक्रिया शेल के साथ लपेटा जाना चाहिए। अन्यथा, कॉलर को नए (gRPC) प्रतिक्रिया प्रारूप के अनुकूल होने के लिए कोड को कई स्थानों पर संशोधित करने की आवश्यकता होती है। विशेष रूप से यह ध्यान में रखते हुए कि पुराने HTTP-आधारित etcd ऑपरेशन्स को भी एक साथ समर्थित करने की आवश्यकता है।
हालांकि HTTP प्रतिक्रिया के साथ संगत होने के लिए एक अतिरिक्त परत जोड़ना वांछनीय नहीं है, हमें इसके आसपास काम करना पड़ता है। इसके अलावा, हमें gRPC प्रतिक्रिया पर कुछ प्रोसेसिंग भी करने की आवश्यकता होती है। उदाहरण के लिए, जब कोई संबंधित डेटा नहीं होता है, तो HTTP कोई डेटा नहीं लौटाता है, लेकिन gRPC एक खाली टेबल लौटाता है। इसे HTTP व्यवहार के साथ संरेखित करने के लिए भी अनुकूलित करने की आवश्यकता होती है।
शॉर्ट कनेक्शन से लॉन्ग कनेक्शन तक
HTTP-आधारित etcd ऑपरेशन्स में, APISIX शॉर्ट कनेक्शन का उपयोग करता है, इसलिए कनेक्शन प्रबंधन पर विचार करने की आवश्यकता नहीं होती है। हमें केवल एक नया कनेक्शन शुरू करने की आवश्यकता होती है जब भी हमें आवश्यकता होती है और इसे बंद कर देते हैं जब हम कर लेते हैं।
लेकिन gRPC ऐसा नहीं कर सकता। gRPC में माइग्रेट करने का एक प्राथमिक उद्देश्य मल्टीप्लेक्सिंग प्राप्त करना है, जो प्राप्त नहीं किया जा सकता है यदि प्रत्येक ऑपरेशन के लिए एक नया gRPC कनेक्शन बनाया जाता है। यहां हमें gRPC-go का धन्यवाद करना चाहिए, क्योंकि इसकी अंतर्निहित कनेक्शन प्रबंधन क्षमता है, जो कनेक्शन टूटने पर स्वचालित रूप से पुनः कनेक्ट कर सकती है। इसलिए हम gRPC-go का उपयोग करके कनेक्शन को पुनः उपयोग कर सकते हैं। और केवल व्यावसायिक आवश्यकताओं पर APISIX स्तर पर विचार करने की आवश्यकता होती है।
APISIX के etcd ऑपरेशन्स को दो श्रेणियों में विभाजित किया जा सकता है, एक etcd डेटा पर CRUD (जोड़ें, हटाएं, संशोधित करें, क्वेरी) ऑपरेशन्स; दूसरा कंट्रोल प्लेन से कॉन्फ़िगरेशन को सिंक्रनाइज़ करना। हालांकि सैद्धांतिक रूप से, ये दो etcd ऑपरेशन्स एक ही gRPC कनेक्शन को साझा कर सकते हैं, हमने जिम्मेदारियों के अलगाव के लिए उन्हें दो कनेक्शन में विभाजित करने का निर्णय लिया। CRUD ऑपरेशन्स के कनेक्शन के लिए, चूंकि APISIX को स्टार्टअप पर और स्टार्टअप के बाद अलग-अलग तरीके से व्यवहार करने की आवश्यकता होती है, इसलिए एक नया कनेक्शन प्राप्त करते समय एक if स्टेटमेंट जोड़ा गया है। यदि कोई मिसमैच होता है (यानी वर्तमान कनेक्शन स्टार्टअप पर बनाया गया है जबकि हमें स्टार्टअप के बाद एक कनेक्शन की आवश्यकता है), तो हम वर्तमान कनेक्शन को बंद कर देंगे और एक नया बनाएंगे। मैंने कॉन्फ़िगरेशन सिंक्रनाइज़ेशन के लिए एक नई सिंक्रनाइज़ेशन विधि विकसित की है, ताकि प्रत्येक संसाधन मौजूदा कनेक्शन के तहत एक स्ट्रीम का उपयोग करके etcd को देख सके।
gRPC में माइग्रेट करने के लाभ
gRPC में माइग्रेट करने के बाद एक स्पष्ट लाभ यह है कि etcd को ऑपरेट करने के लिए आवश्यक कनेक्शन की संख्या बहुत कम हो गई है। जब etcd को HTTP के माध्यम से ऑपरेट किया जाता था, तो APISIX केवल शॉर्ट कनेक्शन का उपयोग कर सकता था। और जब कॉन्फ़िगरेशन को सिंक्रनाइज़ किया जाता था, तो प्रत्येक संसाधन का एक अलग कनेक्शन होता था।
gRPC में स्विच करने के बाद, हम gRPC के मल्टीप्लेक्सिंग फ़ंक्शन का उपयोग कर सकते हैं, और प्रत्येक संसाधन केवल एक स्ट्रीम का उपयोग करता है न कि एक पूर्ण कनेक्शन। इस तरह, कनेक्शन की संख्या अब संसाधनों की संख्या के साथ नहीं बढ़ती है। यह ध्यान में रखते हुए कि APISIX के बाद के विकास में अधिक संसाधन प्रकार शामिल होंगे, उदाहरण के लिए, नवीनतम संस्करण 3.1 में secrets जोड़ा गया है, और gRPC का उपयोग करके कनेक्शन की संख्या में कमी और भी महत्वपूर्ण होगी।
जब gRPC का उपयोग करके सिंक्रनाइज़ेशन किया जाता है, तो प्रत्येक प्रक्रिया में केवल एक (दो, यदि स्ट्रीम सबसिस्टम सक्षम है) कनेक्शन होता है जो कॉन्फ़िगरेशन सिंक्रनाइज़ेशन के लिए होता है। नीचे दिए गए चित्र में, हम देख सकते हैं कि दो प्रक्रियाओं में चार कनेक्शन हैं, जिनमें से दो कॉन्फ़िगरेशन सिंक्रनाइज़ेशन के लिए हैं, Admin API एक कनेक्शन का उपयोग करता है, और शेष कनेक्शन प्रिविलेज्ड एजेंट द्वारा सर्वर जानकारी रिपोर्ट करने के लिए होता है।

तुलना के लिए, नीचे दिया गया चित्र मूल कॉन्फ़िगरेशन सिंक्रनाइज़ेशन विधि का उपयोग करते समय 22 कनेक्शन की आवश्यकता को दर्शाता है जबकि अन्य पैरामीटर्स को अपरिवर्तित रखा गया है। इसके अलावा, ये कनेक्शन शॉर्ट कनेक्शन हैं।

इन दो कॉन्फ़िगरेशन के बीच एकमात्र अंतर यह है कि क्या etcd ऑपरेशन के लिए gRPC सक्षम है:
etcd: use_grpc: true host: - "http://127.0.0.1:2379" prefix: "/apisix" ...
कनेक्शन की संख्या को कम करने के अलावा, gRPC-gateway के बजाय सीधे gRPC का उपयोग करके etcd तक पहुंचने से लेख की शुरुआत में उल्लिखित mTLS प्रमाणीकरण जैसी वास्तुकला से संबंधित समस्याओं को हल किया जा सकता है। gRPC का उपयोग करने के बाद समस्याएं भी कम होंगी, क्योंकि Kubernetes etcd को ऑपरेट करने के लिए gRPC का उपयोग करता है। यदि कोई समस्या होती है, तो इसे Kubernetes समुदाय द्वारा खोजा जाएगा।
बेशक, चूंकि gRPC विधि अभी भी अपेक्षाकृत नई है, APISIX को etcd को gRPC के माध्यम से ऑपरेट करते समय कुछ नई समस्याएं हो सकती हैं। वर्तमान में, डिफ़ॉल्ट रूप से अभी भी मूल HTTP-आधारित विधि का उपयोग करके etcd को ऑपरेट किया जाता है। उपयोगकर्ता config.yaml में etcd के तहत use_grpc को true पर कॉन्फ़िगर करने का विकल्प चुन सकते हैं। आप यह देख सकते हैं कि gRPC विधि बेहतर है या नहीं। हम विभिन्न स्रोतों से प्रतिक्रिया एकत्र करके gRPC-आधारित etcd ऑपरेशन को सुधारने का प्रयास करेंगे। जब हम पाएंगे कि gRPC दृष्टिकोण पर्याप्त परिपक्व है, तो हम इसे डिफ़ॉल्ट दृष्टिकोण बना देंगे।
APISIX को अधिकतम करने के लिए, आपको API7 की आवश्यकता है
आप Apache APISIX के प्रदर्शन से प्यार करते हैं, न कि इसे प्रबंधित करने के ओवरहेड्स से। आप अपने मुख्य व्यवसाय पर ध्यान केंद्रित कर सकते हैं बिना कॉन्फ़िगर करने, बनाए रखने और अपडेट करने की चिंता किए।
हमारी टीम में Apache APISIX के निर्माता और योगदानकर्ता, OpenResty और NGINX के कोर मेन्टेनर्स, Kubernetes के सदस्य, और क्लाउड इंफ्रा पर उद्योग विशेषज्ञ शामिल हैं। आपको पर्दे के पीछे सर्वश्रेष्ठ लोग मिलते हैं।
क्या आप विश्वास के साथ अपने विकास को तेज करना चाहते हैं? APISIX समर्थन को अधिकतम करने के लिए, आपको API7 की आवश्यकता है। हम आपकी आवश्यकताओं के आधार पर APISIX और API प्रबंधन समाधान के लिए गहन समर्थन प्रदान करते हैं!
अभी हमसे संपर्क करें: https://api7.ai/contact।