Ką mes žinome apie mikropaslaugas

Sveiki! Mano vardas Vadimas Madisonas, vadovauju Avito sistemos platformos kūrimui. Jau ne kartą kalbėta, kaip mes įmonėje pereiname nuo monolitinės architektūros prie mikropaslaugų. Atėjo laikas pasidalyti, kaip pertvarkėme savo infrastruktūrą, kad išnaudotume visas mikropaslaugų galimybes ir nepasiklystume jose. Kaip PaaS mums čia padeda, kaip supaprastinome diegimą ir sumažinome mikropaslaugos sukūrimą iki vieno paspaudimo – skaitykite toliau. Ne viskas, apie ką rašau toliau, yra visiškai įdiegta „Avito“, kai kurie iš jų yra tai, kaip mes kuriame savo platformą.

(Ir šio straipsnio pabaigoje pakalbėsiu apie galimybę dalyvauti trijų dienų seminare, kurį rengia mikro paslaugų architektūros ekspertas Chrisas Richardsonas).

Ką mes žinome apie mikropaslaugas

Kaip mes priėjome prie mikropaslaugų

Avito yra viena didžiausių įslaptintų svetainių pasaulyje, joje per dieną paskelbiama daugiau nei 15 milijonų naujų skelbimų. Mūsų backend priima daugiau nei 20 tūkstančių užklausų per sekundę. Šiuo metu turime kelis šimtus mikro paslaugų.

Jau keletą metų kuriame mikro paslaugų architektūrą. Kaip tiksliai – mūsų kolegos išsamiai pasakojo mūsų skyriuje RIT++ 2017. „CodeFest 2017“ (žr. видео), Sergejus Orlovas ir Michailas Prokopčukas išsamiai paaiškino, kodėl mums reikėjo perėjimo prie mikropaslaugų ir kokį vaidmenį čia atliko Kubernetes. Na, dabar darome viską, kad sumažintume tokiai architektūrai būdingas mastelio keitimo išlaidas.

Iš pradžių nesukūrėme ekosistemos, kuri visapusiškai padėtų mums kurti ir paleisti mikropaslaugas. Jie tiesiog surinko protingus atvirojo kodo sprendimus, paleido juos namuose ir pakvietė kūrėją su jais susidoroti. Dėl to jis nuėjo į keliolika vietų (prietaisų skydeliai, vidaus paslaugos), po to sustiprėjo noras iškirpti kodą senu būdu, monolitu. Žemiau pateiktose diagramose žalia spalva rodo, ką kūrėjas vienaip ar kitaip daro savo rankomis, o geltona – automatizavimą.

Ką mes žinome apie mikropaslaugas

Dabar „PaaS CLI“ programoje nauja paslauga sukuriama naudojant vieną komandą, o nauja duomenų bazė pridedama dar dviem ir diegiama „Stage“.

Ką mes žinome apie mikropaslaugas

Kaip įveikti „mikro paslaugų susiskaidymo“ erą

Dėl monolitinės architektūros, siekdami produkto pokyčių nuoseklumo, kūrėjai buvo priversti išsiaiškinti, kas vyksta su kaimynais. Dirbant su nauja architektūra, paslaugų kontekstai nebepriklauso vienas nuo kito.

Be to, norint, kad mikro paslaugų architektūra būtų veiksminga, reikia sukurti daugybę procesų, būtent:

• medienos ruoša;
• prašyti sekimo (Jaeger);
• klaidų agregavimas (Sentry);
• būsenos, pranešimai, įvykiai iš Kubernetes (Event Stream Processing);
• lenktynių limitas / grandinės pertraukiklis (galite naudoti Hystrix);
• paslaugų ryšio valdymas (naudojame Netramesh);
• stebėjimas (Grafana);
• surinkimas (TeamCity);
• komunikacija ir informavimas (Slack, el. paštas);
• užduočių sekimas; (Jira)
• dokumentacijos rengimas.

Siekdami užtikrinti, kad sistema neprarastų vientisumo ir išliktų efektyvi plečiant mastelį, pergalvojome mikropaslaugų organizavimą Avito.

Kaip mes valdome mikropaslaugas

Tai padeda įgyvendinti vieningą „partijos politiką“ tarp daugelio „Avito“ mikropaslaugų:

  • infrastruktūros padalijimas į sluoksnius;
  • Platformos kaip paslaugos (PaaS) koncepcija;
  • stebėti viską, kas vyksta su mikropaslaugomis.

Infrastruktūros abstrakcijos sluoksnius sudaro trys sluoksniai. Eikime iš viršaus į apačią.

A. Viršus – aptarnavimo tinklelis. Iš pradžių bandėme Istio, bet pasirodė, kad jis naudoja per daug resursų, o tai per brangu mūsų apimtims. Todėl architektūros komandos vyresnysis inžinierius Aleksandras Lukjančenka sukūrė savo sprendimą - Netramešas (yra atvirojo kodo), kurį šiuo metu naudojame gamyboje ir kuris sunaudoja kelis kartus mažiau resursų nei Istio (bet nedaro visko, kuo gali pasigirti Istio).
B. Vidutinė – Kubernetes. Jame diegiame ir valdome mikro paslaugas.
C. Dugnas – plikas metalas. Mes nenaudojame debesų ar dalykų, tokių kaip „OpenStack“, bet visiškai pasikliaujame plika metalu.

Visi sluoksniai yra sujungti PaaS. O ši platforma savo ruožtu susideda iš trijų dalių.

I. Generatoriai, valdomas naudojant CLI programą. Būtent ji padeda kūrėjui tinkamai ir įdedant minimalias pastangas sukurti mikropaslaugą.

II. Konsoliduotas kolektorius su visų įrankių valdymu per bendrą prietaisų skydelį.

III. Sandėliavimas. Susisiekia su planuotojais, kurie automatiškai nustato svarbių veiksmų aktyviklius. Tokios sistemos dėka nepraleidžiama nei viena užduotis vien dėl to, kad kažkas pamiršo nustatyti užduotį Jira. Tam naudojame vidinį įrankį, vadinamą Atlas.

Ką mes žinome apie mikropaslaugas

Mikropaslaugų diegimas „Avito“ taip pat vykdomas pagal vieną schemą, kuri supaprastina jų kontrolę kiekviename kūrimo ir išleidimo etape.

Kaip veikia standartinis mikro paslaugų kūrimo vamzdynas?

Apskritai mikro paslaugų kūrimo grandinė atrodo taip:

CLI-push → Nuolatinis integravimas → Kepimas → Diegimas → Dirbtiniai testai → Kanarų testai → Suspaudimo testavimas → Gamyba → Priežiūra.

Pereikime tai tiksliai tokia tvarka.

CLI-push

• Mikropaslaugos sukūrimas.
Mes ilgai stengėmės išmokyti kiekvieną kūrėją atlikti mikro paslaugas. Tai apėmė išsamių instrukcijų rašymą „Confluence“. Tačiau schemos pasikeitė ir buvo papildytos. Rezultatas – kelionės pradžioje atsirado kliūtis: mikropaslaugoms paleisti prireikė daug daugiau laiko, o jas kuriant dažnai iškildavo problemų.

Galų gale sukūrėme paprastą CLI priemonę, kuri automatizuoja pagrindinius veiksmus kuriant mikro paslaugą. Tiesą sakant, jis pakeičia pirmąjį „git“ paspaudimą. Štai ką ji tiksliai daro.

— Sukuria paslaugą pagal šabloną – žingsnis po žingsnio, „vedlio“ režimu. Turime šablonus pagrindinėms programavimo kalboms Avito backend: PHP, Golang ir Python.

- Viena komanda vienu metu diegia vietinio kūrimo aplinką konkrečiame įrenginyje - paleidžiamas „Minikube“, automatiškai generuojamos ir paleidžiamos „Helm“ diagramos vietinėse „kubernetes“.

— Sujungia reikiamą duomenų bazę. Kūrėjas neturi žinoti IP, prisijungimo vardo ir slaptažodžio, kad galėtų pasiekti jam reikalingą duomenų bazę – ar tai būtų vietoje, „Stage“ ar gamyboje. Be to, duomenų bazė nedelsiant įdiegiama gedimams atsparia konfigūracija ir subalansuota.

— Ji pati atlieka tiesioginį surinkimą. Tarkime, kūrėjas kažką pataisė mikroservise per savo IDE. Priemonė mato failų sistemos pakeitimus ir, remdamasi jais, atkuria programą (skirta Golang) ir paleidžia iš naujo. PHP tiesiog persiunčiame katalogą kubo viduje ir ten „automatiškai“ gaunamas tiesioginis perkrovimas.

- Generuoja automatinius testus. Ruošinių pavidalu, bet gana tinkamas naudoti.

• Mikro paslaugų diegimas.

Mikropaslaugos diegimas mums buvo šiek tiek sunkus darbas. Reikėjo šių dalykų:

I. Dockerfile.

II. konfig.
III. Vairo diagrama, kuri pati yra sudėtinga ir apima:

— pačios diagramos;
- šablonai;
— konkrečios vertės, atsižvelgiant į skirtingas aplinkas.

Atsikratėme „Kubernetes“ aprašų pertvarkymo, todėl dabar jie generuojami automatiškai. Tačiau svarbiausia, kad jie iki galo supaprastino diegimą. Nuo šiol turime Dockerfile, o kūrėjas įrašo visą konfigūraciją į vieną trumpą app.toml failą.

Ką mes žinome apie mikropaslaugas

Taip, ir pačiame app.toml nėra ką veikti minutę. Nurodome, kur ir kiek paslaugos kopijų kelti (dev serveryje, sustojime, gamyboje), nurodome jos priklausomybes. Atkreipkite dėmesį į eilutės dydį = "mažas" [variklio] bloke. Tai yra riba, kuri bus skirta paslaugai per „Kubernetes“.

Tada, remiantis konfigūracija, automatiškai sugeneruojamos visos reikalingos Helm diagramos ir sukuriami ryšiai su duomenų bazėmis.

• Pagrindinis patvirtinimas. Tokie patikrinimai taip pat yra automatizuoti.
Reikia sekti:
— ar yra Dockerfile;
— ar yra app.toml;
– ar yra dokumentų?
- ar priklausomybė tvarkinga?
— ar buvo nustatytos įspėjimo taisyklės.
Prie paskutinio taško: paslaugos savininkas pats nustato, kurią produkto metriką stebėti.

• Dokumentacijos ruošimas.
Vis dar probleminė sritis. Tai atrodo akivaizdžiausia, bet kartu tai ir rekordas „dažnai pamirštamas“, taigi ir pažeidžiama grandinės grandis.
Būtina turėti kiekvienos mikropaslaugos dokumentaciją. Jį sudaro šie blokai.

I. Trumpas paslaugos aprašymas. Žodžiu, keli sakiniai apie tai, ką tai daro ir kodėl to reikia.

II. Architektūros diagramos nuoroda. Svarbu, kad greitai pažvelgus į jį būtų lengva suprasti, pavyzdžiui, ar Redis naudojate talpykloje saugoti, ar kaip pagrindinę duomenų saugyklą nuolatiniu režimu. „Avito“ kol kas tai yra nuoroda į „Confluence“.

III. Runbook. Trumpas vadovas, kaip pradėti paslaugą ir jos tvarkymo subtilybes.

IV. DUK, kur būtų gerai numatyti problemas, su kuriomis gali susidurti jūsų kolegos dirbdami su tarnyba.

V. API galutinių taškų aprašymas. Jei staiga nenurodėte krypčių, kolegos, kurių mikropaslaugos yra susijusios su jūsų teikiamomis paslaugomis, beveik neabejotinai už tai sumokės. Dabar tam naudojame Swagger ir mūsų sprendimą, vadinamą trumpu.

VI. Etiketės. Arba žymekliai, rodantys, kuriam produktui, funkcionalumui ar įmonės struktūriniam padaliniui priklauso paslauga. Jie padeda greitai suprasti, pavyzdžiui, ar nenaudojate funkcijų, kurias prieš savaitę kolegos įdiegė tam pačiam verslo padaliniui.

VII. Paslaugos savininkas arba savininkai. Daugeliu atvejų jį (arba juos) galima nustatyti automatiškai naudojant PaaS, tačiau norėdami būti saugūs, reikalaujame, kad kūrėjas juos nurodytų rankiniu būdu.

Galiausiai, gera praktika yra peržiūrėti dokumentus, panašiai kaip kodo peržiūra.

