Hvað vitum við um örþjónustur

Halló! Mitt nafn er Vadim Madison, ég stýri þróun Avito System Platforms. Það hefur verið sagt oftar en einu sinni hvernig við í fyrirtækinu erum að fara frá einhæfum arkitektúr yfir í smáþjónustu. Það er kominn tími til að deila því hvernig við höfum umbreytt innviðum okkar til að fá sem mest út úr örþjónustu og koma í veg fyrir að við týnumst í henni. Hvernig PaaS hjálpar okkur hér, hvernig við einfölduðum uppsetningu og minnkuðum stofnun örþjónustu í einn smell - lestu áfram. Ekki er allt sem ég skrifa um hér að neðan að fullu útfært í Avito, sumt af því er hvernig við þróum vettvang okkar.

(Og í lok þessarar greinar mun ég tala um tækifærið til að sækja þriggja daga málstofu frá arkitektúrsérfræðingi örþjónustu, Chris Richardson).

Hvað vitum við um örþjónustur

Hvernig við komum að örþjónustum

Avito er ein stærsta flokkuð síða í heimi, meira en 15 milljónir nýrra auglýsinga eru birtar á henni á dag. Bakendinn okkar tekur við meira en 20 þúsund beiðnum á sekúndu. Núna erum við með nokkur hundruð örþjónustur.

Við höfum verið að byggja upp örþjónustuarkitektúr í nokkur ár núna. Hvernig nákvæmlega - samstarfsmenn okkar í smáatriðum sagði á hluta okkar á RIT++ 2017. Á CodeFest 2017 (sjá. vídeó), Sergey Orlov og Mikhail Prokopchuk útskýrðu í smáatriðum hvers vegna við þurftum að skipta yfir í örþjónustur og hvaða hlutverki Kubernetes gegndi hér. Jæja, nú gerum við allt til að lágmarka stærðarkostnaðinn sem felst í slíkum arkitektúr.

Upphaflega bjuggum við ekki til vistkerfi sem myndi í heild sinni hjálpa okkur að þróa og opna örþjónustur. Þeir söfnuðu einfaldlega skynsamlegum opnum lausnum, settu þær á markað heima og buðu verktaki að takast á við þær. Fyrir vikið fór hann á tugi staða (mælaborð, innri þjónusta), eftir það varð hann sterkari í löngun sinni til að klippa kóða á gamla mátann, í einlitum. Græni liturinn á skýringarmyndum hér að neðan gefur til kynna hvað verktaki gerir á einn eða annan hátt með eigin höndum og guli liturinn gefur til kynna sjálfvirkni.

Hvað vitum við um örþjónustur

Nú í PaaS CLI tólinu er ný þjónusta búin til með einni skipun og nýjum gagnagrunni er bætt við með tveimur í viðbót og dreift á Stage.

Hvað vitum við um örþjónustur

Hvernig á að sigrast á tímum "örþjónustu sundrungar"

Með einhæfum arkitektúr, vegna samkvæmni breytinga á vörunni, neyddust verktaki til að reikna út hvað var að gerast hjá nágrönnum sínum. Þegar unnið er að nýjum arkitektúr er þjónustusamhengi ekki lengur háð hvert öðru.

Að auki, til að örþjónustuarkitektúr sé árangursríkur, þarf að koma á mörgum ferlum, þ.e.

• skógarhögg;
• biðja um rakningu (Jaeger);
• villusamsöfnun (Sentry);
• stöður, skilaboð, atburðir frá Kubernetes (vinnsla atburðarstraums);
• keppnismörk / aflrofi (þú getur notað Hystrix);
• stjórn á þjónustutengingu (við notum Netramesh);
• eftirlit (Grafana);
• samkoma (TeamCity);
• samskipti og tilkynningar (Slakk, tölvupóstur);
• verkefnamælingu; (Jira)
• gerð skjala.

Til að tryggja að kerfið glati ekki heilleika sínum og haldist virkt þegar það stækkar, endurhugsuðum við skipulag örþjónustu í Avito.

Hvernig við stjórnum örþjónustum

Eftirfarandi hjálpar til við að innleiða sameinaða „flokksstefnu“ meðal margra Avito örþjónustu:

  • skipta innviðum í lög;
  • Platform as a Service (PaaS) hugtak;
  • fylgjast með öllu sem gerist með örþjónustur.

