कैसे एक क्लस्टर के लिए अपने ऑटोस्कोलर बनाने के लिए

नमस्ते! हम लोगों को बड़े डेटा के साथ काम करने के लिए प्रशिक्षित करते हैं। अपने स्वयं के क्लस्टर के बिना बड़े डेटा पर एक शैक्षिक कार्यक्रम की कल्पना करना असंभव है, जिस पर सभी प्रतिभागी एक साथ काम करते हैं। इस कारण से, हमारे पास हमेशा यह हमारे कार्यक्रम पर है :) हम इसकी ट्यूनिंग, ट्यूनिंग और प्रशासन में लगे हुए हैं, और लोग सीधे वहां MapReduce-jobs लॉन्च करते हैं और स्पार्क का उपयोग करते हैं।


इस पोस्ट में, हम यह वर्णन करेंगे कि हमने मेल ऑटोज़ क्लाउड सॉल्यूशंस क्लाउड का उपयोग करके अपने ऑटोसैक्टर को लिखकर असमान क्लस्टर लोडिंग की समस्या को कैसे हल किया।


समस्या


हमारे द्वारा उपयोग किया जाने वाला क्लस्टर काफी विशिष्ट नहीं है। निपटान अत्यधिक असमान है। उदाहरण के लिए, व्यावहारिक अभ्यास हैं जब सभी 30 लोग और शिक्षक क्लस्टर में प्रवेश करते हैं और इसका उपयोग करना शुरू करते हैं। या, फिर से, समय सीमा से पहले दिन होते हैं, जब भार नाटकीय रूप से बढ़ जाता है। बाकी समय, क्लस्टर अंडरलोड मोड में काम करता है।


समाधान # 1 एक क्लस्टर रखने के लिए है जो चोटी के भार का सामना करेगा, लेकिन बाकी समय बेकार रहेगा।


समाधान नंबर 2 एक छोटा क्लस्टर रखना है जिसमें कक्षाओं से पहले और पीक लोड के दौरान मैन्युअल रूप से नोड्स जोड़ना है।


समाधान # 3 एक छोटा क्लस्टर रखना है और एक ऑटोसैकर लिखना है जो वर्तमान क्लस्टर लोड की निगरानी करेगा और, विभिन्न एपीआई का उपयोग करके, क्लस्टर से नोड्स जोड़ और हटा देगा।


इस पोस्ट में हम निर्णय संख्या 3 के बारे में बात करेंगे। इस तरह का एक ऑटोसैकर बाहरी कारकों पर अत्यधिक निर्भर है, और आंतरिक लोगों पर नहीं, और प्रदाता अक्सर इसे प्रदान नहीं करते हैं। हम Mail.ru Cloud Solutions क्लाउड इंफ्रास्ट्रक्चर का उपयोग करते हैं और MCS API का उपयोग करते हुए एक ऑटोसैकर लिखा है। और जब से हम डेटा के साथ काम करने का प्रशिक्षण ले रहे हैं, हमने यह दिखाने का फैसला किया कि आप अपने उद्देश्यों के लिए एक समान ऑटोकैसलर कैसे लिख सकते हैं और अपने क्लाउड के साथ उपयोग कर सकते हैं


आवश्यक शर्तें


सबसे पहले, आपके पास एक Hadoop क्लस्टर होना चाहिए। उदाहरण के लिए, हम एचडीपी वितरण का उपयोग करते हैं।


ताकि आपके नोड्स को जल्दी से जोड़ा और हटाया जा सके, आपके पास नोड्स के बीच कुछ निश्चित भूमिकाएं होनी चाहिए।


  1. मास्टर नोड। खैर, यहां आपको कुछ भी विशेष रूप से समझाने की आवश्यकता नहीं है: क्लस्टर का मुख्य नोड, जिस पर, उदाहरण के लिए, स्पार्क ड्राइवर लॉन्च किया गया है, यदि आप इंटरैक्टिव मोड का उपयोग करते हैं।
  2. दिनांक नोड। यह वह नोड है जिस पर आप एचडीएफएस पर डेटा स्टोर करते हैं और उस पर गणना की जाती है।
  3. कम्प्यूटिंग नोड। यह एक नोड है जिस पर आप एचडीएफएस पर कुछ भी स्टोर नहीं करते हैं, लेकिन गणना उस पर की जाती है।

