paint-brush
गो में शानदार शटडाउन में महारत हासिल करना: कुबेरनेट्स के लिए एक व्यापक गाइडद्वारा@gopher

गो में शानदार शटडाउन में महारत हासिल करना: कुबेरनेट्स के लिए एक व्यापक गाइड

द्वारा Alex6m2024/08/14
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

यह मार्गदर्शिका सुंदर शटडाउन की दुनिया में प्रवेश करेगी, विशेष रूप से कुबेरनेट्स पर चलने वाले गो अनुप्रयोगों में उनके कार्यान्वयन पर ध्यान केंद्रित करेगी।
featured image - गो में शानदार शटडाउन में महारत हासिल करना: कुबेरनेट्स के लिए एक व्यापक गाइड
Alex HackerNoon profile picture

क्या आपने कभी निराशा में अपने कंप्यूटर से पावर कॉर्ड को खींचकर बाहर निकाला है? हालांकि यह एक त्वरित समाधान की तरह लग सकता है, लेकिन इससे डेटा हानि और सिस्टम अस्थिरता हो सकती है। सॉफ़्टवेयर की दुनिया में, एक समान अवधारणा मौजूद है: हार्ड शटडाउन। यह अचानक समाप्ति इसके भौतिक समकक्ष की तरह ही समस्याएँ पैदा कर सकती है। शुक्र है, एक बेहतर तरीका है: शालीन शटडाउन।


सुंदर शटडाउन को एकीकृत करके, हम सेवा को अग्रिम सूचना प्रदान करते हैं। यह इसे चल रहे अनुरोधों को पूरा करने, संभावित रूप से डिस्क पर स्थिति की जानकारी को सहेजने और अंततः शटडाउन के दौरान डेटा भ्रष्टाचार से बचने में सक्षम बनाता है।


यह मार्गदर्शिका सुंदर शटडाउन की दुनिया में प्रवेश करेगी, विशेष रूप से कुबेरनेट्स पर चलने वाले गो अनुप्रयोगों में उनके कार्यान्वयन पर ध्यान केंद्रित करेगी।

यूनिक्स सिस्टम में सिग्नल

यूनिक्स-आधारित सिस्टम में सुंदर शटडाउन प्राप्त करने के लिए प्रमुख उपकरणों में से एक सिग्नल की अवधारणा है, जो सरल शब्दों में, एक प्रक्रिया से दूसरी प्रक्रिया तक एक विशिष्ट चीज़ को संप्रेषित करने का एक सरल तरीका है। सिग्नल कैसे काम करते हैं, यह समझकर हम अपने अनुप्रयोगों के भीतर नियंत्रित समाप्ति प्रक्रियाओं को लागू करने के लिए उनका लाभ उठा सकते हैं, जिससे एक सुचारू और डेटा-सुरक्षित शटडाउन प्रक्रिया सुनिश्चित होती है।


कई संकेत हैं, और आप उन्हें यहां पा सकते हैं, लेकिन हमारी चिंता केवल शटडाउन संकेत हैं:

  • SIGTERM - किसी प्रक्रिया को समाप्त करने के लिए अनुरोध करने के लिए भेजा जाता है। सबसे अधिक इस्तेमाल किया जाता है, और हम बाद में इस पर ध्यान केंद्रित करेंगे।
  • सिगकिल - "तुरंत छोड़ो", इसमें हस्तक्षेप नहीं किया जा सकता।
  • SIGINT - व्यवधान संकेत (जैसे Ctrl+C)
  • SIGQUIT - छोड़ने का संकेत (जैसे Ctrl+D)


ये संकेत उपयोगकर्ता (Ctrl+C / Ctrl+D), किसी अन्य प्रोग्राम/प्रक्रिया से, या स्वयं सिस्टम (कर्नेल/OS) से भेजे जा सकते हैं, उदाहरण के लिए, OS द्वारा एक SIGSEGV उर्फ सेगमेंटेशन फॉल्ट भेजा जाता है।


हमारी गिनी पिग सेवा

व्यावहारिक सेटिंग में सुंदर शटडाउन की दुनिया का पता लगाने के लिए, आइए एक सरल सेवा बनाएं जिसके साथ हम प्रयोग कर सकते हैं। इस "गिनी पिग" सेवा में एक एकल समापन बिंदु होगा जो Redis के INCR कमांड को कॉल करके कुछ वास्तविक दुनिया के काम का अनुकरण करता है (हम थोड़ी देरी जोड़ेंगे)। हम यह परीक्षण करने के लिए एक बुनियादी Kubernetes कॉन्फ़िगरेशन भी प्रदान करेंगे कि प्लेटफ़ॉर्म समाप्ति संकेतों को कैसे संभालता है।


