Si të krijoni një auto-skalues për klasterin

PĂ«rshĂ«ndetje! Ne e mĂ«sojmĂ« njerĂ«zit si tĂ« punojnĂ« me tĂ« dhĂ«nat e mĂ«dha. Nuk Ă«shtĂ« e mundur tĂ« imagjinohet njĂ« program edukativ pĂ«r tĂ« dhĂ«nat e mĂ«dha pa njĂ« klaster tĂ« vetin, ku tĂ« gjithĂ« pjesĂ«marrĂ«sit punojnĂ« sĂ« bashku. PĂ«r kĂ«tĂ« arsye, nĂ« programin tonĂ« ai Ă«shtĂ« gjithmonĂ« aty 🙂 Ne merremi me konfigurimin, tuningun dhe administrimin e tij, ndĂ«rsa djemtĂ« e pĂ«rdorin atĂ« pĂ«r tĂ« nisur punĂ«t MapReduce dhe pĂ«r tĂ« shfrytĂ«zuar Spark-in.

Në këtë post, ne do të flasim se si e zgjidhëm problemin e ngarkesës së pabarabartë të klasterit duke shkruar një auto-skalues, duke përdorur cloud. Mail.ru Cloud Solutions.

Problemi

Klasteri ynë nuk përdoret krejtësisht në një mënyrë tipike. Shfrytëzimi është shumë i pabarabartë. Për shembull, ka seanca praktike të cilat, kur të gjithë 30 persona dhe mësuesi hyjnë në klaster dhe fillojnë ta përdorin. Ose ka ditë përpara deadlines, kur ngarkesa rritet shumë. Në çdo kohë tjetër klasteri punon në një mod është nën ngarkesë.

Zgjidhja nr. 1 – Ă«shtĂ« tĂ« mbani njĂ« klaster qĂ« do tĂ« pĂ«rballojĂ« ngarkesat maksimale, por do tĂ« mbetet i papunĂ« nĂ« çdo kohĂ« tjetĂ«r.

Zgjidhja nr. 2 – Ă«shtĂ« tĂ« mbani njĂ« klaster tĂ« vogĂ«l, nĂ« tĂ« cilin do tĂ« shtoni manualisht node pĂ«rpara seancave dhe gjatĂ« ngarkesave maksimale.

Zgjidhja nr. 3 – Ă«shtĂ« tĂ« mbani njĂ« klaster tĂ« vogĂ«l dhe tĂ« shkruani njĂ« auto-skalues, i cili do tĂ« monitorojĂ« ngarkesĂ«n aktuale tĂ« klasterit dhe vetĂ«, duke pĂ«rdorur API tĂ« ndryshme, do tĂ« shtojĂ« dhe heqĂ« node nga klasteri.

Në këtë post ne do të flasim për zgjidhjen nr. 3. Ky auto-skalues varet shumë nga faktorët e jashtëm, e jo nga ata të brendshëm, dhe ofruesit shpesh nuk e ofrojnë atë. Ne përdorim infrastruktura cloud të Mail.ru Cloud Solutions dhe kemi shkruar një auto-skalues, duke përdorur API MCS. Dhe meqenëse ne e mësojmë si të punojmë me të dhënat, vendosëm të tregojmë se si mund të shkruani një auto-skalues të tillë për qëllimet tuaja dhe ta përdorni me cloud-in tuaj.

Kushtet paraprake

Së pari, ju duhet të keni një klaster Hadoop. Ne, për shembull, po përdorim shpërndarjen HDP.

Që node-t tuaja të mund të shtohen dhe hiqen shpejt, ju duhet një shpërndarje e caktuar e rolit për node.

  1. Master node. Këtu nuk ka nevojë për shumë shpjegim: node kryesore e klasterit, në të cilën funksionon, për shembull, drejtuesi i Spark-it, nëse po përdorni modalitetin interaktiv.
  2. Data node. Ky është node ku keni të dhënat e ruajtura në HDFS dhe aty ndodhin edhe llogaritë.
  3. Noda llogarike. Kjo është një node ku nuk mbani asgjë në HDFS, por ku ndodhin llogaritjet.

Moment i rëndësishëm. Autoskalimi do të ndodhë përmes nodave të tretë. Nëse filloni të tërheqni dhe shtoni nodat e tipit të dytë, atëherë shpejtësia e reagimit do të jetë shumë e ulët - dekomishen dhe rekomishen do të marrë orë në klasterin tuaj. Kjo, natyrisht, nuk është ajo që prisni nga autoskalimi. Pra, ne nuk prekim nodat e tipit të parë dhe të dytë. Ato do të përfaqësojnë një klaster minimalisht të jetueshëm, i cili do të ekzistojë gjatë gjithë kohës së programit.

Pra, autoskaleri ynë është shkruar në Python 3, përdor API-në e Ambari për menaxhimin e shërbimeve të klasterit, përdor API nga Mail.ru Cloud Solutions (MCS) për të nisur dhe ndaluar makinat.

Arkitektura e zgjidhjes

  1. Moduli autoscaler.py. Në të janë të shkruara tri klasa: 1) funksione për punë me Ambari, 2) funksione për punë me MCS, 3) funksione të lidhura drejtpërdrejt me logjikën e funksionimit të autoskaluesit.
  2. Script observer.py. Në thelb përbëhet nga rregulla të ndryshme: kur dhe në cilat momente të thirren funksionet e autoskaluesit.
  3. Skedari me parametrat e konfigurimit config.py. Aty përmban, për shembull, listën e nodave të lejuara për autoskalim dhe parametrat e tjerë që ndikojnë, për shembull, në sa kohë të presim nga momenti kur është shtuar një node e re. Po ashtu aty ndodhen dhe timestampet e fillimit të mbledhjeve, në mënyrë që para mbledhjes të jetë aktivizuar konfigurimi maksimal i lejuar i klasterit.

