Чӣ тавр худатонро барои кластер худатон созед

Салом! Мо одамонро таълим медиҳем, ки бо маълумоти калон кор кунанд. Барномаи таълимиро оид ба додаҳои калон бе кластери худ, ки дар он ҳамаи иштирокчиён якҷоя кор мекунанд, тасаввур кардан ғайриимкон аст. Аз ин сабаб, барномаи мо ҳамеша онро дорад :) Мо ба конфигуратсия, танзим ва маъмурияти он машғулем ва бачаҳо бевосита дар он ҷо ҷойҳои MapReduce-ро оғоз мекунанд ва Spark-ро истифода мебаранд.

Дар ин мақола мо ба шумо мегӯям, ки чӣ гуна мо мушкилоти боркунии кластерҳои нобаробарро тавассути навиштани автоскалери худамон бо истифода аз абр ҳал кардем. Mail.ru ҳалли абрӣ.

проблема

Кластери мо дар реҷаи маъмулӣ истифода намешавад. Бартарафкунӣ хеле нобаробар аст. Масалан, дарсњои амалї њастанд, ки њамаи 30 нафар ва як муаллим ба кластер рафта, аз он истифода мебаранд. Ё боз, рӯзҳои пеш аз мӯҳлат вуҷуд доранд, ки сарборӣ хеле зиёд мешавад. Дар вақти боқимонда кластер дар ҳолати камборкунӣ кор мекунад.

Ҳалли №1 ин нигоҳ доштани кластерест, ки ба сарбориҳои баланд тоб оварад, аммо дар вақти боқимонда бекор мемонад.

Ҳалли №2 ин нигоҳ доштани кластери хурд аст, ки шумо ба он гиреҳҳоро пеш аз дарсҳо ва ҳангоми сарбории баланд дастӣ илова мекунед.

Ҳалли №3 ин нигоҳ доштани кластери хурд ва навиштани автоматикӣ мебошад, ки сарбории ҷории кластерро назорат мекунад ва бо истифода аз API-ҳои гуногун гиреҳҳоро аз кластер илова ва хориҷ мекунад.

Дар ин мақола мо дар бораи ҳалли №3 сӯҳбат хоҳем кард. Ин autoscaler аз омилҳои беруна вобастагии калон дорад, на аз омилҳои дохилӣ ва провайдерҳо аксар вақт онро таъмин намекунанд. Мо инфрасохтори абрии Mail.ru Cloud Solutions-ро истифода мебарем ва бо истифода аз MCS API автомати масштабкунанда навиштем. Ва азбаски мо тарзи кор бо маълумотро таълим медиҳем, мо тасмим гирифтем нишон диҳем, ки чӣ гуна шумо метавонед як автоматии шабеҳро барои мақсадҳои худ нависед ва онро бо абри худ истифода баред

Шарти

Аввалан, шумо бояд кластери Hadoop дошта бошед. Масалан, мо тақсимоти HDP-ро истифода мебарем.

Барои он ки гиреҳҳои шумо зуд илова ва хориҷ карда шаванд, шумо бояд тақсимоти муайяни нақшҳоро дар байни гиреҳҳо дошта бошед.

  1. Гиреҳи усто. Хуб, дар ин ҷо ҳеҷ чизи махсусан зарурӣ барои тавзеҳ нест: гиреҳи асосии кластер, ки дар он, масалан, драйвери Spark, агар шумо режими интерактивиро истифода баред, ба кор андохта мешавад.
  2. Гиреҳи сана. Ин гиреҳест, ки шумо маълумотро дар HDFS нигоҳ медоред ва дар он ҳисобҳо сурат мегиранд.
  3. Нуқтаи ҳисоббарор. Ин гиреҳест, ки дар он шумо чизеро дар HDFS нигоҳ намедоред, аммо дар он ҷо ҳисобҳо сурат мегиранд.

Нуқтаи муҳим. Миқёси худкор аз ҳисоби гиреҳҳои навъи сеюм ба амал меояд. Агар шумо ба гирифтан ва илова кардани гиреҳҳои навъи дуюм шурӯъ кунед, суръати вокуниш хеле паст хоҳад буд - бекоркунӣ ва аз нав ба кор даровардани кластери шумо соатҳо тӯл мекашад. Ин, албатта, он чизе нест, ки шумо аз автоматизатсия интизоред. Яъне мо ба гиреҳҳои навъи якум ва дуюм даст намезанем. Онҳо як кластери ҳадди ақали қобили амалро намояндагӣ мекунанд, ки дар тӯли тамоми давраи барнома вуҷуд хоҳанд дошт.

Ҳамин тавр, autoscaler мо дар Python 3 навишта шудааст, API-и Ambari-ро барои идоракунии хидматҳои кластер истифода мебарад. API аз Mail.ru Cloud Solutions (MCS) барои ба кор андохтан ва боздоштани мошинҳо.

Архитектураи ҳалли

  1. Модул autoscaler.py. Он се синфро дар бар мегирад: 1) функсияҳо барои кор бо Ambari, 2) функсияҳо барои кор бо MCS, 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 Ин аст номи корбар ва пароли шумо барои Ambari: auth = ('login', 'password').

Худи функсия ҷуз якчанд зангҳо тавассути REST API ба Ambari чизи дигаре нест. Аз нуқтаи назари мантиқӣ, мо аввал рӯйхати хидматҳои иҷрошавандаро дар гиреҳ мегирем ва сипас дар кластери додашуда, дар гиреҳи додашуда хоҳиш мекунем, ки хидматҳоро аз рӯйхат ба давлат интиқол диҳем. INSTALLED. Функсияҳо барои оғози ҳама хидматҳо, интиқоли гиреҳҳо ба ҳолати Maintenance ва ғайра ба назар монанд - онҳо танҳо чанд дархост тавассути API мебошанд.

Синфи Mcs

Ин аст он чизе ки як порчаи коди дорои синф ба назар мерасад 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) мо бояд номи мизбонро ба номи мошин дар MCS табдил диҳем, 3) id-и ин мошинро гирем. Баъдан, мо танҳо як дархости почтаи электронӣ мекунем ва ин мошинро ба кор меандозем.

Функсияи ба даст овардани токен чунин аст:

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, рӯйхати гиреҳҳое, ки барои миқёс иҷозат дода шудаанд, инчунин параметрҳои конфигуратсияи гиреҳ: хотира ва CPU, ки ба гиреҳ дар YARN ҷудо карда шудаанд. Инчунин 2 параметрҳои дохилӣ q_ram, q_cpu, ки навбат мебошанд. Бо истифода аз онҳо, мо арзишҳои бори кластери ҷориро нигоҳ медорем. Агар мо бубинем, ки дар тӯли 5 дақиқаи охир сарбории пайваста афзоиш ёфтааст, пас мо қарор медиҳем, ки ба кластер гиреҳ +1 илова кунем. Айнан ҳамин чиз ба ҳолати камистифодабарии кластер дахл дорад.

Рамзи дар боло овардашуда як мисоли функсияест, ки мошинро аз кластер хориҷ мекунад ва онро дар абр қатъ мекунад. Пеш аз ҳама, бекоркунӣ вуҷуд дорад YARN Nodemanager, пас режим фаъол мешавад Maintenance, пас мо ҳама хидматҳоро дар мошин қатъ мекунем ва мошини виртуалиро дар абр хомӯш мекунем.

2. Скрипт observer.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 як ҳалли хуб ва қулай барои он ҳолатҳоест, ки шумо бори нобаробари кластерро эҳсос мекунед. Шумо ҳамзамон конфигуратсияи кластери дилхоҳро барои сарбории авҷи худ ба даст меоред ва ҳамзамон ин кластерро ҳангоми камборӣ нигоҳ намедоред ва пулро сарфа мекунед. Хуб, ин ҳама ба таври худкор бе иштироки шумо рӯй медиҳад. Худи автоматикӣ ҷуз як маҷмӯи дархостҳо ба API-и менеҷери кластер ва API-и провайдери абрӣ, ки тибқи мантиқи муайян навишта шудааст, чизи дигаре нест. Он чизе ки шумо бешубҳа дар хотир доред, тақсимоти гиреҳҳо ба 3 намуд аст, тавре ки мо қаблан навишта будем. Ва шумо хушбахт хоҳед буд.

Манбаъ: will.com

Илова Эзоҳ