Sut i wneud eich autoscaler eich hun ar gyfer clwstwr

Helo! Rydym yn hyfforddi pobl i weithio gyda data mawr. Mae'n amhosibl dychmygu rhaglen addysgol ar ddata mawr heb ei chlwstwr ei hun, y mae'r holl gyfranogwyr yn cydweithio arno. Am y rheswm hwn, mae ein rhaglen bob amser wedi ei :) Rydym yn cymryd rhan yn ei ffurfweddu, tiwnio a gweinyddu, ac mae'r guys yn uniongyrchol lansio MapReduce swyddi yno ac yn defnyddio Spark.

Yn y swydd hon byddwn yn dweud wrthych sut y gwnaethom ddatrys y broblem o lwytho clwstwr anwastad trwy ysgrifennu ein autoscaler ein hunain gan ddefnyddio'r cwmwl Atebion Cwmwl Mail.ru.

problem

Nid yw ein clwstwr yn cael ei ddefnyddio mewn modd nodweddiadol. Mae gwaredu yn anwastad iawn. Er enghraifft, mae dosbarthiadau ymarferol, pan fydd pob un o'r 30 o bobl ac athro yn mynd i'r clwstwr ac yn dechrau ei ddefnyddio. Neu eto, mae yna ddyddiau cyn y dyddiad cau pan fydd y llwyth yn cynyddu'n fawr. Gweddill yr amser mae'r clwstwr yn gweithredu yn y modd tanlwytho.

Ateb #1 yw cadw clwstwr a fydd yn gwrthsefyll llwythi brig, ond a fydd yn segur weddill yr amser.

Ateb #2 yw cadw clwstwr bach, yr ydych chi'n ychwanegu nodau ato â llaw cyn dosbarthiadau ac yn ystod llwythi brig.

Ateb #3 yw cadw clwstwr bach ac ysgrifennu awtoscaler a fydd yn monitro llwyth presennol y clwstwr a, gan ddefnyddio gwahanol APIs, ychwanegu a thynnu nodau o'r clwstwr.

Yn y swydd hon byddwn yn siarad am ateb #3. Mae'r autoscaler hwn yn ddibynnol iawn ar ffactorau allanol yn hytrach na rhai mewnol, ac yn aml nid yw darparwyr yn ei ddarparu. Rydym yn defnyddio seilwaith cwmwl Mail.ru Cloud Solutions ac yn ysgrifennu awtoscaler gan ddefnyddio'r API MCS. Ac ers i ni ddysgu sut i weithio gyda data, fe benderfynon ni ddangos sut y gallwch chi ysgrifennu autoscaler tebyg at eich dibenion eich hun a'i ddefnyddio gyda'ch cwmwl

Rhagofynion

Yn gyntaf, rhaid bod gennych glwstwr Hadoop. Er enghraifft, rydym yn defnyddio'r dosbarthiad HDP.

Er mwyn i'ch nodau gael eu hychwanegu a'u tynnu'n gyflym, rhaid bod gennych chi ddosbarthiad penodol o rolau ymhlith y nodau.

  1. Prif nod. Wel, nid oes unrhyw beth arbennig o angenrheidiol i'w esbonio yma: prif nod y clwstwr, y mae'r gyrrwr Spark yn cael ei lansio arno, er enghraifft, os ydych chi'n defnyddio'r modd rhyngweithiol.
  2. Nod dyddiad. Dyma'r nod rydych chi'n storio data arno ar HDFS a lle mae cyfrifiadau'n digwydd.
  3. Nod cyfrifiadura. Mae hwn yn nod lle nad ydych chi'n storio unrhyw beth ar HDFS, ond lle mae cyfrifiadau'n digwydd.

