Meriv çawa otoskalera xwe ji bo komê çê dike

Slav! Em mirovan perwerde dikin ku bi daneyên mezin re bixebitin. Ne mimkûn e ku meriv bernameyek perwerdehiyê li ser daneyên mezin bêyî komika xwe, ku hemî beşdar bi hev re kar dikin, xeyal bikin. Ji ber vê yekê, bernameya me her gav wê heye :) Em bi veavakirin, birêkûpêkkirin û rêveberiya wê ve mijûl in, û xort rasterast karên MapReduce li wir didin destpêkirin û Spark bikar tînin.

Di vê postê de em ê ji we re vebêjin ka me çawa pirsgirêka barkirina komê ya nehevseng bi nivîsandina otoskalera xwe bi karanîna ewr çareser kir Mail.ru Cloud Solutions.

pirsgirêka

Koma me di moda tîpîk de nayê bikar anîn. Rakirin pir nehevseng e. Bo nimûne, dersên pratîk hene, dema ku her 30 kes û mamosteyek diçin komê û dest bi karanîna wê dikin. An jî dîsa, rojên berî muhletê hene ku bar pir zêde dibe. Demên mayî kom di moda binbarkirinê de dixebite.

Çareseriya #1 ev e ku meriv komeke ku dê li ber bargiraniyên lûtkeyê bisekine, lê dê wextê mayî bêkar bimîne.

Çareseriya #2 ev e ku hûn komek piçûk bihêlin, ku hûn bi destan berî dersan û di dema barkirina lûtkeyê de girêkan lê zêde bikin.

Çareseriya #3 ev e ku meriv komek piçûk bihêle û otoscalerek binivîse ku dê barkirina heyî ya komê bişopîne û, bi karanîna API-yên cihêreng, girêkan ji komê zêde bike û jê rake.

Di vê postê de em ê li ser çareseriya #3 biaxivin. Ev otoscaler li şûna yên hundurîn pir bi faktorên derveyî ve girêdayî ye, û peydaker bi gelemperî wê peyda nakin. Em binesaziya ewr a Mail.ru Cloud Solutions bikar tînin û bi karanîna MCS API-ê otoscalerek nivîsand. Û ji ber ku em hîn dikin ka meriv çawa bi daneyan re dixebite, me biryar da ku nîşan bidin ka hûn çawa dikarin ji bo armancên xwe otoscalerek wusa binivîsin û bi ewrê xwe re bikar bînin.

Pêdivî ye

Pêşîn, divê hûn komek Hadoop hebe. Mînak em belavkirina HDPê bikar tînin.

Ji bo ku girêkên we zû werin zêdekirin û rakirin, divê hûn di nav girêkan de xwedî dabeşek diyar a rolan bin.

  1. Node master. Welê, tiştek bi taybetî hewce tune ku li vir were ravekirin: girêka sereke ya komê, ku, mînakî, ajokara Spark-ê li ser tê destpêkirin, heke hûn moda danûstendinê bikar bînin.
  2. Date node. Ev girêka ku hûn daneyan li ser HDFS-ê hildigirin û li ku derê hesab çêdibin ev e.
  3. Node Computing. Ev nodek e ku hûn li ser HDFS-ê tiştek hilnagirin, lê li ku derê hesab çêdibin.

Xaleke girîng. Xweserî dê ji ber girêkên celebê sêyemîn pêk were. Ger hûn dest bi girtin û lê zêdekirina girêkên celebê duyemîn bikin, leza bersivê dê pir kêm be - hilweşandin û ji nû ve veqetandin dê bi demjimêran li ser koma we bigire. Ev, bê guman, ne ya ku hûn ji autoscaling hêvî dikin e. Ango em dest nadin girêkên cureyên yekem û duyemîn. Ew ê komek guncan a herî kêm a ku dê di dirêjahiya bernameyê de hebe temsîl bikin.

Ji ber vê yekê, otoskalera me di Python 3 de hatî nivîsandin, Ambari API bikar tîne da ku karûbarên komê birêve bibe, bikar tîne API ji Mail.ru Cloud Solutions (MCS) ji bo destpêkirin û rawestandina makîneyan.

Mîmariya Çareseriyê

  1. Modûl autoscaler.py. Ew sê çînan pêk tîne: 1) fonksiyonên ji bo xebata bi Ambari, 2) fonksiyonên ji bo xebata bi MCS re, 3) fonksiyonên ku rasterast bi mantiqa otoscaler ve girêdayî ne.
  2. Nivîs observer.py. Di bingeh de ew ji rêzikên cihêreng pêk tê: kengê û di kîjan kêliyan de meriv fonksiyonên autoscaler bang bike.
  3. Pelê veavakirinê config.py. Mînakî, navnîşek girêkên ku ji bo pîvandina otomatîkî têne destûr kirin û pîvanên din ên ku bandor dikin, vedihewîne, mînakî, ka meriv çiqas li bendê bimîne ji dema ku girêkek nû hatî zêdekirin. Di heman demê de ji bo destpêkirina dersan mohra demjimêran jî hene, da ku berî polê veavakirina komê ya herî zêde destûr were destpêkirin.

Ka em naha li perçeyên kodê yên di hundurê du pelên pêşîn de binêrin.

1. Modula Autoscaler.py

Dersa Ambarî

Parçeyek kodek ku çînek vedihewîne bi vî rengî xuya dike 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

Li jor, wekî mînakek, hûn dikarin li pêkanîna fonksiyonê binêrin stop_all_services, ku hemî karûbaran li ser girêka komê ya xwestî radiwestîne.

Li ber deriyê polê Ambari tu derbas dibî:

  • ambari_url, bo nimûne, wek 'http://localhost:8080/api/v1/clusters/',
  • cluster_name - navê koma we li Ambari,
  • headers = {'X-Requested-By': 'ambari'}
  • û hundir auth Li vir navê bikarhêner û şîfreya we ji bo Ambari ye: auth = ('login', 'password').

Fonksiyon bixwe ji çend bangên bi REST API-ya Ambari re ne tiştek din e. Ji nihêrînek mentiqî, em pêşî navnîşek karûbarên xebitandinê li ser girêkekê werdigirin, û dûv re li ser komek diyarkirî, li ser girêkek diyarkirî, dipirsin ku karûbaran ji navnîşê berbi dewletê veguhezînin. INSTALLED. Fonksiyonên destpêkirina hemî karûbaran, ji bo veguheztina girêkan ji dewletê re Maintenance hwd. dişibin hev - ew bi API-yê tenê çend daxwaz in.

Class Mcs

Parçeyek kodek ku çînek vedihewîne bi vî rengî xuya dike 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

Li ber deriyê polê Mcs em id projeyê di hundurê ewr û nasnameya bikarhêner û şîfreya wî de derbas dikin. Di fonksiyonê de vm_turn_on em dixwazin yek ji makîneyan vekin. Mantiq li vir hinekî tevlihevtir e. Di destpêka kodê de, sê fonksiyonên din têne gotin: 1) pêdivî ye ku em nîşanek bistînin, 2) em hewce ne ku navê mêvandar di nav navê makîneya MCS de veguherînin, 3) nasnameya vê makîneyê bistînin. Dûv re, em tenê daxwazek postê dikin û vê makîneyê didin destpêkirin.

Fonksiyon ji bo bidestxistina token wiha xuya dike:

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

Dersa Autoscaler

Ev çîn fonksiyonên bi mantiqa xebitandinê bixwe ve girêdayî ne.

Parçeyek kodê ji bo vê polê bi vî rengî xuya dike:

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

Em dersan ji bo têketinê qebûl dikin. Ambari и Mcs, navnîşek girêkên ku ji bo pîvandinê têne destûr kirin, û her weha pîvanên veavakirina girêk: bîr û cpu ji girêka YARN re hatî veqetandin. Di heman demê de 2 pîvanên hundurîn q_ram, q_cpu jî hene, ku rêz in. Bi karanîna wan, em nirxên barkirina komê ya heyî hilînin. Ger em bibînin ku di van 5 hûrdemên paşîn de barek bi domdarî zêde bûye, wê hingê em biryar didin ku pêdivî ye ku em +1 nodê li komê zêde bikin. Heman tişt ji bo dewleta kêmkaranîna komê jî rast e.

Koda li jor mînakek fonksiyonek e ku makîneyek ji komê derdixe û di ewr de disekine. Pêşî hilweşandin heye YARN Nodemanager, paşê modê vedike Maintenance, wê hingê em hemî karûbarên li ser makîneyê rawestînin û makîneya virtual ya di ewr de qut bikin.

2. Skrîpt çavdêr.py

Nimûneya kodê ji wir:

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)

Di wê de, em kontrol dikin ka şert û merc ji bo zêdekirina kapasîteya komê hatine afirandin û gelo makîneyên rezervê hene an na, navê mêvandarê yek ji wan bistînin, wê li komê zêde bikin û li ser Slack-a tîmê me peyamek biweşînin. Piştî ku ew dest pê dike cooldown_period, gava ku em tiştek ji komê zêde nakin an jê nakin, lê bi tenê barkirinê çavdêrî dikin. Ger ew stabîl bûye û di nav korîdora nirxên barkirina çêtirîn de ye, wê hingê em tenê çavdêriyê didomînin. Ger yek nodek têrê nekir, wê hingê em yekî din lê zêde dikin.

Ji bo rewşên ku li pêşiya me dersek heye, em jixwe pê dizanin ku yek girêk têrê nake, ji ber vê yekê em tavilê dest bi hemî girêkên belaş dikin û wan heya dawiya dersê çalak dihêlin. Ev bi karanîna navnîşek demjimêrên çalakiyê pêk tê.

encamê

Autoscaler ji bo wan rewşan çareseriyek baş û hêsan e ku hûn barkirina komê ya nehevseng tecrûbir dikin. Hûn di heman demê de ji bo barkirinên lûtkeyê veavakirina komê ya xwestî bi dest dixin û di heman demê de vê komê di bin barkirinê de nahêlin, drav teserûf dikin. Welê, plus ev hemî bixweber bêyî beşdariya we diqewime. Autoscaler bi xwe ji komek daxwaznameyên API-a rêveberê komê û API-ya peydakarê ewr re, ku li gorî mentiqek diyarkirî hatî nivîsandin, ne tiştek din e. Ya ku hûn bê guman hewce ne ku ji bîr bikin ev e ku wekî ku me berê nivîsî, dabeşkirina girêkan li 3 celeban e. Û hûn ê kêfxweş bibin.

Source: www.habr.com

Add a comment