Nuolatinė integracija

  • Saugyklų paruošimas.
  • Dujotiekio sukūrimas TeamCity.
  • Teisių nustatymas.
  • Ieškokite paslaugų savininkų. Čia yra hibridinė schema - rankinis žymėjimas ir minimali automatika iš PaaS. Visiškai automatinė schema sugenda, kai paslaugos perduodamos palaikyti kitai kūrimo komandai arba, pavyzdžiui, paslaugų kūrėjas pasitraukia.
  • Paslaugos registravimas Atlas (pažiūrėkite aukščiau). Su visais savininkais ir priklausomybėmis.
  • Migracijų tikrinimas. Mes patikriname, ar kuris nors iš jų yra potencialiai pavojingas. Pavyzdžiui, viename iš jų pasirodo pakeitimų lentelė ar kažkas kita, galinti sutrikdyti skirtingų paslaugos versijų duomenų schemos suderinamumą. Tada migracija ne atliekama, o įdedama į prenumeratą – PaaS turi signalizuoti paslaugos savininkui, kada saugu ja naudotis.

Kepkite

Kitas etapas – pakavimo paslaugos prieš diegimą.

  • Programos kūrimas. Pagal klasiką – Docker įvaizdyje.
  • Pačios paslaugos ir susijusių išteklių vairo diagramų generavimas. Įskaitant duomenų bazes ir talpyklą. Jie sukuriami automatiškai pagal app.toml konfigūraciją, kuri buvo sugeneruota CLI stūmimo etape.
  • Bilietus, skirtus administratoriams atidaryti prievadus, kūrimas (kai reikia).
  • Vienetų testų vykdymas ir kodo aprėpties skaičiavimas. Jei kodo aprėptis yra mažesnė už nurodytą slenkstį, greičiausiai paslauga nesieks toliau - į diegimą. Jei jis yra ant priimtinos ribos, paslaugai bus priskirtas „pesimizuojantis“ koeficientas: tada, jei rodiklis laikui bėgant nepagerės, kūrėjas gaus pranešimą, kad bandymų pažanga nėra ( ir su tuo reikia ką nors padaryti).
  • Atsižvelgiama į atminties ir procesoriaus apribojimus. Mes daugiausia rašome mikro paslaugas Golang ir vykdome jas Kubernetes. Taigi vienas subtilumas, susijęs su Golang kalbos ypatumu: pagal numatytuosius nustatymus paleidžiant naudojami visi mašinos branduoliai, jei aiškiai nenustatote GOMAXPROCS kintamojo, o kai tame pačiame kompiuteryje paleidžiamos kelios tokios paslaugos, jos prasideda konkuruoti dėl resursų, trukdant vieni kitiems. Toliau pateiktose diagramose parodyta, kaip keičiasi vykdymo laikas, jei programą paleisite be ginčų ir lenktynių dėl išteklių režimu. (Grafų šaltiniai yra čia).

Ką mes žinome apie mikropaslaugas

Vykdymo laikas, mažiau yra geriau. Maksimalus: 643 ms, minimalus: 42 ms. Nuotrauką galima spustelėti.

Ką mes žinome apie mikropaslaugas

Laikas operacijai, mažiau yra geriau. Didžiausia: 14091 ns, mažiausia: 151 ns. Nuotrauką galima spustelėti.

Surinkimo paruošimo etape galite aiškiai nustatyti šį kintamąjį arba galite naudoti biblioteką automaxpros iš vaikinų iš Uber.

Dislokuoti

• Tikrinimo sutartys. Prieš pradėdami tiekti paslaugų komplektus į numatytą aplinką, turite patikrinti šiuos dalykus:
- API galutiniai taškai.
— API galinių taškų atsakymų atitiktis schemai.
— žurnalo formatas.
- Paslaugos užklausų antraščių nustatymas (šiuo metu tai atlieka netramesh)
— Savininko prieigos rakto nustatymas siunčiant pranešimus į įvykių magistralę. Tai reikalinga norint stebėti paslaugų ryšį autobuse. Į magistralę galite siųsti tiek idempotentus duomenis, kurie nepadidina paslaugų jungiamumo (o tai yra gerai), tiek verslo duomenis, kurie stiprina paslaugų ryšį (o tai labai blogai!). Ir tuo metu, kai šis ryšys tampa problema, supratimas, kas rašo ir skaito autobusą, padeda tinkamai atskirti paslaugas.

Avito mieste dar nėra labai daug suvažiavimų, tačiau jų baseinas plečiasi. Kuo daugiau tokių susitarimų komandai suprantama ir suprantama forma, tuo lengviau išlaikyti mikropaslaugų nuoseklumą.

Sintetiniai testai