अंतिम लक्ष्य: सुनिश्चित करें कि हमारी सेवा बिना किसी अनुरोध/डेटा को खोए शटडाउन को सुचारू रूप से संभालती है। Redis में अंतिम काउंटर मान के साथ समानांतर में भेजे गए अनुरोधों की संख्या की तुलना करके, हम यह सत्यापित करने में सक्षम होंगे कि हमारा सुचारू शटडाउन कार्यान्वयन सफल है या नहीं।

हम Kubernetes क्लस्टर और Redis की स्थापना के विवरण में नहीं जाएंगे, लेकिन आप हमारे Github रिपॉजिटरी में पूर्ण सेटअप पा सकते हैं।


सत्यापन प्रक्रिया निम्नलिखित है:

  1. Redis और Go अनुप्रयोग को Kubernetes पर तैनात करें।
  2. 1000 अनुरोध (40 सेकंड में 25/s) भेजने के लिए वेजिटा का उपयोग करें।
  3. जब वेजिटा चल रहा हो, तो छवि टैग को अपडेट करके कुबेरनेट्स रोलिंग अपडेट आरंभ करें।
  4. “काउंटर” को सत्यापित करने के लिए Redis से कनेक्ट करें, यह 1000 होना चाहिए।


आइये अपने बेस गो HTTP सर्वर से शुरुआत करें।

हार्ड-शटडाउन/main.go

 package main import ( "net/http" "os" "time" "github.com/go-redis/redis" ) func main() { redisdb := redis.NewClient(&redis.Options{ Addr: os.Getenv("REDIS_ADDR"), }) server := http.Server{ Addr: ":8080", } http.HandleFunc("/incr", func(w http.ResponseWriter, r *http.Request) { go processRequest(redisdb) w.WriteHeader(http.StatusOK) }) server.ListenAndServe() } func processRequest(redisdb *redis.Client) { // simulate some business logic here time.Sleep(time.Second * 5) redisdb.Incr("counter") }

जब हम इस कोड का उपयोग करके अपनी सत्यापन प्रक्रिया चलाते हैं तो हम देखते हैं कि कुछ अनुरोध विफल हो जाते हैं और काउंटर 1000 से कम है (प्रत्येक बार संख्या भिन्न हो सकती है)।


इसका स्पष्ट अर्थ है कि हमने रोलिंग अपडेट के दौरान कुछ डेटा खो दिया है।

गो में सिग्नल संभालना

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

ऐसा करने के दो विकल्प हैं।


चैनल का उपयोग:

 c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM)


संदर्भ का उपयोग करना (आजकल पसंदीदा दृष्टिकोण):

 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop()


NotifyContext, मूल संदर्भ की एक प्रतिलिपि लौटाता है, जिसे पूर्ण के रूप में चिह्नित किया जाता है (इसका पूर्ण चैनल बंद हो जाता है), जब सूचीबद्ध संकेतों में से एक आता है, जब लौटाए गए stop() फ़ंक्शन को कॉल किया जाता है, या जब मूल संदर्भ का पूर्ण चैनल बंद हो जाता है, जो भी पहले हो।


HTTP सर्वर के हमारे वर्तमान कार्यान्वयन में कुछ समस्याएं हैं:

  1. हमारे पास एक धीमी प्रक्रिया अनुरोध गोरूटीन है, और चूंकि हम समाप्ति संकेत को संभाल नहीं पाते हैं, इसलिए प्रोग्राम स्वचालित रूप से बाहर निकल जाता है, जिसका अर्थ है कि सभी चल रहे गोरूटीन भी समाप्त हो जाते हैं।
  2. प्रोग्राम कोई भी कनेक्शन बंद नहीं करता है।


आइये इसे पुनः लिखें।


ग्रेसफुल-शटडाउन/main.go

 package main // imports var wg sync.WaitGroup func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop() // redisdb, server http.HandleFunc("/incr", func(w http.ResponseWriter, r *http.Request) { wg.Add(1) go processRequest(redisdb) w.WriteHeader(http.StatusOK) }) // make it a goroutine go server.ListenAndServe() // listen for the interrupt signal <-ctx.Done() // stop the server if err := server.Shutdown(context.Background()); err != nil { log.Fatalf("could not shutdown: %v\n", err) } // wait for all goroutines to finish wg.Wait() // close redis connection redisdb.Close() os.Exit(0) } func processRequest(redisdb *redis.Client) { defer wg.Done() // simulate some business logic here time.Sleep(time.Second * 5) redisdb.Incr("counter") }


अपडेट का सारांश इस प्रकार है:

  • SIGTERM समाप्ति सिग्नल को सुनने के लिए signal.NotifyContext जोड़ा गया।
  • इन-फ़्लाइट अनुरोधों (प्रोसेसरिक्वेस्ट गोरूटीन्स) को ट्रैक करने के लिए sync.WaitGroup की शुरुआत की गई।
  • सर्वर को गोरूटीन में लपेटा गया तथा नये कनेक्शनों को स्वीकार करना सुचारू रूप से रोकने के लिए संदर्भ के साथ server.Shutdown का प्रयोग किया गया।
  • आगे बढ़ने से पहले सभी इन-फ़्लाइट अनुरोधों (प्रोसेसरिक्वेस्ट गोरूटीन) को समाप्त करने के लिए wg.Wait() का उपयोग किया गया।
  • संसाधन सफ़ाई: बाहर निकलने से पहले Redis कनेक्शन को ठीक से बंद करने के लिए redisdb.Close() जोड़ा गया।
  • स्वच्छ निकास: सफल समाप्ति को इंगित करने के लिए os.Exit(0) का उपयोग किया जाता है।

अब, यदि हम अपनी सत्यापन प्रक्रिया दोहराते हैं तो हम देखेंगे कि सभी 1000 अनुरोध सही ढंग से संसाधित किए गए हैं।


वेब फ्रेमवर्क / HTTP लाइब्रेरी

इको, जिन, फाइबर और अन्य जैसे फ्रेमवर्क प्रत्येक आने वाले अनुरोधों के लिए एक गोरूटीन बनाएंगे, इसे एक संदर्भ देंगे और फिर आपके द्वारा तय किए गए रूटिंग के आधार पर आपके फ़ंक्शन / हैंडलर को कॉल करेंगे। हमारे मामले में यह "/incr" पथ के लिए HandleFunc को दिया गया अनाम फ़ंक्शन होगा।


जब आप SIGTERM सिग्नल को रोकते हैं और अपने फ्रेमवर्क को सुचारू रूप से बंद करने के लिए कहते हैं, तो 2 महत्वपूर्ण चीजें होती हैं (सरलीकृत करने के लिए):

  • आपका फ्रेमवर्क आने वाले अनुरोधों को स्वीकार करना बंद कर देता है
  • यह किसी भी मौजूदा आने वाले अनुरोध के समाप्त होने की प्रतीक्षा करता है (अंतर्निहित रूप से गोरूटीन के समाप्त होने की प्रतीक्षा करता है)।


नोट: Kubernetes लोडबैलेंसर को समाप्त होने वाला लेबल देने के बाद, लोडबैलेंसर से आने वाले ट्रैफ़िक को आपके पॉड पर भेजना भी बंद कर देता है।

वैकल्पिक: शटडाउन टाइमआउट

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


 shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() go func() { if err := server.Shutdown(shutdownCtx); err != nil { log.Fatalf("could not shutdown: %v\n", err) } }() select { case <-shutdownCtx.Done(): if shutdownCtx.Err() == context.DeadlineExceeded { log.Fatalln("timeout exceeded, forcing shutdown") } os.Exit(0) }

Kubernetes समाप्ति जीवनचक्र

चूँकि हमने अपनी सेवा को तैनात करने के लिए Kubernetes का उपयोग किया है, तो आइए इस बात पर गहराई से विचार करें कि यह पॉड्स को कैसे समाप्त करता है। एक बार जब Kubernetes पॉड को समाप्त करने का फैसला करता है, तो निम्नलिखित घटनाएँ घटित होंगी:

  1. पॉड को "समाप्त" स्थिति पर सेट किया जाता है और सभी सेवाओं की एंडपॉइंट सूची से हटा दिया जाता है।
  2. यदि preStop Hook परिभाषित है तो उसे निष्पादित किया जाता है।
  3. SIGTERM सिग्नल पॉड को भेजा जाता है। लेकिन अब हमारा एप्लीकेशन जानता है कि क्या करना है!
  4. Kubernetes एक अनुग्रह अवधि ( terminationGracePeriodSeconds ) की प्रतीक्षा करता है, जो डिफ़ॉल्ट रूप से 30s होती है।
  5. SIGKILL सिग्नल पॉड को भेजा जाता है, और पॉड को हटा दिया जाता है।

जैसा कि आप देख सकते हैं, यदि आपके पास लंबे समय तक चलने वाली समाप्ति प्रक्रिया है, तो endingGracePeriodSeconds सेटिंग को बढ़ाना आवश्यक हो सकता है, जिससे आपके एप्लिकेशन को सुचारू रूप से बंद होने के लिए पर्याप्त समय मिल सके।

निष्कर्ष

ग्रेसफुल शटडाउन डेटा अखंडता की रक्षा करते हैं, एक सहज उपयोगकर्ता अनुभव बनाए रखते हैं, और संसाधन प्रबंधन को अनुकूलित करते हैं। अपने समृद्ध मानक पुस्तकालय और समवर्तीता पर जोर देने के साथ, Go डेवलपर्स को सहजता से ग्रेसफुल शटडाउन प्रथाओं को एकीकृत करने में सक्षम बनाता है - जो Kubernetes जैसे कंटेनरीकृत या ऑर्केस्ट्रेटेड वातावरण में तैनात अनुप्रयोगों के लिए एक आवश्यकता है।

आप हमारे गिटहब रिपॉजिटरी में गो कोड और कुबेरनेट्स मैनिफ़ेस्ट पा सकते हैं।

संसाधन