Pwynt pwysig. Bydd graddoli awtomatig yn digwydd oherwydd nodau o'r trydydd math. Os byddwch yn dechrau cymryd ac ychwanegu nodau o'r ail fath, bydd y cyflymder ymateb yn isel iawn - bydd datgomisiynu ac ailymrwymo yn cymryd oriau ar eich clwstwr. Nid dyma, wrth gwrs, yw'r hyn rydych chi'n ei ddisgwyl o awtoscaling. Hynny yw, nid ydym yn cyffwrdd â nodau'r math cyntaf a'r ail fath. Byddant yn cynrychioli clwstwr hyfyw lleiaf a fydd yn bodoli trwy gydol y rhaglen.

Felly, mae ein autoscaler wedi'i ysgrifennu yn Python 3, yn defnyddio'r API Ambari i reoli gwasanaethau clwstwr, defnyddiau API gan Mail.ru Cloud Solutions (MCS) ar gyfer cychwyn a stopio peiriannau.

Pensaernïaeth datrysiad

  1. Modiwl autoscaler.py. Mae'n cynnwys tri dosbarth: 1) swyddogaethau ar gyfer gweithio gydag Ambari, 2) swyddogaethau ar gyfer gweithio gyda MCS, 3) swyddogaethau sy'n ymwneud yn uniongyrchol â rhesymeg y autoscaler.
  2. Sgript observer.py. Yn ei hanfod mae'n cynnwys rheolau gwahanol: pryd ac ar ba eiliadau i alw'r awtoscaler yn swyddogaethau.
  3. Ffeil ffurfweddu config.py. Mae'n cynnwys, er enghraifft, rhestr o nodau a ganiateir ar gyfer graddoli awtomatig a pharamedrau eraill sy'n effeithio, er enghraifft, pa mor hir i aros o'r eiliad yr ychwanegwyd nod newydd. Mae yna hefyd stampiau amser ar gyfer dechrau dosbarthiadau, fel bod y cyfluniad clwstwr uchaf a ganiateir yn cael ei lansio cyn y dosbarth.

Gadewch i ni nawr edrych ar y darnau o god y tu mewn i'r ddwy ffeil gyntaf.

1. modiwl Autoscaler.py

Dosbarth Ambari

Dyma sut olwg sydd ar ddarn o god sy'n cynnwys dosbarth 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

Uchod, fel enghraifft, gallwch edrych ar weithrediad y swyddogaeth stop_all_services, sy'n atal pob gwasanaeth ar y nod clwstwr dymunol.

Wrth y fynedfa i'r dosbarth Ambari byddwch yn pasio:

  • ambari_url, er enghraifft, fel 'http://localhost:8080/api/v1/clusters/',
  • cluster_name – enw eich clwstwr yn Ambari,
  • headers = {'X-Requested-By': 'ambari'}
  • a'r tu mewn auth dyma'ch mewngofnodi a'ch cyfrinair ar gyfer Ambari: auth = ('login', 'password').

Nid yw'r swyddogaeth ei hun yn ddim mwy na chwpl o alwadau trwy'r API REST i Ambari. O safbwynt rhesymegol, rydym yn gyntaf yn derbyn rhestr o wasanaethau rhedeg ar nod, ac yna'n gofyn ar glwstwr penodol, ar nod penodol, i drosglwyddo gwasanaethau o'r rhestr i'r wladwriaeth INSTALLED. Swyddogaethau ar gyfer lansio pob gwasanaeth, ar gyfer trosglwyddo nodau i'r wladwriaeth Maintenance ac ati yn edrych yn debyg - dim ond ychydig o geisiadau ydyn nhw trwy'r API.

Dosbarth Mcs

Dyma sut olwg sydd ar ddarn o god sy'n cynnwys dosbarth 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

Wrth y fynedfa i'r dosbarth Mcs rydym yn trosglwyddo id y prosiect y tu mewn i'r cwmwl a'r ID defnyddiwr, yn ogystal â'i gyfrinair. Mewn swyddogaeth vm_turn_on rydym am droi un o'r peiriannau ymlaen. Mae'r rhesymeg yma ychydig yn fwy cymhleth. Ar ddechrau'r cod, gelwir tair swyddogaeth arall: 1) mae angen inni gael tocyn, 2) mae angen i ni drosi'r enw gwesteiwr yn enw'r peiriant yn MCS, 3) cael id y peiriant hwn. Nesaf, rydym yn syml yn gwneud cais post ac yn lansio'r peiriant hwn.