एक महत्वपूर्ण बिंदु। तीसरे प्रकार के नोड्स के कारण ऑटोस्कोलिंग होगा। यदि आप दूसरे प्रकार के नोड्स को चुनना और जोड़ना शुरू करते हैं, तो प्रतिक्रिया की गति बहुत कम होगी - विघटित और अनुशंसित आपके क्लस्टर पर घंटे लगेंगे। यह, निश्चित रूप से, आप ऑटोसालिंग से क्या उम्मीद नहीं करते हैं। यही है, हम पहले और दूसरे प्रकार के नोड्स को नहीं छूते हैं। वे एक न्यूनतम व्यवहार्य क्लस्टर होंगे जो पूरे कार्यक्रम में मौजूद होंगे।


तो, हमारा ऑटोसकोलर पायथन 3 में लिखा गया है, क्लस्टर सेवाओं का प्रबंधन करने के लिए अंबारी एपीआई का उपयोग करता है, मशीनों को शुरू करने और बंद करने के लिए Mail.ru Cloud Solutions (MCS) API का उपयोग करता है


समाधान वास्तुकला


  1. मॉड्यूल autoscaler.py इसमें तीन कक्षाएं पंजीकृत हैं: 1) अम्बारी के साथ काम करने के लिए, 2) एमसीएस के साथ काम करने के लिए 2) कार्य, 3) कार्य सीधे ऑटोसाल्कर तर्क से संबंधित हैं।
  2. स्क्रिप्ट observer.py वास्तव में, इसमें अलग-अलग नियम होते हैं: कब और किन क्षणों में ऑटोसालर फ़ंक्शन को कॉल करना है।
  3. कॉन्फ़िगरेशन मापदंडों के साथ फ़ाइल config.py । इसमें उदाहरण के लिए, ऑटोस्कोलिंग और अन्य मापदंडों के लिए अनुमत नोड्स की एक सूची है जो प्रभावित करते हैं, उदाहरण के लिए, एक नए नोड को जोड़ने के लिए कितना समय इंतजार करना पड़ता है। कक्षाओं की शुरुआत के टाइमस्टैम्प भी हैं, ताकि सत्र से पहले अधिकतम अनुमत क्लस्टर कॉन्फ़िगरेशन लॉन्च हो।

आइए पहले दो फाइलों के अंदर कोड के टुकड़ों को देखें।


1. autoscaler.py मॉड्यूल


कक्षा अम्बारी


यह कोड का टुकड़ा है जिसमें Ambari वर्ग शामिल है:


 class Ambari: def __init__(self, ambari_url, cluster_name, headers, auth): self.ambari_url = ambari_url self.cluster_name = cluster_name self.headers = headers self.auth = auth def stop_all_services(self, hostname): url = self.ambari_url + self.cluster_name + '/hosts/' + hostname + '/host_components/' url2 = self.ambari_url + self.cluster_name + '/hosts/' + hostname req0 = requests.get(url2, headers=self.headers, auth=self.auth) services = req0.json()['host_components'] services_list = list(map(lambda x: x['HostRoles']['component_name'], services)) data = { "RequestInfo": { "context":"Stop All Host Components", "operation_level": { "level":"HOST", "cluster_name": self.cluster_name, "host_names": hostname }, "query":"HostRoles/component_name.in({0})".format(",".join(services_list)) }, "Body": { "HostRoles": { "state":"INSTALLED" } } } req = requests.put(url, data=json.dumps(data), headers=self.headers, auth=self.auth) if req.status_code in [200, 201, 202]: message = 'Request accepted' else: message = req.status_code return message 