Innviðauppdráttarlög innihalda þrjú lög. Förum frá toppi til botns.

A. Top - þjónustunet. Í fyrstu reyndum við Istio, en það kom í ljós að það notar of mikið úrræði, sem er of dýrt fyrir okkar magn. Þess vegna þróaði yfirverkfræðingur í arkitektúrteyminu Alexander Lukyanchenko sína eigin lausn - Netramesh (fáanlegt í Open Source), sem við notum nú í framleiðslu og eyðir margfalt minna fjármagni en Istio (en gerir ekki allt sem Istio getur státað af).
B. Miðlungs - Kubernetes. Við sendum inn og rekum örþjónustur á því.
C. Botn - ber málmur. Við notum ekki ský eða hluti eins og OpenStack, heldur treystum algjörlega á berum málmi.

Öll lög eru sameinuð af PaaS. Og þessi vettvangur, aftur á móti, samanstendur af þremur hlutum.

I. Rafala, stjórnað í gegnum CLI tól. Það er hún sem hjálpar verktaki að búa til örþjónustu á réttan hátt og með lágmarks fyrirhöfn.

II. Samþættur safnari með stjórn á öllum verkfærum í gegnum sameiginlegt mælaborð.

III. Geymsla. Tengist tímaáætlunarmönnum sem setja sjálfkrafa kveikjur fyrir verulegar aðgerðir. Þökk sé slíku kerfi er ekki eitt einasta verkefni saknað bara vegna þess að einhver gleymdi að setja upp verkefni í Jira. Við notum innra tól sem heitir Atlas fyrir þetta.

Hvað vitum við um örþjónustur

Innleiðing örþjónustu í Avito fer einnig fram samkvæmt einu kerfi, sem einfaldar stjórn á þeim á hverju stigi þróunar og útgáfu.

Hvernig virkar staðlað þróunarleiðsla fyrir smáþjónustu?

Almennt séð lítur smáþjónustukeðjan svona út:

CLI-push → Stöðug samþætting → Baka → Dreifa → Gerviprófanir → Kanarípróf → Kreistuprófun → Framleiðsla → Viðhald.

Við skulum fara í gegnum það nákvæmlega í þessari röð.

CLI-ýta

• Að búa til örþjónustu.
Við áttum í erfiðleikum í langan tíma við að kenna hverjum forritara hvernig á að gera smáþjónustu. Þetta innihélt að skrifa ítarlegar leiðbeiningar í Confluence. En kerfin breyttust og bættust við. Niðurstaðan er sú að flöskuháls kom upp í upphafi ferðar: það tók miklu lengri tíma að koma örþjónustum á markað og samt komu oft upp vandamál við gerð þeirra.

Að lokum byggðum við einfalt CLI tól sem gerir grunnskrefin sjálfvirk þegar búið er til örþjónustu. Reyndar kemur það í stað fyrstu git ýtunnar. Hér er nákvæmlega það sem hún gerir.

— Býr til þjónustu í samræmi við sniðmát — skref fyrir skref, í „töframaður“ ham. Við höfum sniðmát fyrir helstu forritunarmálin í Avito bakendanum: PHP, Golang og Python.

- Ein skipun í einu setur upp umhverfi fyrir staðbundna þróun á tiltekinni vél - Minikube er hleypt af stokkunum, Helm töflur eru sjálfkrafa búin til og ræst í staðbundnum kubernetes.

— Tengir nauðsynlegan gagnagrunn. Framkvæmdaraðilinn þarf ekki að vita IP, notandanafnið og lykilorðið til að fá aðgang að gagnagrunninum sem hann þarf - hvort sem það er á staðnum, á sviðinu eða í framleiðslu. Þar að auki er gagnagrunnurinn settur upp strax í bilunarþolinni uppsetningu og með jafnvægi.

— Það framkvæmir sjálft samsetningu í beinni. Segjum að verktaki hafi leiðrétt eitthvað í örþjónustu í gegnum IDE hans. Tækið sér breytingar á skráarkerfinu og, byggt á þeim, endurbyggir forritið (fyrir Golang) og endurræsir. Fyrir PHP sendum við einfaldlega möppuna inni í teningnum og þar fæst lifandi endurhleðsla „sjálfkrafa“.