Dyma sut olwg sydd ar y swyddogaeth ar gyfer cael tocyn:

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

Dosbarth Autoscaler

Mae'r dosbarth hwn yn cynnwys swyddogaethau sy'n gysylltiedig â'r rhesymeg gweithredu ei hun.

Dyma sut olwg sydd ar ddarn o god ar gyfer y dosbarth hwn:

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

Rydym yn derbyn dosbarthiadau ar gyfer mynediad. Ambari и Mcs, rhestr o nodau a ganiateir ar gyfer graddio, yn ogystal â pharamedrau cyfluniad nodau: cof a cpu a ddyrennir i'r nod yn YARN. Mae yna hefyd 2 baramedr mewnol q_ram, q_cpu, sef ciwiau. Gan eu defnyddio, rydym yn storio gwerthoedd y llwyth clwstwr cyfredol. Os gwelwn fod llwyth wedi cynyddu'n gyson dros y 5 munud diwethaf, yna byddwn yn penderfynu bod angen ychwanegu nod +1 i'r clwstwr. Mae'r un peth yn wir am gyflwr tanddefnyddio clwstwr.

Mae'r cod uchod yn enghraifft o swyddogaeth sy'n tynnu peiriant o'r clwstwr a'i atal yn y cwmwl. Yn gyntaf mae datgomisiynu YARN Nodemanager, yna mae'r modd yn troi ymlaen Maintenance, yna byddwn yn atal pob gwasanaeth ar y peiriant ac yn diffodd y peiriant rhithwir yn y cwmwl.

2. Sgript arsylwr.py

Cod sampl oddi yno:

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)

Ynddo, rydym yn gwirio a oes amodau wedi'u creu ar gyfer cynyddu capasiti'r clwstwr ac a oes unrhyw beiriannau wrth gefn, cael enw gwesteiwr un ohonynt, ei ychwanegu at y clwstwr a chyhoeddi neges amdano ar Slack ein tîm. Ar ôl hynny mae'n dechrau cooldown_period, pan na fyddwn yn ychwanegu neu'n tynnu unrhyw beth o'r clwstwr, ond yn syml yn monitro'r llwyth. Os yw wedi sefydlogi a'i fod o fewn coridor y gwerthoedd llwyth gorau posibl, yna yn syml iawn rydym yn parhau i fonitro. Os nad oedd un nod yn ddigon, yna rydym yn ychwanegu un arall.

Mewn achosion pan fydd gennym wers o'n blaenau, rydym eisoes yn gwybod yn sicr na fydd un nod yn ddigon, felly rydym yn cychwyn yr holl nodau rhydd ar unwaith ac yn eu cadw'n actif tan ddiwedd y wers. Mae hyn yn digwydd gan ddefnyddio rhestr o stampiau amser gweithgaredd.

Casgliad

Mae Autoscaler yn ddatrysiad da a chyfleus ar gyfer yr achosion hynny pan fyddwch chi'n profi llwythiad clwstwr anwastad. Rydych chi ar yr un pryd yn cyflawni'r cyfluniad clwstwr dymunol ar gyfer llwythi brig ac ar yr un pryd peidiwch â chadw'r clwstwr hwn yn ystod y tanlwytho, gan arbed arian. Wel, ac mae hyn i gyd yn digwydd yn awtomatig heb eich cyfranogiad. Nid yw'r autoscaler ei hun yn ddim mwy na set o geisiadau i'r rheolwr clwstwr API a'r API darparwr cwmwl, wedi'u hysgrifennu yn unol â rhesymeg benodol. Yr hyn y mae angen i chi ei gofio yn bendant yw rhannu nodau yn 3 math, fel y gwnaethom ysgrifennu'n gynharach. A byddwch yn hapus.

Ffynhonnell: hab.com

Ychwanegu sylw