उपरोक्त उदाहरण के लिए, आप stop_all_services फ़ंक्शन के कार्यान्वयन को देख सकते हैं, जो वांछित क्लस्टर नोड पर सभी सेवाओं को रोकता है।


Ambari वर्ग के लिए आप पास:


  • ambari_url , उदाहरण के लिए, 'http://localhost:8080/api/v1/clusters/' फॉर्म
  • cluster_name अंबारी में आपके क्लस्टर का नाम है,
  • headers = {'X-Requested-By': 'ambari'}
  • और अंदर स्थित अंबारी से आपका उपयोगकर्ता नाम और पासवर्ड निहित है: auth = ('login', 'password')

फ़ंक्शन स्वयं REST API के माध्यम से Ambari पर कॉल के एक जोड़े से अधिक कुछ नहीं है। तर्क की दृष्टि से, हमें पहले नोड पर चलने वाली सेवाओं की एक सूची मिलती है, और फिर हम इस नोड पर क्लस्टर से, सेवाओं को सूची से INSTALLED अवस्था में स्थानांतरित करने के लिए कहते हैं। Maintenance राज्य में नोड्स लगाने के लिए सभी सेवाओं को शुरू करने के लिए कार्य, आदि समान दिखते हैं - वे एपीआई के माध्यम से केवल कुछ अनुरोध हैं।


क्लास एमकेएस


यह कोड का टुकड़ा है जिसमें Mcs वर्ग शामिल है:


 class Mcs: def __init__(self, id1, id2, password): self.id1 = id1 self.id2 = id2 self.password = password self.mcs_host = 'https://infra.mail.ru:8774/v2.1' def vm_turn_on(self, hostname): self.token = self.get_mcs_token() host = self.hostname_to_vmname(hostname) vm_id = self.get_vm_id(host) mcs_url1 = self.mcs_host + '/servers/' + self.vm_id + '/action' headers = { 'X-Auth-Token': '{0}'.format(self.token), 'Content-Type': 'application/json' } data = {'os-start' : 'null'} mcs = requests.post(mcs_url1, data=json.dumps(data), headers=headers) return mcs.status_code 

Mcs वर्ग के लिए, हम क्लाउड और उपयोगकर्ता आईडी के साथ-साथ उसके पासवर्ड के साथ प्रोजेक्ट आईडी पास करते हैं। vm_turn_on फ़ंक्शन में हम किसी एक मशीन को सक्षम करना चाहते हैं। यहाँ तर्क थोड़ा और जटिल है। कोड की शुरुआत में, तीन अन्य कार्यों को कहा जाता है: 1) हमें टोकन प्राप्त करने की आवश्यकता है, 2) हमें होस्टनाम को एमसीएस में मशीन के नाम में बदलने की आवश्यकता है, 3) इस मशीन की आईडी प्राप्त करें। अगला, हम एक सरल पोस्ट-अनुरोध करते हैं और इस मशीन को चलाते हैं।


इस प्रकार टोकन प्राप्त करने का कार्य दिखता है:


 def get_mcs_token(self): url = 'https://infra.mail.ru:35357/v3/auth/tokens?nocatalog' headers = {'Content-Type': 'application/json'} data = { 'auth': { 'identity': { 'methods': ['password'], 'password': { 'user': { 'id': self.id1, 'password': self.password } } }, 'scope': { 'project': { 'id': self.id2 } } } } params = (('nocatalog', ''),) req = requests.post(url, data=json.dumps(data), headers=headers, params=params) self.token = req.headers['X-Subject-Token'] return self.token 

क्लास ऑटोसालर


इस वर्ग में कार्य के तर्क से संबंधित कार्य शामिल हैं।


