Kuidas luua oma auto skaalautuja klastrile

Tere! Me Ă”petame inimesi töötama suurte andmetega. Haridusprogrammi, mis kĂ€sitleb suurte andmete teemade Ă”petamist, ei saa ette kujutada ilma oma klastrita, kus kĂ”ik osalejad suudavad koos töötada. SellepĂ€rast on meie programmis see alati olemas 🙂 Me tegeleme selle seadistamise, hÀÀlestamise ja haldamisega, samas kui osalejad kĂ€ivitavad seal MapReduce-töid ja kasutavad Spark'i.

Selles postituses rÀÀgime, kuidas me lahendasime klastri ebavÔrdse koormuse probleemi, kirjutades oma automaatse skaaleerija, kasutades pilveteenust. Mail.ru Pilve Lahendused.

Probleem

Meie klastrit kasutatakse mitte just tĂŒĂŒpilises reĆŸiimis. Kasutamine on vĂ€ga ebavĂ”rdne. NĂ€iteks on praktikumipĂ€evad, mil kĂ”ik 30 inimest ja Ă”petaja siseneb klastrisse ja hakkavad seda kasutama. VĂ”i on ka pĂ€evi enne tĂ€htaega, mil koormus tĂ”useb mĂ€rgatavalt. KĂ”igil teistel aegadel töötab klaster alakoormuse reĆŸiimis.

Lahendus nr 1 on hoida klaster, mis suudab taluda tipukoormusi, kuid on muul ajal seisakutos.

Lahendus nr 2 on hoida vĂ€ikest klastrit, kuhu lisada node’id kĂ€sitsi enne tunde ja tipukoormuste ajal.

Lahendus nr 3 on hoida vĂ€ikest klastrit ja kirjutada automaatne skaaleerija, mis jĂ€lgib klastri hetkekoormust ja automaatselt, kasutades erinevaid API-sid, lisab ja eemaldab node’e klastri seest.

Selles postituses rÀÀgime lahendusest nr 3. Selline automaatne skaaleerija sĂ”ltub tugevalt vĂ€listest teguritest, mitte sisemistest, ja teenusepakkujad ei paku seda sageli. Me kasutame Mail.ru Cloud Solutions’i pilveuuringute infrastruktuuri ja oleme kirjutanud automaatse skaaleerija, kasutades MCS API-d. Kuna me Ă”petame andmete töötlemist, otsustasime nĂ€idata, kuidas saate kirjutada sellise automaatse skaaleerija oma eesmĂ€rkide tĂ€itmiseks ja kasutada seda oma pilves.

Eeltingimused

Esiteks peab teil olema Hadoopi klaster. Me kasutame nÀiteks HDP distributsiooni.

Et node’id saaksid kiiresti lisanduda ja eemalduda, peab teil olema mÀÀratud rollijagamine node'ite vahel.

  1. Peamoodul. Siin pole erilist seletamist vaja: peamine node klastris, kus kĂ€ivitatakse nĂ€iteks Spark’i draiver, kui kasutate interaktiivset reĆŸiimi.
  2. Andmoodul. See on modul, kus hoitakse andmeid HDFS-is ja kus toimub ka arvutused.
  3. Arvutusnode. See node, kus teil ei ole midagi HDFS-is salvestatud, kuid kus toimub arvutustöö.

Oluline punkt. Automaatne skalaarimine toimub kolmanda tĂŒĂŒbi node'ide arvelt. Kui hakkate lisama ja eemaldama teise tĂŒĂŒbi node'e, siis reageerimiskiirus langeb oluliselt – dekomeerimine ja rekomeerimine vĂ”tab teie klastris tundide viisi. See ei ole see, mida ootate automaatse skalaarimise puhul. Seega esimest ja teist tĂŒĂŒpi node'e me ei muuda. Need moodustavad minimaalsete elujĂ”uliste node'ide k Lastri, mis eksisteerib programmi kestuse vĂ€ltel.

Nii et meie automaatne skaaler on kirjutatud Python 3-s, kasutades Ambari API-d klastriteenuste haldamiseks, kasutab Mail.ru Cloud Solutions API-d (MCS) masinate kÀivitamiseks ja peatamiseks.

Lahenduse arhitektuur

  1. Moodul autoscaler.py. Selles on mÀÀratletud kolm klassi: 1) funktsioonid Ambari tööks, 2) funktsioonid MCS tööks, 3) funktsioonid, mis on otseselt seotud automaatse skaaleri tööloogikaga.
  2. Skript observer.py. See koosneb erinevatest reeglitest: millal ja millistel hetkedel kutsuda automaatse skaaleri funktsioone.
  3. Configuratsiooniparameetrite fail config.py. Seal on nÀiteks loetelu node'idest, mis on lubatud automaatseks skaalamiseks, ja muud parameetrid, mis mÔjutavad nÀiteks seda, kui kaua oodata, alates hetkest, mil uus node lisati. Samuti on seal algusajad, et enne tegevust oleks kÀivitatud maksimaalselt lubatud klastrikonfiguratsioon.

