Apache Bigtop i odabir Hadoop distribucije danas

Apache Bigtop i odabir Hadoop distribucije danas

Verovatno nije tajna da je prošla godina bila godina velikih promena za Apache Hadoop. Prošle godine su se spojili Cloudera i Hortonworks (u suštini, akvizicija potonjeg), a Mapr je, zbog ozbiljnih finansijskih problema, prodan Hewlett Packardu. I ako se nekoliko godina ranije, u slučaju lokalnih instalacija, često moralo birati između Cloudere i Hortonworksa, danas, nažalost, nemamo taj izbor. Još jedno iznenađenje je činjenica da je Cloudera u februaru ove godine najavila da će prestati puštati binarne sklopove svoje distribucije u javno spremište, a oni su sada dostupni samo putem plaćene pretplate. Naravno, još uvijek je moguće preuzeti najnovije verzije CDH i HDP objavljene prije kraja 2019. godine, a podrška za njih se očekuje za jednu do dvije godine. Ali šta dalje? Za one koji su prethodno plaćali pretplatu, ništa se nije promijenilo. A za one koji ne žele da pređu na plaćenu verziju distribucije, ali u isto vreme žele da mogu da primaju najnovije verzije komponenti klastera, kao i zakrpe i druga ažuriranja, pripremili smo ovaj članak. U njemu ćemo razmotriti moguće opcije za izlazak iz ove situacije.

Članak je više pregled. Neće sadržavati poređenje distribucija i njihovu detaljnu analizu, a neće biti ni recepata za njihovu instalaciju i konfiguraciju. Šta će se desiti? Ukratko ćemo govoriti o takvoj distribuciji kao što je Arenadata Hadoop, koja s pravom zaslužuje našu pažnju zbog svoje dostupnosti, što je danas vrlo rijetko. A onda ćemo pričati o Vanilla Hadoop-u, uglavnom o tome kako se može “skuvati” koristeći Apache Bigtop. Spreman? Onda dobrodošli u mačku.

Arenadata Hadoop

Apache Bigtop i odabir Hadoop distribucije danas

Ovo je potpuno nov i za sada malo poznat distributivni komplet domaćeg razvoja. Nažalost, trenutno na Habréu postoji samo Ovaj članak.

Više informacija možete pronaći na službenoj stranici site projekat. Najnovije verzije distribucije su bazirane na Hadoop-u 3.1.2 za verziju 3 i 2.8.5 za verziju 2.

Informacije o mapi puta možete pronaći ovdje.

Apache Bigtop i odabir Hadoop distribucije danas
Arenadata Cluster Manager Interface

Arenadata-in osnovni proizvod je Arenadata Cluster Manager (ADCM), koji se koristi za instalaciju, konfiguraciju i praćenje različitih softverskih rješenja kompanije. ADCM se distribuira besplatno, a njegova funkcionalnost je proširena dodavanjem paketa, koji su skup ansible-playbook-a. Paketi su podijeljeni u dva tipa: poduzeća i zajednica. Potonji su dostupni za besplatno preuzimanje sa Arenadata web stranice. Također je moguće razviti vlastiti paket i povezati ga na ADCM.

Za implementaciju i upravljanje Hadoop-om 3, zajednička verzija paketa se nudi u kombinaciji sa ADCM-om, ali za Hadoop 2 postoji samo Apache Ambari kao alternativu. Što se tiče spremišta sa paketima, ona su otvorena za javni pristup, mogu se preuzeti i instalirati na uobičajen način za sve komponente klastera. Sve u svemu, distribucija izgleda vrlo zanimljivo. Siguran sam da će biti onih koji su navikli na rješenja kao što su Cloudera Manager i Ambari, a kojima će se svidjeti i sam ADCM. Za neke će to biti i veliki plus što distribucija uključeni u registar softvera za zamjenu uvoza.

Ako govorimo o nedostacima, oni će biti isti kao i za sve ostale Hadoop distribucije. naime:

  • Takozvano „zaključavanje dobavljača“. Koristeći primjere Cloudera i Hortonworksa, već smo shvatili da uvijek postoji rizik promjene politike kompanije.
  • Značajno zaostajanje za Apache uzvodno.

Vanilla Hadoop

Apache Bigtop i odabir Hadoop distribucije danas

