Kumaha carana ngadamel autoscaler sorangan pikeun klaster

Halo! Urang ngalatih jalma pikeun digawekeun ku data badag. Teu mungkin pikeun ngabayangkeun program atikan dina data badag tanpa klaster sorangan, dimana sakabeh pamilon gawé bareng. Ku sabab kitu, program urang salawasna mibanda eta :) Urang kalibet dina konfigurasi na, tuning jeung administrasi, jeung guys langsung ngajalankeun jobs MapReduce aya na make Spark.

Dina tulisan ieu kami bakal nyarioskeun ka anjeun kumaha urang ngarengsekeun masalah beban klaster anu henteu rata ku cara nyerat autoscaler urang sorangan nganggo awan. Mail.ru Cloud Solutions.

masalah

Kluster kami henteu dianggo dina modeu biasa. Pembuangan kacida henteu rata. Salaku conto, aya kelas praktis, nalika sadayana 30 urang sareng guru angkat ka kluster sareng mimitian nganggo éta. Atawa deui, aya poé saméméh deadline nalika beban naek greatly. Sesa waktos kluster beroperasi dina modeu underload.

Solusi #1 nyaéta pikeun ngajaga kluster anu bakal tahan beban puncak, tapi bakal dianggurkeun sesa waktos.

Solusi #2 nyaéta ngajaga klaster leutik, dimana anjeun sacara manual nambihan titik sateuacan kelas sareng nalika beban puncak.

Solusi #3 nyaéta ngajaga klaster leutik sareng nyerat autoscaler anu bakal ngawas beban kluster ayeuna sareng, nganggo rupa-rupa API, tambahkeun sareng cabut titik tina kluster.

Dina tulisan ieu urang bakal ngobrol ngeunaan solusi #3. Autoscaler ieu gumantung pisan kana faktor éksternal tinimbang faktor internal, sareng panyadia sering henteu nyayogikeunana. Kami nganggo infrastruktur awan Mail.ru Cloud Solutions sareng nyerat autoscaler nganggo API MCS. Sareng saprak urang ngajarkeun cara damel sareng data, kami mutuskeun pikeun nunjukkeun kumaha anjeun tiasa nyerat autoscaler anu sami pikeun tujuan anjeun nyalira sareng nganggo éta sareng awan anjeun.

Prerequisites

Mimiti, anjeun kedah gaduh kluster Hadoop. Contona, urang ngagunakeun distribusi HDP.

Supados titik anjeun tiasa gancang ditambahkeun sareng dipiceun, anjeun kedah gaduh sebaran peran anu tangtu diantara titik.

  1. Master titik. Nya, teu aya anu peryogi pisan pikeun ngajelaskeun di dieu: titik utama kluster, dimana, contona, supir Spark diluncurkeun, upami anjeun nganggo mode interaktif.
  2. Node tanggal. Ieu mangrupikeun titik dimana anjeun nyimpen data dina HDFS sareng dimana itungan lumangsung.
  3. Komputasi titik. Ieu titik dimana anjeun teu nyimpen nanaon dina HDFS, tapi dimana itungan lumangsung.

titik penting. Autoscaling bakal lumangsung alatan titik tipe katilu. Upami anjeun ngamimitian nyandak sareng nambihan titik-titik tina jinis kadua, kagancangan réspon bakal rendah pisan - decommissioning sareng recommitting bakal nyandak jam dina kluster anjeun. Ieu, tangtosna, sanés anu anjeun ngarepkeun tina autoscaling. Nyaéta, urang henteu nyabak titik tina jinis kahiji sareng kadua. Aranjeunna bakal ngagambarkeun kluster giat minimum anu bakal aya sapanjang durasi program.

Janten, autoscaler kami ditulis dina Python 3, nganggo API Ambari pikeun ngatur jasa klaster, dianggo API ti Mail.ru Cloud Solutions (MCS) pikeun ngamimitian jeung ngeureunkeun mesin.

Arsitéktur solusi

  1. Modul autoscaler.py. Éta ngandung tilu kelas: 1) fungsi pikeun gawé bareng Ambari, 2) fungsi pikeun gawé bareng MCS, 3) fungsi anu aya hubunganana langsung sareng logika autoscaler.
  2. naskah observer.py. Intina diwangun ku aturan anu béda: iraha sareng dina waktos naon nyauran fungsi autoscaler.
  3. file konfigurasi config.py. Éta ngandung, contona, daptar titik anu diidinan pikeun autoscaling sareng parameter sanésna anu mangaruhan, contona, sabaraha lami ngantosan ti mimiti node énggal ditambah. Aya ogé timestamps pikeun mimiti kelas, ku kituna saméméh kelas nu maksimum diwenangkeun konfigurasi klaster dibuka.

Hayu urang ayeuna ningali potongan kode dina dua file munggaran.

1. modul Autoscaler.py

kelas Ambari

Ieu naon sapotong kode ngandung hiji kelas Sigana mah 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

Di luhur, sabagé conto, anjeun tiasa ningali palaksanaan fungsina stop_all_services, anu ngeureunkeun sadaya jasa dina titik kluster anu dipikahoyong.

Di lawang kelas Ambari anjeun lulus:

  • ambari_url, contona, kawas 'http://localhost:8080/api/v1/clusters/',
  • cluster_name - nami klaster anjeun di Ambari,
  • headers = {'X-Requested-By': 'ambari'}
  • jeung jero auth Ieu login sareng kecap akses anjeun pikeun Ambari: auth = ('login', 'password').

Fungsina sorangan henteu langkung ti sababaraha telepon via REST API ka Ambari. Tina sudut pandang logis, urang mimiti nampi daptar jasa anu ngajalankeun dina titik, teras naroskeun dina klaster anu dipasihkeun, dina titik anu dipasihkeun, pikeun mindahkeun jasa tina daptar ka nagara. INSTALLED. Fungsi pikeun ngaluncurkeun sadaya jasa, pikeun nransferkeun titik ka kaayaan Maintenance jsb kasampak sarupa - aranjeunna ngan sababaraha requests ngaliwatan API.

Kelas Mcs

Ieu naon sapotong kode ngandung hiji kelas Sigana mah 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

Di lawang kelas Mcs urang lulus id proyék di jero awan sareng id pangguna, ogé kecap konci na. Dina fungsi vm_turn_on urang rék ngahurungkeun salah sahiji mesin. Logika di dieu rada rumit. Dina awal kode, tilu fungsi séjén disebut: 1) urang kudu meunang token a, 2) urang kudu ngarobah hostname kana nami mesin di MCS, 3) meunang id tina mesin ieu. Salajengna, urang ngan saukur ngadamel pamundut pos sareng ngaluncurkeun mesin ieu.

Ieu mangrupikeun fungsi pikeun kéngingkeun token sapertos kieu:

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

Kelas Autoscaler

Kelas ieu ngandung fungsi nu patali jeung logika operasi sorangan.

Ieu mangrupikeun sapotong kode pikeun kelas ieu:

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

Kami nampi kelas pikeun asup. Ambari и Mcs, daptar titik nu diwenangkeun pikeun skala, kitu ogé parameter konfigurasi titik: memori sareng CPU dialokasikeun ka titik dina BENANG. Aya ogé 2 parameter internal q_ram, q_cpu, anu antrian. Ngagunakeun aranjeunna, urang nyimpen nilai beban klaster ayeuna. Upami urang ningali yén salami 5 menit terakhir aya beban anu ningkat sacara konsisten, maka urang mutuskeun yén urang kedah nambihan titik +1 kana kluster. Sami bener keur kaayaan underutilization klaster.

Kodeu di luhur mangrupa conto fungsi nu ngaluarkeun mesin tina klaster jeung ngeureunkeunana dina awan. Mimiti aya decommissioning YARN Nodemanager, teras modeu hurung Maintenance, lajeng urang ngeureunkeun sagala jasa dina mesin jeung mareuman mesin virtual dina awan.

2. Pengamat naskah.py

Kode sampel ti dinya:

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 jerona, urang pariksa naha kaayaan parantos diciptakeun pikeun ningkatkeun kapasitas klaster sareng naha aya mesin anu dicadangkeun, kéngingkeun hostname salah sahijina, tambahkeun kana kluster sareng nyebarkeun pesen ngeunaan éta dina Slack tim kami. Sanggeus éta dimimitian cooldown_period, lamun urang teu nambahan atawa mupus nanaon tina klaster, tapi ngan ngawas beban. Upami éta stabil sareng aya dina koridor nilai beban anu optimal, maka urang teraskeun ngawaskeun. Upami hiji titik henteu cekap, maka urang tambahkeun hiji deui.

Pikeun kasus nalika urang ngagaduhan palajaran payun, urang parantos terang yén hiji titik moal cekap, janten urang langsung ngamimitian sadaya titik gratis sareng tetep aktip dugi ka ahir palajaran. Ieu lumangsung ngagunakeun daptar cap waktu aktivitas.

kacindekan

Autoscaler mangrupikeun solusi anu saé sareng merenah pikeun kasus-kasus nalika anjeun ngalaman beban klaster anu henteu rata. Anjeun sakaligus ngahontal konfigurasi klaster nu dipikahoyong pikeun beban puncak jeung dina waktos anu sareng teu tetep klaster ieu salila underload, nyimpen duit. Nya, tambah ieu sadayana kajantenan sacara otomatis tanpa partisipasi anjeun. Autoscaler sorangan henteu langkung ti sakumpulan pamundut ka API manajer klaster sareng API panyadia awan, ditulis dumasar kana logika anu tangtu. Anu anjeun kedah émut nyaéta ngabagi titik kana 3 jinis, sapertos anu kami tulis sateuacana. Jeung anjeun bakal senang.

sumber: www.habr.com

Tambahkeun komentar