— Myndar sjálfvirkar prófanir. Í formi blanks, en alveg hentugur til notkunar.

• Uppsetning örþjónustu.

Að dreifa örþjónustu var áður svolítið vandasamt fyrir okkur. Eftirfarandi var krafist:

I. Dockerfile.

II. Config.
III. Hjálmarkort, sem sjálft er fyrirferðarmikið og inniheldur:

— töflurnar sjálfar;
- sniðmát;
— sérstök gildi sem taka mið af mismunandi umhverfi.

Við höfum tekið sársaukann af því að endurvinna Kubernetes birtingarmyndir svo þær eru nú búnar til sjálfkrafa. En síðast en ekki síst, þeir einfaldaðu dreifinguna til hins ýtrasta. Héðan í frá höfum við Dockerfile og verktaki skrifar alla stillinguna í einni stuttri app.toml skrá.

Hvað vitum við um örþjónustur

Já, og í app.toml sjálfu er ekkert að gera í eina mínútu. Við tilgreinum hvar og hversu mörg eintök af þjónustunni á að safna (á þróunarþjóninum, við sviðsetningu, í framleiðslu) og tilgreinum ósjálfstæði hennar. Taktu eftir línustærð = "lítil" í [vél] blokkinni. Þetta er hámarkið sem verður úthlutað til þjónustunnar í gegnum Kubernetes.

Síðan, byggt á stillingunni, eru öll nauðsynleg Helm töflur sjálfkrafa búin til og tengingar við gagnagrunna eru búnar til.

• Grunnprófun. Slíkar athuganir eru einnig sjálfvirkar.
Þarftu að fylgjast með:
— er til Dockerfile;
— er þar app.toml;
— eru til gögn?
— er ósjálfstæði í lagi?
— hvort viðvörunarreglur hafi verið settar.
Að síðasta atriðinu: eigandi þjónustunnar ákveður sjálfur hvaða vörumælingar á að fylgjast með.

• Gerð skjala.
Enn vandamál svæði. Það virðist vera það augljósasta, en á sama tíma er það einnig met sem „gleymist oft“ og því viðkvæmur hlekkur í keðjunni.
Nauðsynlegt er að til séu skjöl fyrir hverja örþjónustu. Það inniheldur eftirfarandi blokkir.

I. Stutt lýsing á þjónustunni. Bókstaflega nokkrar setningar um hvað það gerir og hvers vegna það er þörf.

II. Tengill á arkitektúr skýringarmynd. Það er mikilvægt að með fljótu yfirliti sé auðvelt að skilja, til dæmis hvort þú ert að nota Redis fyrir skyndiminni eða sem aðalgagnageymsla í viðvarandi ham. Í Avito í bili er þetta hlekkur á Confluence.

III. Runbook. Stutt leiðarvísir um að hefja þjónustuna og flóknina við að meðhöndla hana.

IV. Algengar spurningar, þar sem gott væri að sjá fyrir vandamálin sem samstarfsfólk þitt gæti lent í þegar unnið er með þjónustuna.

V. Lýsing á endapunktum fyrir API. Ef þú skyndilega tilgreindir ekki áfangastaði, munu samstarfsmenn sem hafa örþjónustur tengdar þinni greiða fyrir það. Nú notum við Swagger og lausn okkar sem kallast brief fyrir þetta.

VI. Merki. Eða merkingar sem sýna hvaða vöru, virkni eða skipulagsdeild fyrirtækisins þjónustan tilheyrir. Þeir hjálpa þér að skilja fljótt, til dæmis, hvort þú sért að skera niður virkni sem samstarfsmenn þínir settu út fyrir sömu rekstrareiningu fyrir viku síðan.

VII. Eigandi eða eigendur þjónustunnar. Í flestum tilfellum er hægt að ákvarða það - eða þau - sjálfkrafa með PaaS, en til öryggis krefjumst við þess að verktaki tilgreini þau handvirkt.

Að lokum, það er góð venja að fara yfir skjöl, svipað og kóðunarskoðun.

Stöðug samþætting

  • Undirbúa geymslur.
  • Að búa til leiðslu í TeamCity.
  • Stillingarréttindi.
  • Leitaðu að þjónustueigendum. Það er blendingskerfi hér - handvirk merking og lágmarks sjálfvirkni frá PaaS. Alveg sjálfvirkt kerfi mistakast þegar þjónusta er flutt til annars þróunarteymi til stuðnings eða til dæmis ef þjónustuframleiðandinn hættir.
  • Skráning á þjónustu í Atlas (sjá fyrir ofan). Með öllum sínum eigendum og ósjálfstæðum.
  • Athugar flutninga. Við athugum hvort einhver þeirra sé hugsanlega hættuleg. Til dæmis, í einni þeirra birtist breytingtafla eða eitthvað annað sem getur rofið samhæfni gagnaskemmunnar á milli mismunandi útgáfur af þjónustunni. Þá er flutningurinn ekki framkvæmdur heldur settur í áskrift - PaaS verður að gefa þjónustueigandanum merki um hvenær óhætt sé að nota það.

Baka

Næsta stig er pökkunarþjónusta fyrir dreifingu.

  • Að byggja upp forritið. Samkvæmt klassíkinni - í Docker mynd.
  • Gerð Helm töflur fyrir þjónustuna sjálfa og tengd auðlindir. Þar á meðal fyrir gagnagrunna og skyndiminni. Þau eru búin til sjálfkrafa í samræmi við app.toml stillinguna sem var búin til á CLI-push stigi.
  • Að búa til miða fyrir stjórnendur til að opna höfn (þegar þess er krafist).
  • Keyra einingapróf og reikna út kóðaþekju. Ef kóðaþekjan er undir tilgreindum viðmiðunarmörkum, þá mun þjónustan líklegast ekki fara lengra - til dreifingar. Ef það er á mörkum þess að vera ásættanlegt, þá mun þjónustunni vera úthlutað „svartsýni“ stuðull: þá, ef engin framför er á vísinum með tímanum, mun verktaki fá tilkynningu um að engin framfarir séu hvað varðar prófanir ( og eitthvað þarf að gera í því).
  • Gert grein fyrir takmörkunum á minni og CPU. Við skrifum aðallega örþjónustur í Golang og keyrum þær í Kubernetes. Þess vegna er ein lúmska sem tengist sérkenni Golang tungumálsins: sjálfgefið, þegar ræst er, eru allir kjarna í vélinni notaðir, ef þú stillir ekki beinlínis GOMAXPROCS breytuna, og þegar nokkrar slíkar þjónustur eru ræstar á sömu vélinni byrja þær að keppa um auðlindir, trufla hvert annað. Gröfin hér að neðan sýna hvernig framkvæmdartíminn breytist ef þú keyrir forritið án ágreinings og í kapphlaupi um auðlindaham. (Heimildir grafa eru hér).

Hvað vitum við um örþjónustur

Framkvæmdartími, minna er betra. Hámark: 643ms, lágmark: 42ms. Myndin er smellanleg.

Hvað vitum við um örþjónustur

Tími fyrir aðgerð, minna er betra. Hámark: 14091 ns, lágmark: 151 ns. Myndin er smellanleg.

Á undirbúningsstigi samsetningar geturðu stillt þessa breytu sérstaklega eða þú getur notað bókasafnið automaxprocs frá strákunum frá Uber.

Dreifa

• Athugun á samþykktum. Áður en þú byrjar að afhenda þjónustusamstæður í fyrirhugað umhverfi þarftu að athuga eftirfarandi:
- API endapunktar.
— Samræmi við API endapunkta við skemað.
— Skráarsnið.
- Stilla hausa fyrir beiðnir til þjónustunnar (sem stendur er þetta gert af netramesh)
— Stilla eigandatákn þegar skilaboð eru send í viðburðarrútuna. Þetta er nauðsynlegt til að fylgjast með tengingu þjónustu yfir strætó. Bæði er hægt að senda ósjálfbjarga gögn í strætó, sem eykur ekki tengsl þjónustu (sem er gott), og viðskiptagögn sem styrkja tengsl þjónustu (sem er mjög slæmt!). Og á þeim tímapunkti þegar þessi tenging verður vandamál, hjálpar skilningur á því hver skrifar og les strætó að aðskilja þjónustuna á réttan hátt.

Það eru ekki mjög margar ráðstefnur í Avito ennþá, en sundlaugin þeirra er að stækka. Því fleiri slíkir samningar eru tiltækir á því formi að teymið getur skilið og skilið, því auðveldara er að viðhalda samræmi milli örþjónustu.

Tilbúið próf