Kao što znate, Hadoop nije monolitan proizvod, već, u stvari, čitava galaksija usluga oko svog distribuiranog sistema datoteka HDFS. Malo ljudi će imati dovoljan jedan klaster datoteka. Nekima je potreban Hive, drugima Presto, a tu su i HBase i Phoenix; Spark se sve više koristi. Za orkestraciju i učitavanje podataka ponekad se nalaze Oozie, Sqoop i Flume. A ako se pojavi pitanje sigurnosti, onda Kerberos u sprezi sa Rangerom odmah pada na pamet.

Binarne verzije Hadoop komponenti dostupne su na web stranici svakog projekta ekosistema u obliku arhivskih datoteka. Možete ih preuzeti i započeti instalaciju, ali uz jedan uvjet: osim samostalnog sastavljanja paketa iz „sirovih“ binarnih datoteka, što najvjerovatnije želite učiniti, nećete imati povjerenja u kompatibilnost preuzetih verzija komponenti sa svakom ostalo. Preferirana opcija je pravljenje koristeći Apache Bigtop. Bigtop će vam omogućiti da gradite iz Apache maven spremišta, pokrećete testove i pravite pakete. Ali, ono što je za nas veoma važno, Bigtop će sastaviti one verzije komponenti koje će biti međusobno kompatibilne. O tome ćemo detaljnije govoriti u nastavku.

Apache Bigtop

Apache Bigtop i odabir Hadoop distribucije danas

Apache Bigtop je alat za pravljenje, pakovanje i testiranje brojnih
open source projekti, kao što su Hadoop i Greenplum. Bigtop ima dosta
izdanja. U vrijeme pisanja ovog teksta, najnovija stabilna verzija bila je verzija 1.4,
a u masteru je bilo 1.5. Različite verzije izdanja koriste različite verzije
komponente. Na primjer, za 1.4 Hadoop komponente jezgre imaju verziju 2.8.5 iu master
2.10.0. Sastav podržanih komponenti se također mijenja. Nešto zastarjelo i
neobnovljivo odlazi, a na njegovo mjesto dolazi nešto novo, traženije i
nije nužno nešto iz same porodice Apača.

Osim toga, Bigtop ima mnogo viljuške.

Kada smo počeli da se upoznajemo sa Bigtopom, pre svega smo bili iznenađeni njegovom skromnom, u poređenju sa drugim Apache projektima, rasprostranjenošću i popularnošću, kao i veoma malom zajednicom. Iz ovoga proizilazi da je informacija o proizvodu minimalna, a traženje rješenja za probleme koji su se pojavili na forumima i mailing listama možda neće dati ništa. Isprva nam se pokazalo težak zadatak da završimo kompletnu montažu distribucije zbog karakteristika samog alata, ali o tome ćemo malo kasnije.

Kao teaser, oni koji su u jednom trenutku bili zainteresovani za takve projekte Linux univerzuma kao što su Gentoo i LFS mogli bi smatrati da je nostalgično ugodno raditi s ovom stvari i prisjetiti se onih „epskih“ vremena kada smo i sami tražili (ili čak pisali) ebuild i redovno obnavlja Mozilla sa novim zakrpama.

Velika prednost Bigtopa je otvorenost i svestranost alata na kojima se zasniva. Baziran je na Gradle i Apache Maven. Gradle je prilično poznat kao alat koji Google koristi za izradu Androida. Fleksibilan je i, kako kažu, „testiran u borbi“. Maven je standardni alat za izgradnju projekata u samom Apacheu, a budući da se većina njegovih proizvoda izdaje preko Mavena, ni ovdje se ne bi moglo bez njega. Vrijedi obratiti pažnju na POM (projektni objektni model) - „osnovnu“ xml datoteku koja opisuje sve što je potrebno Mavenu za rad s vašim projektom, oko kojeg se gradi sav posao. Tačno u
dijelovima Maven-a i postoje neke prepreke s kojima se prvi Bigtop korisnici obično susreću.

Praksa

Dakle, odakle početi? Idite na stranicu za preuzimanje i preuzmite najnoviju stabilnu verziju kao arhivu. Tu možete pronaći i binarne artefakte koje je prikupio Bigtop. Inače, među uobičajenim menadžerima paketa podržani su YUM i APT.

Alternativno, možete direktno preuzeti najnovije stabilno izdanje
github:

$ git clone --branch branch-1.4 https://github.com/apache/bigtop.git

Kloniranje u “bigtop”…

remote: Enumerating objects: 46, done.
remote: Counting objects: 100% (46/46), done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 40217 (delta 14), reused 10 (delta 1), pack-reused 40171
Получение объектов: 100% (40217/40217), 43.54 MiB | 1.05 MiB/s, готово.
Определение изменений: 100% (20503/20503), готово.
Updating files: 100% (1998/1998), готово.