• Uždarojo ciklo testavimas. Šiuo metu mes naudojame atvirąjį kodą Hoverfly.io. Pirma, jis įrašo tikrąją paslaugos apkrovą, tada – tiesiog uždarame cikle – imituoja ją.

• Testavimas nepalankiausiomis sąlygomis. Stengiamės, kad visos paslaugos veiktų optimaliai. Ir visoms kiekvienos paslaugos versijoms turi būti taikomas apkrovos testavimas – taip galime suprasti esamą paslaugos našumą ir skirtumą nuo ankstesnių tos pačios paslaugos versijų. Jei po paslaugos atnaujinimo jo našumas sumažėjo pusantro karto, tai yra aiškus signalas jos savininkams: reikia įsigilinti į kodą ir ištaisyti situaciją.
Surinktus duomenis naudojame, pavyzdžiui, norėdami teisingai įdiegti automatinį mastelio keitimą ir galiausiai suprasti, kaip keičiamasi paslauga.

Apkrovos testavimo metu patikriname, ar išteklių suvartojimas atitinka nustatytas ribas. O mes pirmiausia orientuojamės į kraštutinumus.

a) Mes žiūrime į bendrą apkrovą.
– Per mažas – greičiausiai kažkas visai neveikia, jei apkrova staiga kelis kartus nukrenta.
– Per didelis – reikalingas optimizavimas.

b) Mes žiūrime į ribą pagal RPS.
Čia žiūrime į skirtumą tarp dabartinės versijos ir ankstesnės bei bendro kiekio. Pavyzdžiui, jei paslauga gamina 100 rps, tada ji arba blogai parašyta, arba tai yra jos specifika, bet bet kuriuo atveju tai yra priežastis labai atidžiai žiūrėti į paslaugą.
Jei, priešingai, yra per daug RPS, galbūt yra kokia nors klaida ir kai kurie galiniai taškai nustojo vykdyti naudingą apkrovą, o kai kurie kiti tiesiog suveikia return true;

Kanarų testai

Išlaikę sintetinius testus, mikropaslaugą išbandome su nedideliu vartotojų skaičiumi. Pradedame atsargiai, turėdami nedidelę paslaugos tikslinės auditorijos dalį – mažiau nei 0,1%. Šiame etape labai svarbu, kad į stebėjimą būtų įtrauktos teisingos techninės ir produkto metrikos, kad jos kuo greičiau parodytų serviso problemą. Minimalus kanarėlių testo laikas yra 5 minutės, pagrindinis - 2 valandos. Sudėtingoms paslaugoms laiką nustatome rankiniu būdu.
Išanalizuokime:
— kalbai būdingi metrikai, ypač php-fpm darbuotojai;
— Sentry klaidos;
— atsakymo būsenos;
— reakcijos laikas, tikslus ir vidutinis;
- latentinis laikotarpis;
— išimtys, apdorotos ir neapdorotos;
- produkto metrika.

Suspaudimo bandymas

Suspaudimo testavimas taip pat vadinamas „išspaudimo“ testavimu. Technikos pavadinimas buvo pristatytas „Netflix“. Jo esmė ta, kad pirmiausia vieną egzempliorių užpildome tikru srautu iki gedimo taško ir taip nustatome jo ribą. Tada pridedame kitą egzempliorių ir įkeliame šią porą - vėl maksimaliai; matome jų lubas ir deltą su pirmuoju „išspaudimu“. Taigi mes prijungiame po vieną egzempliorių ir apskaičiuojame pokyčių modelį.
Bandymų duomenys „suspaudimo“ būdu taip pat patenka į bendrą metrikų duomenų bazę, kur jais arba praturtiname dirbtinės apkrovos rezultatus, arba netgi jais pakeičiame „sintetiką“.

Gamyba

• Mastelio keitimas. Kai pristatome paslaugą į gamybą, stebime, kaip ji plečiasi. Mūsų patirtis rodo, kad stebėti tik procesoriaus rodiklius yra neveiksminga. Automatinis mastelio keitimas naudojant RPS lyginamąją analizę veikia gryna forma, tačiau tik tam tikroms paslaugoms, pvz., srautiniam perdavimui internetu. Taigi pirmiausia žiūrime į konkrečios programos produkto metriką.

Dėl to, didindami mastelį, analizuojame:
- CPU ir RAM indikatoriai,
— eilėje esančių užklausų skaičius,
- atsakymo laikas,
— prognozė, pagrįsta sukauptais istoriniais duomenimis.

Keičiant paslaugos mastelį, taip pat svarbu stebėti jos priklausomybes, kad nepadidintume pirmosios paslaugos grandinėje, o tos, kurias ji pasiekia, sugestų esant apkrovai. Norėdami nustatyti priimtiną apkrovą visam paslaugų telkiniui, žiūrime į „artimiausios“ priklausomos paslaugos istorinius duomenis (remiantis procesoriaus ir RAM rodiklių deriniu, kartu su konkrečios programos metrika) ir palyginame juos su istoriniais duomenimis. inicijavimo paslaugos ir tt visoje „priklausomybės grandinėje““ nuo viršaus iki apačios.

Обслуживание

Pradėjus eksploatuoti mikropaslaugą, prie jos galime pritvirtinti trigerius.

Čia pateikiamos tipiškos situacijos, kai atsiranda trigerių.
— Aptikta potencialiai pavojingų migracijų.
— Buvo išleisti saugos naujinimai.
— Pati paslauga ilgą laiką nebuvo atnaujinta.
— Paslaugos apkrova pastebimai sumažėjo arba kai kurie jos produkto rodikliai yra už įprasto diapazono ribų.
— Paslauga nebeatitinka naujos platformos reikalavimų.

Kai kurie paleidikliai yra atsakingi už veikimo stabilumą, kai kurie – kaip sistemos priežiūros funkcija – pavyzdžiui, kai kurios paslaugos nebuvo įdiegtos ilgą laiką, o jos pagrindinis vaizdas nebeatitinka saugos patikrų.

Prietaisų skydelis

Trumpai tariant, prietaisų skydelis yra viso mūsų PaaS valdymo skydelis.

  • Vienintelis informacijos apie paslaugą taškas su duomenimis apie jos testavimo aprėptį, jos vaizdų skaičių, gamybos kopijų skaičių, versijas ir kt.
  • Įrankis duomenims filtruoti pagal paslaugas ir etiketes (priklausomybės verslo padaliniams žymeklius, produkto funkcionalumą ir kt.)
  • Įrankis, skirtas integruoti su infrastruktūros įrankiais, skirtais sekti, registruoti ir stebėti.
  • Vieno punkto aptarnavimo dokumentacija.
  • Vienintelis visų paslaugų teikimo įvykių vaizdas.

Ką mes žinome apie mikropaslaugas
Ką mes žinome apie mikropaslaugas
Ką mes žinome apie mikropaslaugas
Ką mes žinome apie mikropaslaugas

Iš viso

Prieš pristatydamas „PaaS“, naujas kūrėjas gali praleisti kelias savaites, kad suprastų visus įrankius, reikalingus mikropaslaugoms paleisti gamyboje: „Kubernetes“, „Helm“, mūsų vidines „TeamCity“ funkcijas, duomenų bazių ir talpyklų jungčių nustatymą gedimams atspariu būdu ir kt. užtrunka kelias valandas, kol perskaitysite trumpą pradžią ir sukursite pačią paslaugą.

Pateikiau pranešimą šia tema „HighLoad++ 2018“, galite žiūrėti видео и pristatymas.

Papildomas takelis tiems, kurie perskaitė iki galo

Mes, Avito, organizuojame vidinius trijų dienų mokymus kūrėjams nuo Chrisas Richardsonas, mikro paslaugų architektūros ekspertas. Norėtume suteikti galimybę jame dalyvauti vienam iš šio įrašo skaitytojų. Čia Mokymų programa paskelbta.

Mokymai vyks rugpjūčio 5-7 dienomis Maskvoje. Tai darbo dienos, kurios bus visiškai užimtos. Pietūs ir mokymai vyks mūsų biure, o pasirinktas dalyvis apmokės už kelionę ir apgyvendinimą pats.

Galite kreiptis dėl dalyvavimo šioje google formoje. Iš jūsų – atsakymas į klausimą, kodėl reikia dalyvauti mokymuose ir informacija, kaip su jumis susisiekti. Atsakykite angliškai, nes Chrisas pats išsirinks dalyvį, kuris dalyvaus mokymuose.
Mokymų dalyvio vardą paskelbsime šio įrašo atnaujinime ir socialiniuose tinkluose Avito kūrėjams (AvitoTech in Фейсбуке, Vkontakte, Twitter) ne vėliau kaip liepos 19 d.

Šaltinis: www.habr.com

Добавить комментарий