PĂ«rshĂ«ndetje! Ne edukojmĂ« njerĂ«zit pĂ«r punĂ«n me tĂ« dhĂ«na tĂ« mĂ«dha. ĂshtĂ« e pamundur tĂ« imagjinohet njĂ« program arsimor pĂ«r tĂ« dhĂ«na tĂ« mĂ«dha pa njĂ« klaster tĂ« vetin, mbi tĂ« cilin tĂ« gjithĂ« pjesĂ«marrĂ«sit punojnĂ« sĂ« bashku. PĂ«r kĂ«tĂ« arsye, nĂ« programin tonĂ« ai Ă«shtĂ« gjithmonĂ« atje đ Ne merremi me konfigurimin, tunimin dhe administrimin e tij, ndĂ«rsa studentĂ«t e aktivizojnĂ« atje punĂ«t MapReduce dhe pĂ«rdorin Spark.
Në këtë post, do të flasim se si ne zgjidhëm problemin e ngarkesës së pandershme të klasterit, duke shkruar skalerin tonë automatik, duke përdorur cloud. .
Problemi
Klasteri ynë përdoret jo krejtësisht në një mënyrë tipike. Shfrytëzimi është shumë i pabarabartë. Për shembull, ka aktivitete praktike kur të gjithë 30 persona dhe mësuesi hyjnë në klaster dhe fillojnë ta përdorin atë. Ose ndodhin ditë para afateve kur ngarkesa rritet shumë. Në të gjitha pjesët e tjera, klasteri punon në mënyrë të nën-shfrytëzuar.
Zgjidhja â1 â Ă«shtĂ« tĂ« mbash njĂ« klaster qĂ« do tĂ« pĂ«rballojĂ« ngarkesat maksimale, por do tĂ« jetĂ« i papunĂ« nĂ« gjithĂ« kohĂ«n tjetĂ«r.
Zgjidhja â2 â Ă«shtĂ« tĂ« mbash njĂ« klaster tĂ« vogĂ«l, nĂ« tĂ« cilin tĂ« shtosh manualisht node para aktiviteteve dhe gjatĂ« ngarkesave maksimale.
Zgjidhja â3 â Ă«shtĂ« tĂ« mbash njĂ« klaster tĂ« vogĂ«l dhe tĂ« shkruash njĂ« skaler automatik, qĂ« do tĂ« monitorojĂ« ngarkesĂ«n aktuale tĂ« klasterit dhe vetĂ«, duke pĂ«rdorur API tĂ« ndryshme, do tĂ« shtojĂ« dhe fshijĂ« node nga klasteri.
NĂ« kĂ«tĂ« post, do tĂ« flasim pĂ«r zgjidhjen â3. Ky skaler automatik Ă«shtĂ« shumĂ« i varur nga faktorĂ«t e jashtĂ«m, dhe shpesh ofruesit e tij nuk e ofrojnĂ«. Ne pĂ«rdorim infrastrukturĂ«n cloud tĂ« Mail.ru Cloud Solutions dhe shkruam njĂ« skaler automatik, duke pĂ«rdorur API MCS. Dhe pasi ne edukojmĂ« pĂ«r punĂ«n me tĂ« dhĂ«na, e kemi vendosur tĂ« tregojmĂ« se si mund tĂ« shkruani njĂ« skaler tĂ« tillĂ« pĂ«r qĂ«llimet tuaja dhe tĂ« pĂ«rdorni me cloud-in tuaj.
Kushtet e nevojshme
Së pari, ju duhet të keni një klaster Hadoop. Ne, për shembull, përdorim distribuimin HDP.
Për të pasur node që mund të shtohen dhe hiqen shpejt, ju duhet të keni një shpërndarje të caktuar rolesh mbi node.
- Node Master. Këtu nuk ka nevojë për shpjegime të mëtejshme: node kryesore e klasterit, mbi të cilën aktivizohet, për shembull, drejtuesi i Spark, nëse jeni duke përdorur modalitetin interaktiv.
- Node Data. Kjo është node ku ndodhen të dhënat në HDFS dhe aty ndodhin gjithashtu llogaritjet.
- Node Computation. Kjo është node, mbi të cilin nuk ruhet asgjë në HDFS, por aty ndodhin llogaritjet.
NjĂ« moment i rĂ«ndĂ«sishĂ«m. Autoskalimi do tĂ« ndodhi pĂ«r shkak tĂ« node-ve tĂ« tipit tĂ« tretĂ«. NĂ«se filloni tĂ« hiqni dhe shtoni node tĂ« tipit tĂ« dytĂ«, reagimi do tĂ« jetĂ« shumĂ« i ngadaltĂ« â dekomishimi dhe rekomishimi do tĂ« marrin orĂ« nĂ« klasterin tuaj. Kjo, natyrisht, nuk Ă«shtĂ« ajo qĂ« prisni nga autoskalimi. Pra, node-t e tipit tĂ« parĂ« dhe tĂ« dytĂ« nuk i prekim. Ata do tĂ« pĂ«rfaqĂ«sojnĂ« njĂ« klaster minimalisht tĂ« qĂ«ndrueshĂ«m, qĂ« do tĂ« ekzistojĂ« gjatĂ« gjithĂ« kohĂ«s sĂ« programit.
Pra, skaleri ynë automatik është shkruar në Python 3, përdor API-në Ambari për menaxhimin e shërbimeve të klasterit, përdor (MCS) për të aktivizuar dhe ndaluar makinacak.
Arkitektura e zgjidhjes
- Moduli
autoscaler.py. Ai përmban tre klasa: 1) funksione për punën me Ambari, 2) funksione për punën me MCS, 3) funksione që lidhen drejtpërdrejt me logjikën e funksionimit të skalerit automatik. - Skripti
observer.py. Në thelb përbëhet nga rregulla të ndryshme: kur dhe në çfarë momentesh të thërrasë funksionet e skalerit automatik. - Skedari i konfigurimit
config.py. Aty ndodhet, për shembull, lista e node-ve të lejuara për autoskalim dhe parametra të tjerë që ndikojnë, për shembull, në sa kohë të prisni nga momenti kur është shtuar një node e re. Aty ndodhen gjithashtu dhe timestamp-et e fillimit të aktiviteteve, për t'u siguruar që para aktivitetit të aktivizohet konfigurimi maksimal i lejuar i klasterit.
Tani le të shikojmë pjesët e kodit që ndodhen brenda dy skedarëve të parë.
1. Moduli autoscaler.py
Klasa Ambari
Ja si duket një pjesë kodit që përmban klasën 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 messageMund të shihni një implementim të funksionit më sipër si shembull. stop_all_services, i cili ndalon të gjitha shërbimet në nodën e duhur të grumbullit.
Kur keni klasën Ambari ju jepni:
ambari_url, pĂ«r shembull, nĂ« formĂ«n e'http://localhost:8080/api/v1/clusters/',cluster_nameâ emri i grumbullit tuaj nĂ« Ambari,headers = {'X-Requested-By': 'ambari'}- dhe brenda
authndodhen kredencialet tuaja për Ambari:auth = ('login', 'password').
Funksioni vetĂ« pĂ«rbĂ«het nga disa thirrje pĂ«rmes REST API nĂ« Ambari. Nga pikĂ«pamja logjike, fillimisht marrim listĂ«n e shĂ«rbimeve tĂ« aktivizuara nĂ« nodĂ«, dhe mĂ« pas kĂ«rkojmĂ« nĂ« kĂ«tĂ« grumbull, nĂ« kĂ«tĂ« nodĂ«, qĂ« shĂ«rbimet nga lista tĂ« kalojnĂ« nĂ« gjendjen INSTALLED. Funksionet pĂ«r aktivizimin e tĂ« gjitha shĂ«rbimeve, pĂ«r kalimin e nodave nĂ« gjendjen Maintenance etj. duken ngjashĂ«m â ato janĂ« thjesht disa kĂ«rkesa pĂ«rmes API.
Klasës Mcs
Ja si duket një pjesë kodit që përmban klasën 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_codeKur keni klasën Mcs ne i kalojmë id e projektit brenda cloud-it dhe id e përdoruesit, si dhe fjalëkalimin e tij. Në funksionin vm_turn_on duam të aktivizojmë njërën nga makinat. Logjika këtu është pak më e komplikuar. Në fillim të kodit bëhet thirrja e tre funksioneve të tjera: 1) na nevojitet të marrëim token-in, 2) na nevojitet të konvertojmë hostname-in në emrin e makinës në MCS, 3) të marrim id e kësaj makine. Më pas bëjmë një kërkesë post dhe aktivizojmë këtë makinë.
Këtu ë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.tokenKlasës Autoscaler
Ky klas përmban funksione që lidhen me logjikën e funksionimit të saj.
Ja si duket një pjesë e kodit të këtij klasi:
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('Decommission 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 messageNe marrim si input klasat Ambari dhe Mcs, lista e nodave të lejuara për shkallëzim, si dhe parametrat e konfigurimit të nodave: memoria dhe CPU, të dedikuara për nodën në YARN. Ka gjithashtu 2 parametra të brendshëm q_ram, q_cpu, të cilat janë radhë. Me anë të tyre ruajmë vlerat e ngarkesës aktuale të klasterit. Nëse shohim se në pesë minutat e fundit ka pasur ngarkesë të lartë, ne marrim vendimin për të shtuar +1 nod në klaster. E njëjta është e vërtetë edhe për gjendjen e nën-ngarkesës së klasterit.
Në kodin më sipër është një shembull funksioni që heq një makinë nga klasteri dhe e ndalon atë në cloud. Fillimisht ndodh dekomisionimi YARN Nodemanager, pastaj aktivizohet moda Maintenance, më pas ndalojmë të gjitha shërbimet në makinën dhe ndajmë makinën virtuale në cloud.
2. Skripti observer.py
Shembulli i kodit nga aty:
nëse scaler.assert_up(config.scale_up_thresholds) == True:
hostname = cloud.get_vm_to_up(config.scaling_hosts)
nëse hostname != None:
status1 = scaler.scale_up(hostname)
nëse status1 == 'Sukses':
text = {"text": "{0} është shkallëzuar 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ë ne kontrollojmë nëse kushtet për rritjen e kapaciteve të klasterit janë përmbushur dhe nëse ka ndonjë makinë të lirë, të marrim emrin e saj, ta shtojmë në klaster dhe publikojmë një mesazh për këtë në Slack të ekipit tonë. Pas kësaj fillon perioda e ftohjes, kur nuk shtojmë dhe as heqim asgjë nga klasteri, por vetëm monitorojmë ngarkesën. Nëse ajo është stabilizuar dhe është brenda koridorit optimal të vlerave të ngarkesës, ne vazhdojmë monitorimin. Nëse një nod nuk mjafton, shtojmë edhe një.
Për rastet kur kemi një seancë përpara, ne e dimë me siguri se një nod nuk do të jetë e mjaftueshme, prandaj menjëherë fillojmë të gjitha nodat e lira dhe i mbajmë aktive deri në fund të seancës. Kjo ndodh përmes një liste timestamp-esh të seancave.
Përfundimi
Autoskaleri â Ă«shtĂ« njĂ« zgjidhje e mirĂ« dhe e pĂ«rshtatshme pĂ«r ata raste kur keni njĂ« ngarkesĂ« tĂ« paekuilibruar tĂ« klasterit. Ju arrini nĂ« tĂ« njĂ«jtĂ«n kohĂ« konfigurimin e nevojshĂ«m tĂ« klasterit pĂ«r ngarkesa maksimale dhe duke mos e mbajtur kĂ«tĂ« klaster nĂ« kohĂ«n e nĂ«n-ngarkesĂ«s, kurseni burimet. Po ashtu, kjo ndodh nĂ« mĂ«nyrĂ« automatike pa ndihmĂ«n tuaj. VetĂ« autoskaleri nuk Ă«shtĂ« mĂ« shumĂ« se njĂ« grup kĂ«rkesash pĂ«r API-nĂ« e menaxherit tĂ« klasterit dhe API-nĂ« e ofruesit tĂ« cloud-it, tĂ« shkruara sipas njĂ« logjike tĂ« caktuar. E rĂ«ndĂ«sishme Ă«shtĂ« tĂ« mbahet mend ndarja e nodave nĂ« 3 tipe, siç kemi shkruar mĂ« parĂ«. Dhe do tĂ« keni sukses.
Burimi: habr.com