Tani le të shikojmë copat e kodit që ndodhen brenda dy skedarëve të parë.

1. Moduli autoscaler.py

Klasa Ambari

Kështu duket një copë kodi që përmban klasën Ambari:

klasa 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

Mund të shihni implementimin e funksionit më sipër si shembull stop_all_services, i cili ndalon të gjitha shërbimet në nodën e nevojshme të klasterit.

Në klasë Ambari i jepni si_input:

  • ambari_url, pĂ«r shembull, nĂ« formatin e 'http://localhost:8080/api/v1/clusters/',
  • cluster_name – emri i klasterit tuaj nĂ« Ambari,
  • headers = {'X-Requested-By': 'ambari'}
  • dhe brenda auth ndodhet emri i pĂ«rdoruesit dhe fjalĂ«kalimi pĂ«r Ambari: auth = ('login', 'password').

Funksioni vetë përbën jo më shumë se disa thirrje përmes REST API për Ambari. Nga pikëpamja logjike, fillimisht marrim një listë të shërbimeve të nisura në nodë, dhe pastaj i kërkojmë në këtë klaster, në këtë nodë, që shërbimet në listë të kalojnë në gjendjen INSTALLED. Funksionet për nisjen e të gjitha shërbimeve, për kalimin e nodave në gjendjen Maintenance dhe të tjerët duken gjithashtu të ngjashme - thjesht janë disa kërkesa përmes API-së.

klasa Mcs

Kështu duket një copë kodi që përmban klasën Mcs:

klasa 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

Në klasë Mcs ne kalojmë id-në e projektit brenda cloud-it dhe id-në e përdoruesit, si dhe fjalëkalimin e tij. Në funksionin vm_turn_on Ne duam të aktivizojmë një nga makinat. Logjika këtu është pak më komplekse. Në fillim të kodit ka thirrje për tre funksione të tjera: 1) na nevojitet të marrim token-in, 2) na nevojitet të konvertojmë hostname në emrin e makinës në MCS, 3) të marrim id-në e kësaj makine. Më pas, ne bëjmë thjesht një post-request dhe nisi këtë makinë.

Kjo është funksioni për marrjen e token-it:

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

Klasa Autoscaler

Në këtë klasë janë përfshira funksionet që lidhen me logjikën e punës.

Kjo është një pjesë e kodit të kësaj klase:

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

Ne input, ne marrim klaset Ambari dhe Mcs, lista e nodave që janë të lejuara për skalimin, si dhe parametrat e konfigurimit të nodave: RAM dhe CPU të rezervuara për nodin në YARN. Ka gjithashtu 2 parameter të brendshëm q_ram, q_cpu, që janë radhë. Me ndihmën e tyre ruajmë vlerat e ngarkesës aktuale të grumbullit. Nëse shohim që gjatë 5 minutave të fundit ka qenë stabilisht një ngarkesë e lartë, ne vendosim se duhet të shtojmë +1 nod në grumbull. E njëjta gjë vlen edhe për gjendjen e pakufizuar të grumbullit.

Në kodin e mësipërm, jepet një shembull funksioni që heq makinën nga grumbulli dhe e ndalon atë në cloud. Fillimisht ndodh dekoma YARN Nodemanager, më pas aktivizohet moda Maintenance, më pas ndalim të gjitha shërbimet në makinë dhe fikim makinën virtuale në cloud.

2. Skripti observer.py

Shembuj kodi nga atje:

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} është rritur me sukses".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)

Në të kjo ne kontrollojmë nëse janë plotësuar kushtet për rritjen e kapaciteteve të klasterit dhe nëse ka ndonjë makinë të lirë, marrim emrin e host-it të njërës prej tyre, e shtojmë atë në klaster dhe publikojmë një mesazh për këtë në Slack të ekipit tonë. Pas kësaj fillon cooldown_period, kur ne nuk shtojmë dhe nuk heqim asgjë nga klasteri, por thjesht monitorojmë ngarkesën. Nëse ajo është stabilizuar dhe është brenda koridorit optimal të vlerave të ngarkesës, thjesht vazhdojmë monitorimin. Nëse një nodë nuk mjafton, atëherë shtojmë një tjetër.

Për rastet kur kemi një aktivitet përpara, ne tashmë e dimë me siguri që një nodë nuk do të mjaftojë, prandaj menjëherë fillojmë të gjitha nodet e lira dhe i mbajmë të aktivizuara deri në fund të aktivitetit. Kjo ndodh përmes një liste të timestamp-ëve të aktiviteteve.

Përfundim

Autoskaler-i është një zgjidhje e mirë dhe e përshtatshme për ato raste kur keni ngarkesë të pakoordinuar në klaster. Ju arrini një konfigurim të nevojshëm të klasterit për ngarkesa maksimale dhe për më tepër nuk e mbani këtë klaster gjatë periudhave të nën-ngarkesës, duke kursyer fonde. Dhe gjithashtu, kjo ndodh në mënyrë automatike pa pjesëmarrjen tuaj. Vetë autoskaler-i është thjesht një grup kërkesash në API-në e menaxherit të klasterit dhe API-në e ofruesit të cloud-it, të shkruara sipas një logjike të caktuar. Ajo që duhet të mbani mend me siguri është ndarja e nodëve në 3 lloje, siç kemi shkruar më parë. Dhe do të jeni të lumtur.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster