ለክላስተር የራስዎን አውቶማቲክ እንዴት እንደሚሰራ

ሀሎ! ሰዎች በትልቁ ዳታ እንዲሰሩ እናሠለጥናለን። ሁሉም ተሳታፊዎች አብረው የሚሰሩበት የራሱ ዘለላ ሳይኖር በትልቁ መረጃ ላይ ትምህርታዊ ፕሮግራም መገመት አይቻልም። በዚህ ምክንያት ፕሮግራማችን ሁል ጊዜ አለው :) በአወቃቀሩ ፣በማስተካከል እና በአስተዳደር ስራ ላይ ተሰማርተናል እና ሰዎቹ እዚያ MapReduce ስራዎችን በቀጥታ አስጀምረዋል እና ስፓርክን ይጠቀማሉ።

በዚህ ጽሁፍ ላይ ደመናውን ተጠቅመን የራሳችንን አውቶማቲክ በመፃፍ ያልተስተካከለ የክላስተር ጭነት ችግር እንዴት እንደፈታን እንነግራችኋለን። Mail.ru የደመና መፍትሄዎች.

ችግር

የእኛ ክላስተር በተለመደው ሁነታ ጥቅም ላይ አይውልም. አወጋገድ በጣም ያልተስተካከለ ነው። ለምሳሌ፣ ሁሉም 30 ሰዎች እና አስተማሪ ወደ ክላስተር ሄደው መጠቀም ሲጀምሩ ተግባራዊ ትምህርቶች አሉ። ወይም ደግሞ, ጭነቱ በጣም በሚጨምርበት ጊዜ ከማለቁ ቀናት በፊት ቀናት አሉ. ቀሪው ጊዜ ክላስተር በዝቅተኛ ጭነት ሁነታ ይሰራል።

መፍትሄ #1 ከፍተኛ ሸክሞችን የሚቋቋም ዘለላ ማስቀመጥ ነው፣ ነገር ግን በቀሪው ጊዜ ስራ ፈት ይሆናል።

መፍትሄ ቁጥር 2 ትንሽ ዘለላ ማቆየት ነው, ይህም ከመማሪያ ክፍሎች በፊት እና በከፍተኛ ጭነት ወቅት እራስዎ ኖዶችን ይጨምራሉ.

መፍትሄ #3 ትንሽ ዘለላ ማስቀመጥ እና የክላስተርን ወቅታዊ ጭነት የሚከታተል እና የተለያዩ ኤፒአይዎችን በመጠቀም ኖዶችን በመጨመር እና በማንሳት አውቶማቲክ መፃፍ ነው።

በዚህ ጽሑፍ ውስጥ ስለ መፍትሄ ቁጥር 3 እንነጋገራለን. ይህ አውቶማቲክ መለኪያ ከውስጥ ይልቅ በውጫዊ ሁኔታዎች ላይ በጣም ጥገኛ ነው, እና አቅራቢዎች ብዙውን ጊዜ አይሰጡትም. የMail.ru Cloud Solutions ደመና መሠረተ ልማትን እንጠቀማለን እና ኤምሲኤስ ኤፒአይን በመጠቀም አውቶማቲክን ጻፍን። እና ከውሂብ ጋር እንዴት መስራት እንዳለብን ስለምናስተምር ለእራስዎ ዓላማ ተመሳሳይ አውቶማቲክ መፃፍ እና ከደመና ጋር እንዴት እንደሚጠቀሙበት ለማሳየት ወስነናል

ቅድመ-ሁኔታዎች

በመጀመሪያ የHadoop ክላስተር ሊኖርዎት ይገባል። ለምሳሌ, የ HDP ስርጭትን እንጠቀማለን.

አንጓዎችዎ በፍጥነት እንዲታከሉ እና እንዲወገዱ፣ በመስቀለኛ መንገዱ መካከል የተወሰነ የተግባር ስርጭት ሊኖርዎት ይገባል።

  1. ዋና መስቀለኛ መንገድ. ደህና ፣ እዚህ ለማብራራት ምንም የተለየ አስፈላጊ ነገር የለም-የክላስተር ዋና መስቀለኛ መንገድ ፣ ለምሳሌ ፣ የስፓርክ ሾፌር ተጀምሯል ፣ በይነተገናኝ ሁነታን ከተጠቀሙ።
  2. የቀን መስቀለኛ መንገድ. ይህ በኤችዲኤፍኤስ ላይ ውሂብ የሚያከማቹበት እና ስሌቶች የሚከናወኑበት መስቀለኛ መንገድ ነው።
  3. የኮምፒውተር መስቀለኛ መንገድ. ይህ በኤችዲኤፍኤስ ላይ ምንም ነገር የማትከማቹበት፣ ነገር ግን ስሌቶች የሚከሰቱበት መስቀለኛ መንገድ ነው።

ጠቃሚ ነጥብ. በሦስተኛው ዓይነት አንጓዎች ምክንያት አውቶማቲካሊንግ ይከሰታል። የሁለተኛው ዓይነት አንጓዎችን መውሰድ እና መጨመር ከጀመሩ የምላሽ ፍጥነቱ በጣም ዝቅተኛ ይሆናል - ማሰናከል እና መልሶ ማቋቋም በክላስተርዎ ላይ ሰዓታትን ይወስዳል። ይህ በእርግጥ ከራስ-ስኬል የሚጠብቁት አይደለም. ማለትም የመጀመሪያዎቹን እና የሁለተኛውን ዓይነቶች አንጓዎች አንነካም. በፕሮግራሙ ጊዜ ውስጥ የሚኖረውን አነስተኛ አዋጭ ዘለላ ይወክላሉ።

ስለዚህ፣ የእኛ አውቶማቲክ መለኪያ በፓይዘን 3 ተጽፏል፣ የክላስተር አገልግሎቶችን፣ አጠቃቀሞችን ለማስተዳደር Ambri API ይጠቀማል API ከ Mail.ru Cloud Solutions (MCS) ለመጀመር እና ለማቆሚያ ማሽኖች.

የመፍትሄው አርክቴክቸር

  1. ሞዱል autoscaler.py. ሶስት ክፍሎችን ይዟል፡ 1) ከአምባሪ ጋር ለመስራት ተግባራት፣ 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 ለአምባሪ የተጠቃሚ ስምህ እና ይለፍ ቃልህ ይኸውልህ፡- 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

Autoscaler ክፍል

ይህ ክፍል ከኦፕሬቲንግ ሎጂክ ራሱ ጋር የተያያዙ ተግባራትን ይዟል።

የዚህ ክፍል ኮድ ይህን ይመስላል።

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, ለመለካት የሚፈቀዱ የአንጓዎች ዝርዝር, እንዲሁም የመስቀለኛ ውቅር መለኪያዎች: ማህደረ ትውስታ እና ሲፒዩ በ YARN ውስጥ ላለው መስቀለኛ ክፍል ይመደባሉ. እንዲሁም 2 የውስጥ መለኪያዎች q_ram፣ q_cpu፣ እነሱም ወረፋዎች አሉ። እነሱን በመጠቀም, የአሁኑን የክላስተር ጭነት ዋጋዎችን እናከማቻለን. ባለፉት 5 ደቂቃዎች ውስጥ በተከታታይ እየጨመረ የሚሄድ ጭነት እንዳለ ከተመለከትን ወደ ክላስተር +1 መስቀለኛ መንገድ መጨመር እንዳለብን እንወስናለን. ለጥቅል-ጥቅም-አልባ ሁኔታ ሁኔታም ተመሳሳይ ነው።

ከላይ ያለው ኮድ ማሽንን ከጥቅል ውስጥ አውጥቶ በደመና ውስጥ የሚያቆመው ተግባር ምሳሌ ነው። በመጀመሪያ ማሰናከል አለ YARN Nodemanager, ከዚያ ሁነታው ይበራል Maintenance, ከዚያም ሁሉንም አገልግሎቶች በማሽኑ ላይ እናቆማለን እና ቨርቹዋል ማሽንን በደመና ውስጥ እናጠፋለን.

2. ስክሪፕት ተመልካች.py

የናሙና ኮድ ከዚያ፡

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)

በውስጡ፣ የክላስተርን አቅም ለመጨመር ሁኔታዎች መፈጠሩን እና በመጠባበቂያ ውስጥ ማሽነሪዎች መኖራቸውን እንፈትሻለን፣ የአንዱን አስተናጋጅ ስም አግኝ፣ ወደ ክላስተር ጨምረን እና ስለእሱ መልእክት በቡድናችን Slack ላይ እናተምታለን። ከዚያ በኋላ ይጀምራል cooldown_period, ከጥቅሉ ውስጥ ምንም ነገር ሳንጨምር ወይም ሳናስወግድ, ነገር ግን ጭነቱን በቀላሉ ይቆጣጠሩ. የተረጋጋ ከሆነ እና በተሻሉ የጭነት ዋጋዎች ውስጥ በአገናኝ መንገዱ ውስጥ ከሆነ በቀላሉ ክትትልን እንቀጥላለን። አንድ መስቀለኛ መንገድ በቂ ካልሆነ ሌላ እንጨምራለን.

ወደፊት ትምህርት ሲኖረን, አንድ መስቀለኛ መንገድ በቂ እንደማይሆን አስቀድመን አውቀናል, ስለዚህ ወዲያውኑ ሁሉንም ነፃ ኖዶች እንጀምራለን እና እስከ ትምህርቱ መጨረሻ ድረስ ንቁ እንዲሆኑ እናደርጋለን. ይህ የሚሆነው የእንቅስቃሴ ጊዜ ማህተም ዝርዝርን በመጠቀም ነው።

መደምደሚያ

እኩል ያልሆነ የክላስተር ጭነት ሲያጋጥምዎት Autoscaler ጥሩ እና ምቹ መፍትሄ ነው። ለከፍተኛ ጭነት የሚፈለገውን የክላስተር ውቅር በተመሳሳይ ጊዜ ያገኙታል እና በተመሳሳይ ጊዜ ይህንን ክላስተር በሚጫኑበት ጊዜ አያስቀምጡ ፣ ይህም ገንዘብ ይቆጥባል። ደህና፣ በተጨማሪም ይህ ያለእርስዎ ተሳትፎ በራስ-ሰር ይከሰታል። አውቶማቲካለር ራሱ ለክላስተር አስተዳዳሪ ኤፒአይ እና የደመና አቅራቢው ኤፒአይ በተወሰነ አመክንዮ የተፃፈ የጥያቄዎች ስብስብ ብቻ አይደለም። በእርግጠኝነት ማስታወስ ያለብዎት ነገር ቀደም ሲል እንደጻፍነው የአንጓዎችን በ 3 ዓይነቶች መከፋፈል ነው ። እና ደስተኛ ትሆናለህ.

ምንጭ: hab.com

አስተያየት ያክሉ