U velikim cloud sistemima, posebno se postavlja pitanje automatskog upravljanja ili izjednačavanja opterećenja na računalne resurse. Ovim pitanjem su se bavili i u Tioniksu (razvijaču i operateru cloud usluga, deo grupe kompanija Rostelekom).
I, pošto je naša osnovna platforma za razvoj Openstack, a mi, kao i svi ljudi, smo lenji, odlučili smo da pronađemo neki gotov modul koji već postoji u okviru platforme. Naš izbor je pao na Watcher, koji smo odlučili da koristimo za svoje potrebe.
Za početak, razjasnimo termine i definicije.
Termini i definicije
Cilj — to je ljudski čitljiv, posmatranoj i merljivi konačni rezultat koji treba postići. Za postizanje svakog cilja postoje jedna ili više strategija. Strategija je implementacija algoritma koji može pronaći rešenje za dati cilj.
Akcija (Action) — to je elementarna zadatak koji menja trenutno stanje ciljanog upravljanog resursa OpenStack klastera, kao što su: migracija virtuelne mašine (migration), promena stanja napajanja čvora (change_node_power_state), promena stanja nova usluge (change_nova_service_state), promena flašera (resize), registracija NOP poruke (nop), odsustvo akcija tokom određenog vremenskog perioda — pauza (sleep), prenos diska (volume_migrate).
Plan akcija (Action Plan) — specifičan tok akcija, izvršenih u određenom redu, za postizanje konkretne Cilja. Plan akcija takođe sadrži procenjenu globalnu efikasnost sa skupom pokazatelja efikasnosti. Plan akcija generiše Watcher nakon uspešno obavljenog audita, u kojem korišćena strategija pronalazi rešenje za postizanje cilja. Plan akcija se sastoji od liste uzastopnih akcija.
Audit (Audit) — to je zahtev za optimizaciju klastera. Optimizacija se izvršava da bi se postigao jedan Cilj u ovom klasteru. Za svaki uspešan audit, Watcher generiše Plan akcija.
Oblast audita (Audit Scope) — je niz virov, v okviru katerih se izvaja revizija (dostopna območja, agregatorji vozlišč, posamezni računalniški vozlišča ali vozlišča za shranjevanje itd.). Območje revizije je določeno v vsakem predlogi. Če območje revizije ni navedeno, se izvede revizija celotnega klasterja.
Predloga revizije (Audit Template) — shranjen niz nastavitev za zagon revizije. Predloge so potrebne za večkratno izvajanje revizij z enakimi nastavitvami. Predloga mora nujno vsebovati cilj revizije, če strategije niso navedene, izberejo se najbolj primerne iz obstoječih strategij.
Klaster (Cluster) — je niz fizičnih naprav, ki zagotavljajo računalniške vire, shranjevalne vire in omrežne vire ter so upravljani z istim upravljavskim vozliščem OpenStack.
Model podatkov klasterja (Cluster Data Model, CDM) — je logična predstavitev trenutnega stanja in topologije virov, ki jih upravlja klaster.
Kazalnik učinkovitosti (Efficacy Indicator) — kazalnik, ki kaže, kako dobro se izvaja rešitev, ki jo zagotavlja ta strategija. Kazalniki učinkovitosti so specifični za določen cilj in se običajno uporabljajo za izračun globalne učinkovitosti končnega akcijskega načrta.
Specifikacija učinkovitosti (Efficacy Specification) — je niz specifičnih lastnosti, povezanih z vsakim Ciljem, ki določajo različne kazalnike učinkovitosti, ki jih mora strategija, ki zagotavlja dosego ustreznega cilja, vključiti v svojo rešitev. V resnici se vsaka rešitev, ki jo predlaga strategija, preveri glede skladnosti s specifikacijo, preden se izračuna njena globalna učinkovitost.
„Številčevalni“ motor (Scoring Engine) — je izvršljiva datoteka, ki ima jasno določene vhodne podatke, jasno določene izhodne podatke in izvaja zgolj matematično nalogo. Tako izračun ni odvisen od okolja, v katerem se izvaja — dal bo enak rezultat kjerkoli.
Watcher načrtovalec (Watcher Planner) — del dela mehanizma odlučivanja Watcher. Ovaj modul prihvata set akcija generisanih strategijom i kreira plan radnog toka koji definiše kako planirati u vremenu te različite akcije i za svaku akciju, koje su prethodne pretpostavke.
Ciljevi i strategije Watcher
Cilj
Strategije
Dummy cilj
Dummy strategija
Dummy strategija koja koristi uzorke Scoring Engines
Dummy strategija sa promenom veličine
Ušteda energije
Strategija uštede energije
Konsolidacija servera
Osnovna offline konsolidacija servera
Strategija konsolidacije radnog opterećenja VM
Balansiranje radnog opterećenja
Strategija migracije balansa radnog opterećenja
Strategija balansa kapaciteta skladišta
Stabilizacija radnog opterećenja
Bučni sused
Bučni sused
Termalna optimizacija
Strategija zasnovana na temperaturi izlaza
Optimizacija protoka vazduha
Strategija migracije uniformnog protoka vazduha
Održavanje hardvera
Migracija zone
Neklasifikovano
Aktuator
Dummy cilj — rezervisani cilj koji se koristi za testiranje (reserved goal that is used for testing purposes).
Povezane strategije: Dummy strategija, Dummy strategija koja koristi uzorke Scoring Engines i Dummy strategija sa promenom veličine. Dummy strategija — fiktivna strategija koja se koristi za integracijsko testiranje putem Tempest-a. Ova strategija ne obezbeđuje nikakvu korisnu optimizaciju, njen jedini cilj je da koristi testove Tempest.
Dummy strategija koja koristi uzorke Scoring Engines — strategija je slična prethodnoj, razlikuje se samo po korišćenju uzorka 'ocenjivačkog pokretača', koji vrši brojanje koristeći metode mašinskog učenja.
Dummy strategija sa promenom veličine — strategija je slična prethodnoj, razlikuje se samo po korišćenju izmene veličine (migracija i promena veličine).
Ne koristi se u produkciji.
Ušteda energije — minimizovati potrošnju energije. Strategija ovog cilja, Strategija uštede energije u kombinaciji sa strategijom konsolidacije radnog opterećenja VM (Konsolidacija servera) sposobna je da obavlja funkcije dinamičkog upravljanja napajanjem (DPM), koje štede električnu energiju putem dinamičke konsolidacije radnog opterećenja čak i u periodima niskog opterećenja resursa: virtuelne mašine se premeštaju na manji broj čvorova, a nepotrebni čvorovi se isključuju. Nakon konsolidacije, strategija nudi rešenje o uključivanju/isključivanju čvorova u skladu sa zadata parametrima: “min_free_hosts_num” — broj slobodnih uključenih čvorova koji čekaju opterećenje, i “free_used_percent” — procentualni odnos slobodnih uključenih čvorova prema broju čvorova koji su zauzete mašinama. Za rad strategije mora biti uključen i podešen Ironic za rad sa uključivanjem/isključivanjem napajanja na čvorovima.
Parametri strategije
parametar
tip
po defaultu
opis
procent_izrabotenosti
Число
10.0
соотношение количества свободных вычислительных узлов к количеству вычислительных узлов с виртуальными машинами
min_free_hosts_num
Int
1
минимальное количество свободных вычислительных узлов
В облаке должно быть минимум два узла. Используемый метод — изменение состояния питания узла (change_node_power_state). Сбора метрик стратегия не требует.
Слияние серверов — минимизировать количество вычислительных узлов (консолидация). Имеет две стратегии: Basic Offline Server Consolidation и VM Workload Consolidation Strategy.
Стратегия Basic Offline Server Consolidation минимизирует общее количество используемых серверов, а также минимизирует количество миграций.
Базовая стратегия требует следующие метрики:
метрика
служба
vtičnike
комментарий
compute.node.cpu.percent
none
cpu_util
none
Параметры стратегии: migration_attempts — количество комбинаций для поиска потенциальных кандидатов на выключение (по умолчанию, 0, нет ограничений), period — интервал времени в секундах для получения статической агрегации из источника данных метрики (по умолчанию, 700).
Используемые методы: миграция, изменение состояния службы nova (change_nova_service_state).
Стратегия VM Workload Consolidation Strategy основана на эвристическом алгоритме первого подходящего (first-fit), который фокусируется на измеренной загрузке CPU и пытается минимизировать узлы, которые имеют слишком большую или слишком небольшую нагрузку с учетом ограничений емкости ресурсов. Эта стратегия предоставляет решение, которое приводит к более эффективному использованию ресурсов кластера, используя следующие четыре этапа:
- Фаза разгрузки — обработка перерасходованных ресурсов;
- Фаза консолидации — обработка недостаточно используемых ресурсов;
- Оптимизация решения — сокращение количества миграций;
- Отключение неиспользуемых вычислительных узлов.
Стратегия требует следующие метрики:
метрика
служба
vtičnike
комментарий
memory
none
disk.root.size
none
Следующие метрики не являются обязательными, но повышают точность стратегии, если доступны:
метрика
служба
vtičnike
комментарий
memory.resident
none
cpu_util
none
Параметры стратегии: period — интервал времени в секундах для получения статической агрегации из источника данных метрики (по умолчанию, 3600).
Использует те же методы, что и предыдущая стратегия. Подробнее .
Balansiranje radnog opterećenja — uravnotežiti obremenitev med računalniškimi vozlišči. Cilj ima tri strategije: Strategija migracije obremenitve, Stabilizacija obremenitve, Strategija ravnotežja kapacitet shrambe.
Strategija migracije obremenitve začenja migracije virtualnih strojev na podlagi obremenitve virtualnih strojev vozlišč. Odločitev o premiku se sprejme vsakič, ko % uporabe CPU ali RAM-a na vozlišču preseže določeno mejo. Premikajoči se virtualni stroj mora približati povprečni obremenitvi vseh vozlišč.
Zahteve
- Uporaba fizičnih procesorjev;
- Najmanj dva fizična računalniška vozlišča;
- Nameščen in konfiguriran komponent Ceilometer — ceilometer-agent-compute, ki deluje na vsakem računalniškem vozlišču, in Ceilometer API, ter zbiranje naslednjih metrik:
метрика
служба
vtičnike
комментарий
cpu_util
none
memory.resident
none
Parametri strategije:
parametar
tip
po defaultu
opis
metrics
Niz
'cpu_util'
Metrike, ki jih temelji: 'cpu_util', 'memory.resident'.
threshold
Число
25.0
Meja obremenitve za migracijo.
period
Число
300
Skupno obdobje časa Ceilometer.
Uporabljen postopek — migracija.
Stabilizacija obremenitve — strategija, usmerjena v stabilizacijo obremenitve z uporabo žive migracije. Strategija temelji na algoritmu standardnega odklona in določa, ali obstaja preobremenitev v gruči, ter na to reagira z začetkom migracije strojev za stabilizacijo gruče.
Zahteve
- Uporaba fizičnih procesorjev;
- Najmanj dva fizična računalniška vozlišča;
- Nameščen in konfiguriran komponent Ceilometer — ceilometer-agent-compute, ki deluje na vsakem računalniškem vozlišču, in Ceilometer API, ter zbiranje naslednjih metrik:
метрика
служба
vtičnike
комментарий
cpu_util
none
memory.resident
none
Strategija ravnotežja kapacitet shrambe (strategija implementirana od Queens) — strategija premika diske glede na obremenitev Cinder bazenov. Odločitev o premiku se sprejme vsakič, ko koeficient uporabe bazena preseže določeno mejo. Premikajoči se disk mora približati povprečni obremenitvi vseh Cinder bazenov.
Zahteve in omejitve
- Najmanj dva Cinder bazena;
- Možnost migracije diskov.
- Model podatkov gručne — model zbiranja podatkov Cinder.
Parametri strategije:
parametar
tip
po defaultu
opis
volume_threshold
Число
80.0
Mejna vrednost diskov za ravnotežje obsega.
Uporabljen postopek — migracija diskov (volume_migrate).
Noisy Neighbor — identificirati i premjestiti "bučnog susjeda" — virtualnu mašinu s niskim prioritetom koja negativno utiče na performanse virtualne mašine s visokim prioritetom u smislu IPC-a, prekomjerno koristeći Last Level Cache. Vlastita strategija: Noisy Neighbor (korišteni parametar strategije — cache_threshold (podrazumijevana vrijednost — 35), kada performanse padnu na navedenu vrijednost, pokreće se migracija. Za rad strategije potrebne su uključene LLC (Last Level Cache) metrike, posljednji Intel server s podrškom za CMT, kao i prikupljanje sljedećih metrika:
метрика
служба
vtičnike
комментарий
cpu_l3_cache
none
Potrebna je Intel .
Model podataka klastera (podrazumijevano): Nova cluster data model collector. Primijenjena metoda — migracija.
Rad na ovoj cilju putem Dashboarda nije u potpunosti implementiran u Queens.
Termalna optimizacija — optimizirati temperaturni režim. Temperatura na izlazu (izduvni zrak) jedna je od važnih toplinskih telemetrijskih sistema za mjerenje stanja toplinskog / radnog opterećenja servera. Za cilj postoji jedna strategija — Outlet temperature based strategy, koja donosi odluke o premještanju radnih opterećenja na čvorove s povoljnim temperaturnim režimom (najniža temperatura na izlazu), kada temperatura na izlazu izvornih hostova dostigne podesivi prag.
Za rad strategije potreban je server s instaliranim i podešenim Intel Power Node Manager , kao i prikupljanje sljedećih metrika:
метрика
служба
vtičnike
комментарий
hardware.ipmi.node.outlet_temperature
IPMI
Parametri strategije:
parametar
tip
po defaultu
opis
threshold
Число
35.0
Temperaturni prag za migraciju.
period
Число
30
Interval vremena u sekundama za dobivanje statističke agregacije iz izvora podataka metrika.
Uporabljen postopek — migracija.
Optimizacija protoka vazduha — optimizirati režim ventilacije. Vlastita strategija — Uniform Airflow using live migration. Strategija pokreće migraciju virtualne mašine svaki put kada zračni tok od ventilatora servera premaši navedeni prag.
Za rad strategije potrebni su:
- Hardver: računalni čvorovi <s podrškom za NodeManager 3.0;
- Minimalno dva računalna čvora;
- Instaliran i podešen na svakom računalnom čvoru komponenta ceilometer-agent-compute i Ceilometer API, koji može uspješno izvještavati o takvim metrikama kao što su protok zraka, snaga sistema, temperatura na ulazu:
метрика
служба
vtičnike
комментарий
hardware.ipmi.node.airflow
IPMI
hardware.ipmi.node.temperature
IPMI
hardware.ipmi.node.power
IPMI
Za delovanje strategije je potreben strežnik z nameščenim in konfiguriranim Intel Power Node Manager 3.0 ali novejšo različico.
Omejitve: Koncept ni namenjen za produkcijsko rabo.
Priporoča se, da se ta algoritem uporablja z neprekinjenimi revizijami, saj se v enem krogu predvideva migracija le ene virtuallne naprave.
Možne so žive migracije.
Parametri strategije:
parametar
tip
po defaultu
opis
threshold_airflow
Число
400.0
Prag pretoka zraka za migracijo enota je 0.1 CFM.
threshold_inlet_t
Число
28.0
Prag temperature vhodnega zraka za odločitev o migraciji.
threshold_power
Число
350.0
Prag moči sistema za odločitev o migraciji.
period
Число
30
Interval vremena u sekundama za dobivanje statističke agregacije iz izvora podataka metrika.
Uporabljen postopek — migracija.
Vzdrževanje strojne opreme — vzdrževanje strojne opreme. Strategija, povezana s tem ciljem, je migracija po conah. Strategija je orodje za učinkovito avtomatizirano in minimalno migracijo virtualnih naprav in diskov v primeru potrebe po tehničnem vzdrževanju strojne opreme. Strategija oblikuje načrt ukrepov v skladu z utežmi: niz ukrepov, ki ima večjo utež, bo načrtnjen prej od drugih. Obstajata dva konfiguracijska parametra: uteži ukrepov (action_weights) in vzporednost (parallelization).
Omejitve: potrebna je nastavitev uteži ukrepov in vzporednosti.
Parametri strategije:
parametar
tip
po defaultu
opis
compute_nodes
array
Noben
Računalniški vozli za migracijo.
storage_pools
array
Noben
Vozli za shranjevanje za migracijo.
parallel_total
število
6
Skupno število ukrepov, ki jih je treba izvajati hkrati.
parallel_per_node
število
2
Število ukrepov, ki se izvajajo hkrati za vsak računalniški vozel.
parallel_per_pool
število
2
Število ukrepov, ki se izvajajo hkrati za vsak skladiščni bazen.
priority
objekt
Noben
Seznam prioritet za virtualne naprave in diske.
with_attached_volume
boolean
False
False — virtualne naprave bodo prenesene po prenosu vseh diskov. True — virtualne naprave bodo prenesene po migraciji vseh povezanih diskov.
Elementi tabele računalniških vozlov:
parametar
tip
po defaultu
opis
src_node
string
Noben
Računalniški vozel, s katerega se prenašajo virtualne naprave (obvezno).
dst_node
string
Noben
Računalniški vozel, na katerega se migrirajo virtualne naprave.
Elementi tabele skladiščnih vozlov:
parametar
tip
po defaultu
opis
src_pool
string
Noben
Skladiščni bazen, iz katerega se prenašajo diski (obvezno).
dst_pool
string
Noben
Skladiščni bazen, na katerega se prenašajo diski.
src_type
string
Noben
Izvorni tip diska (obvezno).
dst_type
string
Noben
Končni tip diska (obvezno).
Elementi prioritetnosti objektov:
parametar
tip
po defaultu
opis
project
array
Noben
Imena projektov.
compute_node
array
Noben
Imena računalniških vozlov.
storage_pool
array
Noben
Imena skladiščnih bazenov.
compute
enum
Noben
Parametri virtualne mašine [“vcpu_num”, “mem_size”, “disk_size”, “created_at”].
pohrana
enum
Noben
Parametri diskova [“size”, “created_at”].
Korišćene metode — migracija virtualnih mašina, migracija diskova.
Neklasifikovano — pomoćni cilj, korišćen za olakšavanje procesa razvijanja strategije. Ne sadrži specifikacije i može se koristiti svaki put kada strategija još nije povezana sa postojećim ciljem. Ovaj cilj se takođe može koristiti kao prelazna faza. Strategija povezana s ovim ciljem je Actuator.
Kreiranje novog cilja
Watcher Decision Engine ima interfejs dodatka “spoljni cilj”, koji omogućava integraciju spoljnog cilja koji se može postići pomoću strategije.
Pre nego što kreirate novi cilj, trebalo bi da se uverite da nijedan od postojećih ciljeva ne odgovara vašim potrebama.
Kreiranje novog dodatka
Da biste kreirali novi cilj, morate: proširiti klasu cilja, implementirati metodu klase get_name () da biste vratili jedinstveni identifikator novog cilja koji želite da kreirate. Ovaj jedinstveni identifikator mora se poklapati sa imenom ulazne tačke koju kasnije deklarisate.
Zatim je potrebno implementirati metodu klase get_display_name () da biste vratili prevedeno ime cilja koji želite da kreirate (ne koristite promenljivu za vraćanje prevedenog stringa, kako bi se ona mogla automatski prikupiti alatkom za prevođenje.).
Implementirajte metodu klase get_translatable_display_name (), da biste vratili ključ prevođenja (u stvari, englesko prikazano ime) vašeg novog cilja. Vraćena vrednost mora se poklapati sa stringom, prevedenim u get_display_name ().
Implementirajte njegovu metodu get_efficacy_specification (), da biste vratili specifikaciju efikasnosti za vaš cilj. Metoda get_efficacy_specification () vraća instancu Unclassified (), koju obezbeđuje Watcher. Ova specifikacija efikasnosti je korisna tokom razvoja vašeg cilja, jer odgovara praznoj specifikaciji.
→
Arhitektura Watcher (više ).

Komponente

Watcher API — komponenta koja implementira REST API koji pruža Watcher. Mehanizmi interakcije: CLI, dodatak Horizon, Python SDK.
Watcher DB — baza podataka Watcher.
Watcher Applier — komponenta koja implementira izvršenje plana akcija, koji je kreiran komponentom Watcher Decision Engine.
Watcher Decision Engine — komponent, ki je odgovoren za izračun niza potencialnih dejanj za optimizacijo za dosego cilja revizije. Če strategija ni navedena, komponent samostojno izbere najustreznejšo.
Objavitelj metrik Watcher — komponent, ki zbira in izračunava nekatere metrike ali dogodke ter jih objavi na končni točki CEP. Funkcionalnost komponente lahko zagotovi tudi Ceilometer publisher.
Motor kompleksne obdelave dogodkov (CEP) — motor za kompleksno obdelavo dogodkov. Zaradi razlogov učinkovitosti je lahko več vzorcev CEP Engine, ki delujejo hkrati, pri čemer vsak obdeluje določeno vrsto metrike / dogodkov. V sistemu Watcher CEP izvaja dve vrsti dejanj: — zapisovanje ustreznih dogodkov / metrik v bazo podatkov časovnih vrst; — pošiljanje ustreznih dogodkov v komponento Watcher Decision Engine, kadar lahko ta dogodek vpliva na izid trenutne strategije optimizacije, saj klaster OpenStack ni statičen sistem.
Interakcija komponent poteka prek protokola AMQP.
→
Shema interakcije z Watcher

Rezultati testiranja Watcher
- Na strani Optimizacija — Akcijski načrti se prikaže napaka 500 (tako na čistem Queens kot na testni postaji s moduli Tionix), prikaže se šele po zagonu revizije in generaciji akcijskega načrta, prazna se odpre normalno.
- Na zavihku Podrobnosti dejanj se prikažejo napake, cilj in strategija revizije se ne moreta pridobiti (tako na čistem Queens kot na testni postaji s moduli Tionix).
- Revizije s ciljem Dummy (testne) se uspešno ustvarjajo in izvajajo, ustvarjajo se akcijski načrti.
- Revizije z neklasificiranim ciljem se ne ustvarjajo, ker cilj ni funkcionalen in je namenjen vmesni nastavitvi pri ustvarjanju novih strategij.
- Revizije z namenom Uravnoteženje delovne obremenitve (strategija Ravnotežje zmogljivosti shranjevanja) se uspešno ustvarjajo, vendar se akcijski načrt ne generira. Optimizacija shramb se ne zahteva.
- Revizije z namenom Uravnoteženje delovne obremenitve (strategija Migracija ravnotežja delovne obremenitve) se uspešno ustvarjajo, vendar se akcijski načrt ne generira.
- Revizije z namenom Uravnoteženje delovne obremenitve (strategija Stabilizacija delovne obremenitve) se končajo z napako.
- Revizije z namenom Noisy Neighbor se uspešno ustvarjajo, vendar se akcijski načrt ne generira.
- Auditi za namenom vzdrževanja strojne opreme se uspešno ustvarjajo, načrt ukrepanja pa se ne generira v celoti (generirajo se kazalniki učinkovitosti, vendar se dejanski seznam ukrepov ne generira).
- Popravki v nastavitvah nova.conf (v privzeti sekciji compute_monitors = cpu.virt_driver) na računalniških in upravljalnih vozliščih ne odpravijo napak.
- Auditi za namenom konsolidacije strežnikov (strategija Basic) se prav tako zaključijo z napako.
- Auditi za namenom konsolidacije strežnikov (strategija VM workload consolidation) se zaključijo z napako. V dnevnikih je napaka pri pridobivanju izvornih podatkov. Razprava o napaki, zlasti .
Poskusili smo določiti v konfiguracijski datoteki Watcher (ni pomagalo — posledica so napake na vseh straneh Optimization, vrnitev na izvorno vsebino konfiguracijske datoteke ne reši situacije):[watcher_strategies.basic]
datasource = ceilometer, gnocchi - Auditi za namenom varčevanja z energijo se zaključijo z napako. Sodeč po dnevnikih, je težava vseeno v pomanjkanju Ironic, brez storitve baremetal ne bo delovalo.
- Auditi za namenom toplotne optimizacije se zaključijo z napako. Sledi enaka napaka kot pri konsolidaciji strežnikov (strategija VM workload consolidation) (napaka pri izvornih podatkih)
- Auditi za namenom optimizacije pretoka zraka se zaključijo z napako.
Pojavljajo se tudi naslednje napake pri zaključevanju audita. Sledi v dnevnikih decision-engine.log (stanje gruče ni določeno).
→ Razprava o napaki
Zaključek
Rezultat naših dveh mesecev raziskav je jasen sklep, da bomo za pridobitev povsem delujočega sistema za uravnoteženje obremenitve morali v tem delu neposredno delati na razvoju orodij za platformo Openstack.
Watcher se je izkazal za resen in hitro razvijajoč se izdelek z ogromnim potencialom, za njegovo polno uporabo pa bo potrebnega veliko resnega dela.
O tem pa v naslednjih člankih cikla.
Izvor: habr.com