Rezultirajući direktorij ./bigtop izgleda otprilike ovako:

./bigtop-bigpetstore — demo aplikacije, sintetički primjeri
./bigtop-ci - CI alati, Jenkins
./bigtop-data-generators — generiranje podataka, sintetika, za ispitivanje dima, itd.
./bigtop-deploy - alati za implementaciju
./bigtop-packages — konfiguracije, skripte, zakrpe za montažu, glavni dio alata
./bigtop-test-framework — okvir za testiranje
./bigtop-tests — sami testovi, opterećenje i dim
./bigtop_toolchain — okruženje za sklapanje, priprema okoline za rad alata
./build — izgraditi radni direktorij
./dl — direktorij za preuzete izvore
./docker — ugradnja u docker slike, testiranje
./gradle - gradle konfiguracija
./output – direktorij u koji idu artefakti izgradnje
./provisioner — opskrba

Najzanimljivija stvar za nas u ovoj fazi je glavna konfiguracija ./bigtop/bigtop.bom, u kojem vidimo sve podržane komponente s verzijama. Ovdje možemo navesti drugu verziju proizvoda (ako odjednom poželimo pokušati da ga napravimo) ili verziju za izgradnju (ako smo, na primjer, dodali značajnu zakrpu).

Poddirektorij je također od velikog interesa ./bigtop/bigtop-packages, koji je direktno povezan sa procesom sklapanja komponenti i paketa sa njima.

Dakle, preuzeli smo arhivu, raspakovali je ili napravili klon sa github-a, možemo li početi da gradimo?

Ne, hajde da prvo pripremimo okruženje.

Priprema okoline

I ovdje nam treba malo povlačenje. Da biste napravili gotovo svaki manje-više složen proizvod, potrebno vam je određeno okruženje - u našem slučaju, to je JDK, iste zajedničke biblioteke, zaglavlja itd., alati, na primjer, ant, ivy2 i još mnogo toga. Jedna od opcija da dobijete okruženje koje vam je potrebno za Bigtop je instaliranje potrebnih komponenti na host za izgradnju. Možda grešim u hronologiji, ali izgleda da je sa verzijom 1.0 postojala i opcija za ugradnju unapred konfigurisanih i dostupnih Docker slika, koje se mogu naći ovde.

Što se tiče pripreme okoline, za to postoji pomoćnik - Lutka.

Možete koristiti sljedeće naredbe, pokrenute iz korijenskog direktorija
alat, ./bigtop:

./gradlew toolchain
./gradlew toolchain-devtools
./gradlew toolchain-puppetmodules

Ili direktno preko lutke:

puppet apply --modulepath=<path_to_bigtop> -e "include bigtop_toolchain::installer"
puppet apply --modulepath=<path_to_bigtop> -e "include bigtop_toolchain::deployment-tools"
puppet apply --modulepath=<path_to_bigtop> -e "include bigtop_toolchain::development-tools"

Nažalost, već u ovoj fazi mogu nastati poteškoće. Opšti savjet ovdje je da koristite podržanu distribuciju, ažurnu na hostu build-a, ili isprobate docker rutu.

Montaža

Šta možemo pokušati prikupiti? Odgovor na ovo pitanje će dati izlaz naredbe

./gradlew tasks

U odjeljku zadaci paketa nalazi se niz proizvoda koji su konačni artefakti Bigtopa.
Mogu se identifikovati sufiksom -rpm ili -pkg-ind (u slučaju izgradnje
u dockeru). U našem slučaju, najzanimljiviji je Hadoop.

Pokušajmo izgraditi u okruženju našeg build servera:

./gradlew hadoop-rpm

Bigtop će sam preuzeti potrebne izvore potrebne za određenu komponentu i započeti sastavljanje. Dakle, rad alata zavisi od Maven repozitorijuma i drugih izvora, odnosno zahteva pristup Internetu.

Tokom rada, generira se standardni izlaz. Ponekad vam to i poruke o greškama mogu pomoći da shvatite šta je pošlo po zlu. A ponekad trebate dobiti dodatne informacije. U ovom slučaju vrijedi dodati argumente --info ili --debug, a može biti i korisno –stacktrace. Postoji pogodan način za generiranje skupa podataka za naknadni pristup mailing listama, ključ --scan.