इस तरह इस वर्ग का एक कोड दिखता है:


 class Autoscaler: def __init__(self, ambari, mcs, scaling_hosts, yarn_ram_per_node, yarn_cpu_per_node): self.scaling_hosts = scaling_hosts self.ambari = ambari self.mcs = mcs self.q_ram = deque() self.q_cpu = deque() self.num = 0 self.yarn_ram_per_node = yarn_ram_per_node self.yarn_cpu_per_node = yarn_cpu_per_node def scale_down(self, hostname): flag1 = flag2 = flag3 = flag4 = flag5 = False if hostname in self.scaling_hosts: while True: time.sleep(5) status1 = self.ambari.decommission_nodemanager(hostname) if status1 == 'Request accepted' or status1 == 500: flag1 = True logging.info('Decomission request accepted: {0}'.format(flag1)) break while True: time.sleep(5) status3 = self.ambari.check_service(hostname, 'NODEMANAGER') if status3 == 'INSTALLED': flag3 = True logging.info('Nodemaneger decommissioned: {0}'.format(flag3)) break while True: time.sleep(5) status2 = self.ambari.maintenance_on(hostname) if status2 == 'Request accepted' or status2 == 500: flag2 = True logging.info('Maintenance request accepted: {0}'.format(flag2)) break while True: time.sleep(5) status4 = self.ambari.check_maintenance(hostname, 'NODEMANAGER') if status4 == 'ON' or status4 == 'IMPLIED_FROM_HOST': flag4 = True self.ambari.stop_all_services(hostname) logging.info('Maintenance is on: {0}'.format(flag4)) logging.info('Stopping services') break time.sleep(90) status5 = self.mcs.vm_turn_off(hostname) while True: time.sleep(5) status5 = self.mcs.get_vm_info(hostname)['server']['status'] if status5 == 'SHUTOFF': flag5 = True logging.info('VM is turned off: {0}'.format(flag5)) break if flag1 and flag2 and flag3 and flag4 and flag5: message = 'Success' logging.info('Scale-down finished') logging.info('Cooldown period has started. Wait for several minutes') return message 

इनपुट के लिए हम Ambari और Mcs कक्षाओं को लेते हैं, स्केलिंग के लिए अनुमति देने वाले नोड्स की सूची, साथ ही नोड्स के कॉन्फ़िगरेशन पैरामीटर: स्मृति और सीपीयू को यार्न में नोड को आवंटित किया गया। 2 आंतरिक पैरामीटर q_ram, q_cpu भी हैं, जो कतार में हैं। उनका उपयोग करते हुए, हम वर्तमान क्लस्टर लोड के मूल्यों को संग्रहीत करते हैं। यदि हम देखते हैं कि पिछले 5 मिनटों में स्थिर वृद्धि हुई है, तो हम तय करते हैं कि हमें क्लस्टर में +1 नोड जोड़ने की आवश्यकता है। क्लस्टर अंडरलोड स्थिति के लिए भी यही सच है।


उपरोक्त कोड एक फ़ंक्शन का एक उदाहरण दिखाता है जो एक मशीन को क्लस्टर से निकालता है और इसे क्लाउड में रोकता है। सबसे पहले, YARN Nodemanager , फिर Maintenance मोड चालू होता है, फिर हम मशीन पर सभी सेवाओं को बंद कर देते हैं और क्लाउड में वर्चुअल मशीन को बंद कर देते हैं।


2. स्क्रिप्ट प्रेक्षक


वहां से नमूना कोड:


 if scaler.assert_up(config.scale_up_thresholds) == True: hostname = cloud.get_vm_to_up(config.scaling_hosts) if hostname != None: status1 = scaler.scale_up(hostname) if status1 == 'Success': text = {"text": "{0} has been successfully scaled-up".format(hostname)} post = {"text": "{0}".format(text)} json_data = json.dumps(post) req = requests.post(webhook, data=json_data.encode('ascii'), headers={'Content-Type': 'application/json'}) time.sleep(config.cooldown_period*60) 

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


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


निष्कर्ष


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

Source: https://habr.com/ru/post/hi482198/


All Articles