• Lokað lykkja próf. Til þess erum við nú að nota opinn uppspretta Hoverfly.io. Í fyrsta lagi skráir það raunverulegt álag á þjónustuna, síðan - bara í lokaðri lykkju - líkir það eftir því.

• Álagspróf. Við reynum að koma allri þjónustu í sem bestan árangur. Og allar útgáfur hverrar þjónustu verða að vera háðar álagsprófun - þannig getum við skilið núverandi frammistöðu þjónustunnar og muninn á fyrri útgáfum af sömu þjónustu. Ef árangur hennar minnkaði um eitt og hálft skipti eftir þjónustuuppfærslu er þetta skýrt merki fyrir eigendur þess: þú þarft að grafa ofan í kóðann og leiðrétta ástandið.
Við notum söfnuð gögn, til dæmis, til að innleiða sjálfvirka mælingu á réttan hátt og á endanum skiljum við almennt hversu skalanleg þjónustan er.

Við álagsprófun athugum við hvort auðlindanotkun standist sett mörk. Og við einblínum fyrst og fremst á öfgar.

a) Við skoðum heildarálag.
- Of lítið - líklega virkar eitthvað ekki ef álagið lækkar skyndilega nokkrum sinnum.
- Of stór - hagræðingu krafist.

b) Við skoðum niðurskurð samkvæmt RPS.
Hér skoðum við muninn á núverandi útgáfu og þeirri fyrri og heildarmagninu. Til dæmis, ef þjónusta framleiðir 100 snúninga á sekúndu, þá er hún annað hvort illa skrifuð eða þetta er sérstaða hennar, en í öllum tilvikum er þetta ástæða til að skoða þjónustuna mjög vel.
Ef, þvert á móti, það eru of margir RPS, þá er kannski einhvers konar villa og sumir endapunktarnir hafa hætt að keyra hleðsluna og einhver annar er einfaldlega ræstur return true;

Kanarípróf

Eftir að við höfum staðist gerviprófin prófum við örþjónustuna á fáum notendum. Við byrjum varlega, með örlítinn hlut af ætluðum markhópi þjónustunnar - innan við 0,1%. Á þessu stigi er mjög mikilvægt að rétt tækni- og vörumæling sé tekin með í vöktuninni svo þau sýni vandann í þjónustunni eins fljótt og auðið er. Lágmarkstími fyrir kanarípróf er 5 mínútur, sá aðaltími er 2 klukkustundir. Fyrir flókna þjónustu stillum við tímann handvirkt.
Við skulum greina:
— málsértækar mælingar, einkum php-fpm starfsmenn;
— villur í Sentry;
— viðbragðsstöður;
— viðbragðstími, nákvæmur og meðaltal;
— leynd;
— undantekningar, unnar og ómeðhöndlaðar;
— vörumælingar.

Kreistuprófun

Kreistapróf er einnig kallað „kreista“ próf. Nafn tækninnar var kynnt á Netflix. Kjarni þess er sá að fyrst fyllum við eitt tilvik af raunverulegri umferð þar til það bilar og setjum þannig takmörk þess. Síðan bætum við öðru tilviki við og hleðum þessu pari - aftur upp í hámarkið; við sjáum loftið og delta með fyrstu „kreistu“. Og svo tengjum við eitt tilvik í einu og reiknum út mynstur breytinga.
Prófgögn með því að „kreista“ streyma einnig inn í algengan mælingagagnagrunn, þar sem við annað hvort auðgum gerviálagsniðurstöðurnar með þeim, eða jafnvel skiptum út „gerviefni“ fyrir þær.

Framleiðsla

• Stærð. Þegar við útfærum þjónustu til framleiðslu fylgjumst við með hvernig hún stækkar. Reynsla okkar er að eftirlit með örgjörvavísum er ekki árangursríkt. Sjálfvirk stigstærð með RPS viðmiðun í hreinu formi virkar, en aðeins fyrir ákveðna þjónustu, svo sem streymi á netinu. Svo við skoðum fyrst umsóknarsértæka vörumælingar.

Þar af leiðandi, þegar við mælikvarða, greinum við:
- Örgjörva og vinnsluminni vísbendingar,
— fjöldi beiðna í biðröð,
- viðbragðstími,
— spá byggð á uppsöfnuðum sögulegum gögnum.

Þegar þjónusta er stækkuð er einnig mikilvægt að fylgjast með ósjálfstæði hennar þannig að við stækkum ekki fyrstu þjónustuna í keðjunni og þær sem hún hefur aðgang að mistakast undir álagi. Til að koma á ásættanlegu álagi fyrir allan þjónustusafnið skoðum við söguleg gögn „næstu“ háðrar þjónustu (byggt á samsetningu örgjörva og vinnsluminni vísbendinga, ásamt forritssértækum mæligildum) og berum þau saman við söguleg gögn frumstillingarþjónustunnar og svo framvegis í gegnum „fíknikeðjuna“, frá toppi til botns.

Þjónusta

Eftir að örþjónustan er tekin í notkun getum við tengt kalla á hana.

Hér eru dæmigerðar aðstæður þar sem kveikjur eiga sér stað.
— Hugsanlega hættulegir fólksflutningar greindir.
— Öryggisuppfærslur hafa verið gefnar út.
— Þjónustan sjálf hefur ekki verið uppfærð í langan tíma.
— Álagið á þjónustuna hefur minnkað áberandi eða sumar vörumælingar hennar eru utan eðlilegra marka.
— Þjónustan uppfyllir ekki lengur nýju vettvangskröfurnar.

Sumir kveikjanna eru ábyrgir fyrir stöðugleika í rekstri, sumir - sem fall af viðhaldi kerfisins - til dæmis hefur einhver þjónusta ekki verið notuð í langan tíma og grunnmynd hennar hefur hætt að standast öryggiseftirlit.

Mælaborð

Í stuttu máli er mælaborðið stjórnborð alls PaaS okkar.

  • Einn upplýsingastaður um þjónustuna, með gögnum um prófunarumfang hennar, fjölda mynda, fjölda framleiðslueintaka, útgáfur o.s.frv.
  • Tól til að sía gögn eftir þjónustu og merkjum (merki um að tilheyra rekstrareiningum, virkni vöru osfrv.)
  • Tól til samþættingar við innviðaverkfæri til að rekja, skrá og fylgjast með.
  • Einn þjónustustaður.
  • Eitt sjónarhorn á alla atburði þvert á þjónustu.

Hvað vitum við um örþjónustur
Hvað vitum við um örþjónustur
Hvað vitum við um örþjónustur
Hvað vitum við um örþjónustur

Alls

Áður en PaaS er kynnt gæti nýr þróunaraðili eytt nokkrum vikum í að skilja öll þau verkfæri sem nauðsynleg eru til að koma örþjónustu af stað í framleiðslu: Kubernetes, Helm, innri TeamCity eiginleika okkar, uppsetningu tenginga við gagnagrunna og skyndiminni á bilanaþolinn hátt, o.s.frv. tekur nokkra klukkutíma að lesa skyndiræsingu og búa til þjónustuna sjálfa.

Ég gaf skýrslu um þetta efni fyrir HighLoad++ 2018, þú getur horft á hana vídeó и kynningu.

Bónus lag fyrir þá sem lesa til enda

Við hjá Avito erum að skipuleggja innri þriggja daga þjálfun fyrir forritara frá Chris Richardson, sérfræðingur í smáþjónustuarkitektúr. Okkur langar að gefa einum af lesendum þessarar færslu tækifæri til að taka þátt í henni. Hér Þjálfunaráætlunin hefur verið birt.

Þjálfunin fer fram dagana 5. til 7. ágúst í Moskvu. Þetta eru virkir dagar sem verða fulluppteknir. Hádegisverður og þjálfun verður á skrifstofu okkar og mun sá þátttakandi sem valinn er borga sjálfur fyrir ferðir og gistingu.

Hægt er að sækja um þátttöku í þessu google formi. Frá þér - svarið við spurningunni hvers vegna þú þarft að mæta á þjálfunina og upplýsingar um hvernig á að hafa samband við þig. Svaraðu á ensku, því Chris velur sjálfur þann þátttakanda sem mun mæta á þjálfunina.
Við munum tilkynna nafn þjálfunarþátttakandans í uppfærslu á þessari færslu og á samfélagsmiðlum Avito fyrir forritara (AvitoTech í Facebook, Vkontakte, Twitter) eigi síðar en 19. júlí.

Heimild: www.habr.com

Bæta við athugasemd