Uz njegovu pomoć, bigtop će prikupiti sve informacije i staviti ih u gradle, nakon čega će dati link,
prateći koje će kompetentna osoba moći razumjeti zašto je skupština propala.
Imajte na umu da ova opcija može otkriti informacije koje ne želite, kao što su korisnička imena, čvorovi, varijable okruženja, itd., stoga budite oprezni.

Često su greške posljedica nemogućnosti nabavke komponenti potrebnih za montažu. Tipično, možete riješiti problem kreiranjem zakrpe da popravite nešto u izvorima, na primjer, adrese u pom.xml u korijenskom direktoriju izvora. To se postiže kreiranjem i stavljanjem u odgovarajući direktorij ./bigtop/bigtop-packages/src/common/oozie/ zakrpa, na primjer, u obliku patch2-fix.diff.

--- a/pom.xml
+++ b/pom.xml
@@ -136,7 +136,7 @@
<repositories>
<repository>
<id>central</id>
- <url>http://repo1.maven.org/maven2</url>
+ <url>https://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>

Najvjerovatnije, u vrijeme čitanja ovog članka, nećete morati sami da radite gore navedene popravke.

Prilikom uvođenja bilo kakvih zakrpa i promjena u mehanizam sklopa, možda ćete morati "resetirati" sklop pomoću naredbe za čišćenje:

./gradlew hadoop-clean
> Task :hadoop_vardefines
> Task :hadoop-clean
BUILD SUCCESSFUL in 5s
2 actionable tasks: 2 executed

Ova operacija će poništiti sve promjene u sklopu ove komponente, nakon čega će se sastavljanje ponovo izvršiti. Ovaj put ćemo pokušati izgraditi projekat u docker slici:

./gradlew -POS=centos-7 -Pprefix=1.2.1 hadoop-pkg-ind
> Task :hadoop-pkg-ind
Building 1.2.1 hadoop-pkg on centos-7 in Docker...
+++ dirname ./bigtop-ci/build.sh
++ cd ./bigtop-ci/..
++ pwd
+ BIGTOP_HOME=/tmp/bigtop
+ '[' 6 -eq 0 ']'
+ [[ 6 -gt 0 ]]
+ key=--prefix
+ case $key in
+ PREFIX=1.2.1
+ shift
+ shift
+ [[ 4 -gt 0 ]]
+ key=--os
+ case $key in
+ OS=centos-7
+ shift
+ shift
+ [[ 2 -gt 0 ]]
+ key=--target
+ case $key in
+ TARGET=hadoop-pkg
+ shift
+ shift
+ [[ 0 -gt 0 ]]
+ '[' -z x ']'
+ '[' -z x ']'
+ '[' '' == true ']'
+ IMAGE_NAME=bigtop/slaves:1.2.1-centos-7
++ uname -m
+ ARCH=x86_64
+ '[' x86_64 '!=' x86_64 ']'
++ docker run -d bigtop/slaves:1.2.1-centos-7 /sbin/init
+
CONTAINER_ID=0ce5ac5ca955b822a3e6c5eb3f477f0a152cd27d5487680f77e33fbe66b5bed8
+ trap 'docker rm -f
0ce5ac5ca955b822a3e6c5eb3f477f0a152cd27d5487680f77e33fbe66b5bed8' EXIT
....
много вывода
....
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-yarn-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-mapreduce-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-namenode-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-secondarynamenode-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-zkfc-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-journalnode-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-datanode-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-httpfs-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-yarn-resourcemanager-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-yarn-nodemanager-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-yarn-proxyserver-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-yarn-timelineserver-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-mapreduce-historyserver-2.8.5-
1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-client-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-conf-pseudo-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-doc-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-libhdfs-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-libhdfs-devel-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-fuse-2.8.5-1.el7.x86_64.rpm
Wrote: /bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-debuginfo-2.8.5-1.el7.x86_64.rpm
+ umask 022
+ cd /bigtop/build/hadoop/rpm//BUILD
+ cd hadoop-2.8.5-src
+ /usr/bin/rm -rf /bigtop/build/hadoop/rpm/BUILDROOT/hadoop-2.8.5-1.el7.x86_64
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.uQ2FCn
+ exit 0
+ umask 022
Executing(--clean): /bin/sh -e /var/tmp/rpm-tmp.CwDb22
+ cd /bigtop/build/hadoop/rpm//BUILD
+ rm -rf hadoop-2.8.5-src
+ exit 0
[ant:touch] Creating /bigtop/build/hadoop/.rpm
:hadoop-rpm (Thread[Task worker for ':',5,main]) completed. Took 38 mins 1.151 secs.
:hadoop-pkg (Thread[Task worker for ':',5,main]) started.
> Task :hadoop-pkg
Task ':hadoop-pkg' is not up-to-date because:
Task has not declared any outputs despite executing actions.
:hadoop-pkg (Thread[Task worker for ':',5,main]) completed. Took 0.0 secs.
BUILD SUCCESSFUL in 40m 37s
6 actionable tasks: 6 executed
+ RESULT=0
+ mkdir -p output
+ docker cp
ac46014fd9501bdc86b6c67d08789fbdc6ee46a2645550ff6b6712f7d02ffebb:/bigtop/build .
+ docker cp
ac46014fd9501bdc86b6c67d08789fbdc6ee46a2645550ff6b6712f7d02ffebb:/bigtop/output .
+ docker rm -f ac46014fd9501bdc86b6c67d08789fbdc6ee46a2645550ff6b6712f7d02ffebb
ac46014fd9501bdc86b6c67d08789fbdc6ee46a2645550ff6b6712f7d02ffebb
+ '[' 0 -ne 0 ']'
+ docker rm -f ac46014fd9501bdc86b6c67d08789fbdc6ee46a2645550ff6b6712f7d02ffebb
Error: No such container:
ac46014fd9501bdc86b6c67d08789fbdc6ee46a2645550ff6b6712f7d02ffebb
BUILD SUCCESSFUL in 41m 24s
1 actionable task: 1 executed

Izrada je izvedena pod CentOS-om, ali se može uraditi i pod Ubuntuom:

./gradlew -POS=ubuntu-16.04 -Pprefix=1.2.1 hadoop-pkg-ind

Pored pravljenja paketa za različite Linux distribucije, alat može kreirati spremište sa prevedenim paketima, na primjer:

./gradlew yum

Također se možete sjetiti o dimnim testovima i implementaciji u Dockeru.

Kreirajte klaster od tri čvora:

./gradlew -Pnum_instances=3 docker-provisioner

Pokrenite testove dima u grupi od tri čvora:

./gradlew -Pnum_instances=3 -Prun_smoke_tests docker-provisioner

Izbrišite klaster:

./gradlew docker-provisioner-destroy

Dobijte komande za povezivanje unutar docker kontejnera:

./gradlew docker-provisioner-ssh

Prikaži status:

./gradlew docker-provisioner-status

Više o zadacima implementacije možete pročitati u dokumentaciji.

Ako govorimo o testovima, ima ih prilično velik broj, uglavnom dim i integracija. Njihova analiza je izvan okvira ovog članka. Samo da kažem da sklapanje distributivnog kompleta nije tako težak zadatak kao što se na prvi pogled čini. Uspjeli smo sastaviti i proći testove na svim komponentama koje koristimo u našoj proizvodnji, a također nismo imali problema s njihovom implementacijom i izvođenjem osnovnih operacija u test okruženju.

Pored postojećih komponenti u Bigtopu, moguće je dodati bilo šta drugo, čak i sopstveni razvoj softvera. Sve je to savršeno automatizirano i uklapa se u CI/CD koncept.

zaključak

Očigledno, ovako sastavljenu distribuciju ne treba odmah slati u proizvodnju. Morate shvatiti da ako postoji stvarna potreba za izgradnjom i podrškom vaše distribucije, onda morate uložiti novac i vrijeme u to.

Međutim, u kombinaciji s pravim pristupom i stručnim timom, sasvim je moguće bez komercijalnih rješenja.

Važno je napomenuti da je samom projektu Bigtop potreban razvoj i čini se da se danas ne razvija aktivno. Izgledi da se Hadoop 3 pojavi u njemu također su nejasni. Usput, ako imate stvarnu potrebu da napravite Hadoop 3, možete pogledati viljuška iz Arenadata, u kojoj pored standardnih
Postoji niz dodatnih komponenti (Ranger, Knox, NiFi).

Što se tiče Rostelecoma, za nas je Bigtop jedna od opcija koja se danas razmatra. Hoćemo li to izabrati ili ne, vrijeme će pokazati.

dodatak

Da biste uključili novu komponentu u sklop, potrebno je da dodate njen opis u bigtop.bom i ./bigtop-packages. Možete pokušati to učiniti analogijom s postojećim komponentama. Pokušajte to shvatiti. Nije tako teško kao što se čini na prvi pogled.

Šta ti misliš? Bit će nam drago vidjeti vaše mišljenje u komentarima i hvala vam na pažnji!

Članak je pripremio tim za upravljanje podacima Rostelecoma

izvor: www.habr.com

Dodajte komentar