Hvernig á að búa til þinn eigin autoscaler fyrir klasa

Halló! Við þjálfum fólk í að vinna með stór gögn. Það er ómögulegt að ímynda sér fræðsluáætlun um stór gögn án þess að vera með eigin klasa, sem allir þátttakendur vinna saman að. Af þessum sökum hefur forritið okkar það alltaf 🙂 Við tökum þátt í uppsetningu, stillingu og stjórnun þess, og strákarnir setja MapReduce störf þar beint og nota Spark.

Í þessari færslu munum við segja þér hvernig við leystum vandamálið með ójafnri þyrpingahleðslu með því að skrifa okkar eigin sjálfvirka mælikvarða með því að nota skýið Mail.ru skýjalausnir.

vandamálið

Klasinn okkar er ekki notaður í dæmigerðum ham. Förgun er mjög misjöfn. Til dæmis eru verklegir tímar þar sem allir 30 manns og kennari fara í klasann og byrja að nota hann. Eða aftur, það eru dagar fyrir frestinn þegar álagið eykst mikið. Afganginn af tímanum starfar klasinn í undirálagsham.

Lausn #1 er að halda þyrpingu sem þolir hámarksálag, en verður aðgerðalaus það sem eftir er.

Lausn #2 er að hafa lítinn klasa, sem þú bætir hnútum við handvirkt fyrir kennslustundir og meðan á hámarksálagi stendur.

Lausn #3 er að hafa lítinn klasa og skrifa sjálfvirkan mælikvarða sem mun fylgjast með núverandi álagi klasans og, með því að nota ýmis API, bæta við og fjarlægja hnúta úr klasanum.

Í þessari færslu munum við tala um lausn #3. Þessi autoscaler er mjög háður ytri þáttum frekar en innri, og veitendur veita það oft ekki. Við notum skýjainnviði Mail.ru Cloud Solutions og skrifuðum sjálfvirkan mælikvarða með MCS API. Og þar sem við kennum hvernig á að vinna með gögn ákváðum við að sýna hvernig þú getur skrifað svipaðan sjálfvirkan mælikvarða í eigin tilgangi og notað hann með skýinu þínu

Forkröfur

Í fyrsta lagi verður þú að hafa Hadoop þyrping. Til dæmis notum við HDP dreifingu.

Til þess að hægt sé að bæta við og fjarlægja hnútana þína fljótt verður þú að hafa ákveðna dreifingu hlutverka á milli hnútanna.

  1. Master hnútur. Jæja, það er engin þörf á að útskýra neitt sérstaklega: aðalhnút þyrpingarinnar, þar sem, til dæmis, Spark driverinn er ræstur, ef þú notar gagnvirka stillingu.
  2. Dagsetningarhnútur. Þetta er hnúturinn sem þú geymir gögn á HDFS og þar sem útreikningar fara fram.
  3. Tölvuhnútur. Þetta er hnút þar sem þú geymir ekki neitt á HDFS, en þar sem útreikningar gerast.

Mikilvægur punktur. Sjálfvirk stærð mun eiga sér stað vegna hnúta af þriðju gerðinni. Ef þú byrjar að taka og bæta við hnútum af annarri gerðinni verður viðbragðshraðinn mjög lítill - að taka úr notkun og endurtaka mun taka klukkustundir á klasanum þínum. Þetta er auðvitað ekki það sem þú býst við af sjálfstýringu. Það er, við snertum ekki hnúta af fyrstu og annarri gerðinni. Þeir munu tákna lágmarks lífvænlegan klasa sem verður til á meðan á áætluninni stendur.

Svo, autoscaler okkar er skrifaður í Python 3, notar Ambari API til að stjórna klasaþjónustu, notar API frá Mail.ru Cloud Solutions (MCS) til að ræsa og stöðva vélar.

Lausnararkitektúr

  1. Module autoscaler.py. Það inniheldur þrjá flokka: 1) aðgerðir til að vinna með Ambari, 2) aðgerðir til að vinna með MCS, 3) aðgerðir sem tengjast beint rökfræði sjálfvirka skalarans.
  2. Handrit observer.py. Í meginatriðum samanstendur það af mismunandi reglum: hvenær og á hvaða augnablikum á að kalla á sjálfvirka mælikvarða.
  3. Stillingarskrá config.py. Það inniheldur til dæmis lista yfir hnúta sem leyfðir eru fyrir sjálfvirka mælingu og aðrar breytur sem hafa til dæmis áhrif á hversu lengi á að bíða frá því að nýjum hnút var bætt við. Það eru líka tímastimplar fyrir upphaf flokka, þannig að fyrir flokkinn er hámarks leyfð klasastilling ræst.

Við skulum nú líta á kóðann í fyrstu tveimur skránum.

1. Autoscaler.py eining

Ambari bekknum

Svona lítur stykki af kóða út sem inniheldur bekk 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

Hér að ofan, sem dæmi, er hægt að skoða útfærslu aðgerðarinnar stop_all_services, sem stöðvar alla þjónustu á viðkomandi klasahnút.

Við innganginn að bekknum Ambari þú framhjá:

  • ambari_urltd eins og 'http://localhost:8080/api/v1/clusters/',
  • cluster_name - nafn klasans þíns í Ambari,
  • headers = {'X-Requested-By': 'ambari'}
  • og inni auth hér er notandanafnið þitt og lykilorðið fyrir Ambari: auth = ('login', 'password').

Aðgerðin sjálf er ekkert annað en nokkur símtöl í gegnum REST API til Ambari. Frá rökréttu sjónarhorni fáum við fyrst lista yfir starfandi þjónustu á hnút og biðjum síðan á tilteknum klasa, á tilteknum hnút, að flytja þjónustu af listanum til ríkisins INSTALLED. Aðgerðir til að ræsa alla þjónustu, til að flytja hnúta í ástand Maintenance osfrv líta svipað út - þetta eru bara nokkrar beiðnir í gegnum API.

Flokkur Mcs

Svona lítur stykki af kóða út sem inniheldur bekk 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

Við innganginn að bekknum Mcs við sendum verkefnisauðkennið inni í skýinu og notendaauðkenni, auk lykilorðs hans. Í virkni vm_turn_on við viljum kveikja á einni af vélunum. Rökfræðin hér er aðeins flóknari. Í upphafi kóðans eru þrjár aðrar aðgerðir kallaðar: 1) við þurfum að fá tákn, 2) við þurfum að breyta hýsingarheitinu í nafn vélarinnar í MCS, 3) fá auðkenni þessarar vélar. Næst gerum við einfaldlega póstbeiðni og ræsum þessa vél.

Svona lítur aðgerðin til að fá tákn út:

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 flokkur

Þessi flokkur inniheldur aðgerðir sem tengjast rekstrarrökfræðinni sjálfri.

Svona lítur kóðinn fyrir þennan flokk út:

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

Við tökum við námskeiðum fyrir inngöngu. Ambari и Mcs, listi yfir hnúta sem eru leyfðir fyrir stigstærð, svo og hnútstillingarfæribreytur: minni og örgjörvi úthlutað til hnútsins í YARN. Það eru líka 2 innri breytur q_ram, q_cpu, sem eru biðraðir. Með því að nota þau geymum við gildi núverandi klasaálags. Ef við sjáum að á síðustu 5 mínútum hefur verið stöðugt aukið álag, þá ákveðum við að við þurfum að bæta +1 hnút við þyrpinguna. Sama á við um vannýtingarástand klasans.

Kóðinn hér að ofan er dæmi um aðgerð sem fjarlægir vél úr þyrpingunni og stöðvar hana í skýinu. Fyrst er það niðurlagning YARN Nodemanager, þá kviknar á stillingunni Maintenance, þá stöðvum við alla þjónustu á vélinni og slökkum á sýndarvélinni í skýinu.

2. Script observer.py

Dæmi um kóða þaðan:

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)

Þar athugum við hvort aðstæður hafi verið skapaðar til að auka afkastagetu klasans og hvort einhverjar vélar séu í varasjóði, fáum hýsingarheiti einnar þeirra, bætum því við klasann og birtum skilaboð um það á Slack liðsins okkar. Eftir það byrjar það cooldown_period, þegar við bætum ekki við eða fjarlægjum neitt úr þyrpingunni, heldur fylgjumst einfaldlega með álaginu. Ef það hefur náð jafnvægi og er innan gangs ákjósanlegra álagsgilda, þá höldum við einfaldlega áfram að fylgjast með. Ef einn hnút var ekki nóg, þá bætum við öðrum við.

Fyrir tilvik þegar við eigum kennslustund framundan vitum við nú þegar að einn hnút dugar ekki, svo við byrjum strax alla ókeypis hnúta og höldum þeim virkum til loka kennslustundarinnar. Þetta gerist með því að nota lista yfir tímastimpla virkni.

Ályktun

Autoscaler er góð og þægileg lausn í þeim tilvikum þegar þú finnur fyrir ójafnri hleðslu á klasa. Þú nærð samtímis æskilegri klasastillingu fyrir hámarkshleðslu og á sama tíma heldurðu ekki þessum klasa meðan á undirálagi stendur, sem sparar peninga. Jæja, auk þess sem þetta gerist allt sjálfkrafa án þátttöku þinnar. Autoscaler sjálfur er ekkert annað en sett af beiðnum til cluster manager API og skýjaveitu API, skrifaðar samkvæmt ákveðinni rökfræði. Það sem þú þarft örugglega að muna er skipting hnúta í 3 tegundir, eins og við skrifuðum áðan. Og þú verður ánægður.

Heimild: www.habr.com

Bæta við athugasemd