Vaadakem nĂŒĂŒd koodijuppe, mis asuvad esimeses kahes failis.

1. Moodul autoscaler.py

Ambari klass

NÀin vÀlja osa koodist, mis sisaldab klassi 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

Ülal toodud nĂ€ites on nĂ€ha funktsiooni rakendust stop_all_services, mis peatab kĂ”ik teenused soovitud klastrisĂ”lmes.

Klassi sisendiks Ambari antakse:

  • ambari_url, nĂ€iteks jĂ€rgmisega 'http:\/\/localhost:8080\/api\/v1\/clusters\/ ',
  • cluster_name – teie klastrinimi Ambaris,
  • headers = {'X-Requested-By': 'ambari'}
  • ja sees on auth teie Ambari kasutajanimi ja parool: auth = ('login', 'password').

Funktsioon koosneb mitte rohkem kui paarist pöördumisest Ambari REST API kaudu. Loogika poolelt saame esmalt nimekirja rakendatud teenustest sĂ”lmes ja palume siis katta need klastris ja antud sĂ”lmes olekusse INSTALLED. KĂ”ik teenuste kĂ€ivitamise funktsioonid, sĂ”lmede viimine seisundisse Maintenance ja muud sarnane nĂ€evad vĂ€lja sarnasti – need on lihtsalt mĂ”ned pĂ€ringud API kaudu.

Klass Mcs

NÀin vÀlja osa koodist, mis sisaldab klassi 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

Klassi sisendiks Mcs me anname projektide id pilves ja kasutaja id, samuti tema parooli. Funktsioonis vm_turn_on Me tahame sisse lĂŒlitada ĂŒhe masinast. Siin on veidi keerulisem loogika. Koodi alguses kutsutakse vĂ€lja kolm muud funktsiooni: 1) meil on vaja saada token, 2) meil on vaja konverteerida hostname masina nimeks MCS-is, 3) saada selle masina id. SeejĂ€rel teeme lihtsalt postipĂ€ra ja kĂ€ivitame selle masina.

Siin on funktsioon tokeni saamiseks:

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

Klass Autoscaler

Selles klassis on funktsioonid, mis on seotud loogikaga.

Siin on selle klassi koodilÔik:

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

Sisendiks vÔtame klassid Ambari ja Mcs, skaleerimiseks lubatud sÔlmede loendi, samuti sÔlme konfiguratsiooni parameetrid: YARN-is eraldatud mÀlumaht ja CPU. Samuti on meil kaks sisemist parameetrit q_ram, q_cpu, mis on jÀrjekorrad. Nende abil salvestame klastrite praeguste koormuse vÀÀrtusi. Kui nÀeme, et viimase viie minuti jooksul on koormus pidevalt kÔrge, siis otsustame, et on vajalik klastrisse lisada +1 sÔlm. Sama kehtib ka klastrite alakoormuse olukorra kohta.

Ülaltoodud koodis on nĂ€ide funktsioonist, mis eemaldab masina klastrist ja peatab selle pilves. Alguses toimub dekomeerimine YARN Nodemanager, siis lĂŒlitame sisse reĆŸiimi Maintenance, siis peatame kĂ”ik teenused masinas ja lĂŒlitame vĂ€lja virtuaalse masina pilves.

2. Skript observer.py

KoodinÀide sealt:

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} on edukalt suurendatud".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)

Siin kontrollime, kas klastrite vĂ”imsuste suurendamise tingimused on tekkinud ja kas varuks on masinad, saame ĂŒhe nende hostnimedest, lisame selle klastrisse ja avaldame sellest meie meeskonna Slackis teadet. PĂ€rast seda algab cooldown_period, mil me ei lisa ega eemalda midagi klastrist, vaid jĂ€lgime lihtsalt koormust. Kui see stabiliseerub ja jÀÀb optimaalse koormuse vÀÀrtuste vahemikku, jĂ€tkame lihtsalt jĂ€lgimist. Kui aga ĂŒks node ei piisanud, siis lisame veel ĂŒhe.

Olukordade puhul, kui meil on ees tegevus, teame juba kindlasti, et ĂŒks node ei piisa, seetĂ”ttu kĂ€ivitame kohe kĂ”ik vabad node'id ja hoiame need aktiivsena kuni tegevuse lĂ”puni. See toimub ajastamiste nimekirja abil.

KokkuvÔte

Autoskaleerija on hea ja mugav lahendus, kui teie klastris esineb ebaĂŒhtlane koormus. Saate samal ajal saavutada vajaliku klastrikonfiguratsiooni tippkoormuste jaoks ning sÀÀsta raha, hoides klastrit tĂŒhja ajal. Ja kĂ”ik see juhtub automaatselt teie osaluseta. Ise autoskaleerija ei ole midagi muud kui korduste komplekt API pĂ€ringutest klaster-halduriga ja pilveteenuse pakkujaga, mis on kirjutatud teatud loogika jĂ€rgi. Mida tuleks kindlasti meeles pidada, on node'ide jagamine 3 tĂŒĂŒpi, nagu me varem kirjutasime. Ja see toob teile Ă”nne.

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster