Kamusta! Ang pangalan ko ay Vadim Madison, pinangunahan ko ang pagbuo ng Avito System Platform. Nasabi nang higit sa isang beses kung paano kami sa kumpanya ay lumilipat mula sa monolitikong arkitektura patungo sa isang microservice. Oras na para ibahagi kung paano namin binago ang aming imprastraktura upang masulit ang mga microservice at maiwasan ang aming sarili na mawala sa kanila. Paano kami tinutulungan ng PaaS dito, kung paano namin pinasimple ang deployment at binawasan ang paggawa ng microservice sa isang click - basahin. Hindi lahat ng isinulat ko tungkol sa ibaba ay ganap na ipinatupad sa Avito, ang ilan sa mga ito ay kung paano namin binuo ang aming platform.
(At sa dulo ng artikulong ito, magsasalita ako tungkol sa pagkakataong dumalo sa isang tatlong araw na seminar mula sa dalubhasa sa arkitektura ng microservice na si Chris Richardson).
Ang Avito ay isa sa pinakamalaking classified na site sa mundo; higit sa 15 milyong bagong advertisement ang nai-publish dito bawat araw. Ang aming backend ay tumatanggap ng higit sa 20 libong mga kahilingan sa bawat segundo. Sa kasalukuyan, mayroon kaming ilang daang microservice.
Sa una, hindi kami lumikha ng isang ecosystem na komprehensibong makakatulong sa amin na bumuo at maglunsad ng mga microservice. Nangongolekta lang sila ng mga matinong solusyon sa open source, inilunsad ang mga ito sa bahay at inimbitahan ang developer na harapin ang mga ito. Bilang isang resulta, nagpunta siya sa isang dosenang mga lugar (dashboard, panloob na mga serbisyo), pagkatapos nito ay naging mas malakas siya sa kanyang pagnanais na i-cut ang code sa lumang paraan, sa isang monolith. Ang berdeng kulay sa mga diagram sa ibaba ay nagpapahiwatig kung ano ang ginagawa ng developer sa isang paraan o iba pa gamit ang kanyang sariling mga kamay, at ang dilaw na kulay ay nagpapahiwatig ng automation.
Ngayon sa PaaS CLI utility, ang isang bagong serbisyo ay nilikha gamit ang isang command, at isang bagong database ay idinagdag na may dalawa pa at na-deploy sa Stage.
Paano malalampasan ang panahon ng "pagkapira-piraso ng microservice"
Bilang karagdagan, para maging mabisa ang isang arkitektura ng microservice, maraming proseso ang kailangang itatag, lalo na:
β’ pagtotroso;
β’ humiling ng pagsubaybay (Jaeger);
β’ Π°Π³ΡΠ΅Π³Π°ΡΠΈΡ ΠΎΡΠΈΠ±ΠΎΠΊ (Sentry);
β’ mga katayuan, mensahe, kaganapan mula sa Kubernetes (Pagproseso ng Stream ng Kaganapan);
β’ race limit / circuit breaker (ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Hystrix);
β’ kontrol sa pagkakakonekta ng serbisyo (ginagamit namin ang Netramesh);
β’ pagsubaybay (Grafana);
β’ pagpupulong (TeamCity);
β’ komunikasyon at abiso (Slack, email);
β’ pagsubaybay sa gawain; (Jira)
β’ paghahanda ng dokumentasyon.
Upang matiyak na ang system ay hindi mawawala ang integridad nito at mananatiling epektibo habang ito ay sumusukat, muli naming inisip ang organisasyon ng mga microservice sa Avito.
A. Nangungunang - service mesh. Sa una sinubukan namin ang Istio, ngunit ito ay lumabas na gumagamit ito ng napakaraming mapagkukunan, na masyadong mahal para sa aming mga volume. Samakatuwid, ang senior engineer sa pangkat ng arkitektura na si Alexander Lukyanchenko ay bumuo ng kanyang sariling solusyon - Netramesh (magagamit sa Open Source), na kasalukuyang ginagamit namin sa produksyon at kumokonsumo ng ilang beses na mas kaunting mapagkukunan kaysa sa Istio (ngunit hindi ginagawa ang lahat ng maaaring ipagmalaki ni Istio). B. Daluyan - Kubernetes. Nag-deploy at nagpapatakbo kami ng mga microservice dito. C. Ibaba - hubad na metal. Hindi kami gumagamit ng mga ulap o mga bagay tulad ng OpenStack, ngunit ganap na umaasa sa bare metal.
Ang lahat ng mga layer ay pinagsama ng PaaS. At ang platform na ito, naman, ay binubuo ng tatlong bahagi.
I. Mga Generator, kinokontrol sa pamamagitan ng isang CLI utility. Siya ang tumutulong sa developer na lumikha ng isang microservice sa tamang paraan at may kaunting pagsisikap.
II. Pinagsama-samang kolektor na may kontrol sa lahat ng tool sa pamamagitan ng isang karaniwang dashboard.
III. Imbakan. Kumokonekta sa mga scheduler na awtomatikong nagtatakda ng mga trigger para sa mahahalagang pagkilos. Salamat sa ganoong sistema, wala ni isang gawain ang napalampas dahil lang sa may nakalimutang mag-set up ng gawain sa Jira. Gumagamit kami ng panloob na tool na tinatawag na Atlas para dito.
Ang pagpapatupad ng mga microservice sa Avito ay isinasagawa din ayon sa isang solong pamamaraan, na pinapasimple ang kontrol sa mga ito sa bawat yugto ng pag-unlad at pagpapalabas.
Paano gumagana ang isang karaniwang microservice development pipeline?
Sa pangkalahatan, ganito ang hitsura ng chain ng paglikha ng microservice:
Suriin natin ito nang eksakto sa ganitong pagkakasunud-sunod.
CLI-push
β’ Paglikha ng microservice.
Matagal kaming nahirapan para turuan ang bawat developer kung paano gumawa ng mga microservice. Kasama dito ang pagsulat ng mga detalyadong tagubilin sa Confluence. Ngunit ang mga scheme ay nagbago at dinagdagan. Ang resulta ay lumitaw ang isang bottleneck sa simula ng paglalakbay: tumagal ng mas maraming oras upang ilunsad ang mga microservice, at madalas pa ring lumitaw ang mga problema sa panahon ng kanilang paglikha.
Sa huli, bumuo kami ng simpleng CLI utility na nag-automate sa mga pangunahing hakbang kapag gumagawa ng microservice. Sa katunayan, pinapalitan nito ang unang git push. Narito kung ano ang eksaktong ginagawa niya.
β Lumilikha ng serbisyo ayon sa isang template β hakbang-hakbang, sa βwizardβ mode. Mayroon kaming mga template para sa mga pangunahing programming language sa Avito backend: PHP, Golang at Python.
- Isang utos sa isang pagkakataon ay naglalagay ng kapaligiran para sa lokal na pag-unlad sa isang partikular na makina - Ang Minikube ay inilunsad, ang mga Helm chart ay awtomatikong nabuo at inilulunsad sa mga lokal na kubernete.
β Nagsasagawa ito ng live na pagpupulong mismo. Sabihin nating may naitama ang isang developer sa isang microservice sa pamamagitan ng kanyang IDE. Nakikita ng utility ang mga pagbabago sa file system at, batay sa mga ito, muling itinatayo ang application (para sa Golang) at mag-restart. Para sa PHP, ipinapasa lang namin ang direktoryo sa loob ng cube at doon ay makukuha ang live-reload na "awtomatikong".
β Bumubuo ng mga autotest. Sa anyo ng mga blangko, ngunit medyo angkop para sa paggamit.
β’ Pag-deploy ng microservice.
Ang pag-deploy ng microservice dati ay medyo mahirap para sa amin. Ang mga sumusunod ay kinakailangan:
I. Dockerfile.
II. Config.
III. Helm chart, na kung saan ay napakahirap at kinabibilangan ng:
β ang mga tsart mismo;
- mga template;
β mga tiyak na halaga na isinasaalang-alang ang iba't ibang mga kapaligiran.
Inalis namin ang sakit sa muling paggawa ng mga manifest ng Kubernetes kaya awtomatiko na itong nabuo. Ngunit ang pinakamahalaga, pinasimple nila ang pag-deploy sa limitasyon. Mula ngayon mayroon na kaming Dockerfile, at isinusulat ng developer ang buong config sa isang solong maikling app.toml file.
Pagkatapos, batay sa config, ang lahat ng kinakailangang Helm chart ay awtomatikong nabubuo at ang mga koneksyon sa mga database ay nilikha.
β’ Pangunahing pagpapatunay. Ang mga naturang pagsusuri ay awtomatiko din. Kailangang subaybayan:
β Π΅ΡΡΡ Π»ΠΈ Dockerfile;
β mayroon bang app.toml;
β mayroon bang magagamit na dokumentasyon?
β maayos ba ang dependency?
β kung naitakda na ang mga alituntunin ng alerto.
Hanggang sa huling punto: ang may-ari ng serbisyo mismo ang nagpapasiya kung aling mga sukatan ng produkto ang susubaybayan.
β’ Paghahanda ng dokumentasyon.
Problema pa rin ang lugar. Tila ito ang pinaka-halata, ngunit sa parehong oras ito rin ay isang talaan na "madalas na nakalimutan", at samakatuwid ay isang mahina na link sa kadena.
Kinakailangan na mayroong dokumentasyon para sa bawat microservice. Kasama dito ang mga sumusunod na bloke.
I. Maikling paglalarawan ng serbisyo. Literal na ilang pangungusap tungkol sa kung ano ang ginagawa nito at kung bakit ito kinakailangan.
II. Link ng diagram ng arkitektura. ΠΠ°ΠΆΠ½ΠΎ, ΡΡΠΎΠ±Ρ ΠΏΡΠΈ Π±Π΅Π³Π»ΠΎΠΌ Π²Π·Π³Π»ΡΠ΄Π΅ Π½Π° Π½Π΅Ρ Π»Π΅Π³ΠΊΠΎ Π±ΡΠ»ΠΎ ΠΏΠΎΠ½ΡΡΡ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΠ΅ Π²Ρ Redis Π΄Π»Ρ ΠΊΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈΠ»ΠΈ ΠΊΠ°ΠΊ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠ΅ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅ Π΄Π°Π½Π½ΡΡ Π² ΠΏΠ΅ΡΡΠΈΡΡΠ΅Π½ΡΠ½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅. Π ΠΠ²ΠΈΡΠΎ ΠΏΠΎΠΊΠ° ΡΡΠΎ ΡΡΠΎ ΡΡΡΠ»ΠΊΠ° Π½Π° Confluence.
III. Runbook. Isang maikling gabay sa pagsisimula ng serbisyo at ang pagkasalimuot ng paghawak nito.
IV. FAQ, kung saan makabubuting asahan ang mga problemang maaaring makaharap ng iyong mga kasamahan kapag nagtatrabaho sa serbisyo.
V. Paglalarawan ng mga endpoint para sa API. Kung bigla mong hindi tinukoy ang mga destinasyon, halos tiyak na babayaran ito ng mga kasamahan na may kaugnayan sa iyo ang mga microservice. Ngayon ginagamit namin ang Swagger at ang aming solusyon na tinatawag na maikling para dito.
VI. Mga label. O mga marker na nagpapakita kung saang produkto, functionality, o structural division ng kumpanya kabilang ang serbisyo. Tinutulungan ka nilang mabilis na maunawaan, halimbawa, kung pinuputol mo ang functionality na inilunsad ng iyong mga kasamahan para sa parehong unit ng negosyo noong nakaraang linggo.
VII. May-ari o may-ari ng serbisyo. Sa karamihan ng mga kaso, ito β o sila β ay maaaring awtomatikong matukoy gamit ang PaaS, ngunit upang maging ligtas, hinihiling namin sa developer na tukuyin ang mga ito nang manu-mano.
Sa wakas, isang magandang kasanayan na suriin ang dokumentasyon, katulad ng pagsusuri ng code.
Patuloy na Pagsasama
Paghahanda ng mga repositoryo.
Paglikha ng pipeline sa TeamCity.
Pagtatakda ng mga karapatan.
Maghanap ng mga may-ari ng serbisyo. Mayroong hybrid scheme dito - manu-manong pagmamarka at minimal na automation mula sa PaaS. Nabigo ang isang ganap na awtomatikong scheme kapag inilipat ang mga serbisyo para sa suporta sa isa pang development team o, halimbawa, kung huminto ang developer ng serbisyo.
Pagrerehistro ng isang serbisyo sa Atlas (tingnan sa itaas). Sa lahat ng may-ari at dependency nito.
Sinusuri ang mga migrasyon. Sinusuri namin kung ang alinman sa mga ito ay potensyal na mapanganib. Halimbawa, sa isa sa mga ito ay may lalabas na alter table o iba pang bagay na maaaring masira ang compatibility ng data schema sa pagitan ng iba't ibang bersyon ng serbisyo. Pagkatapos ay hindi isinasagawa ang paglipat, ngunit inilagay sa isang subscription - dapat na senyales ng PaaS ang may-ari ng serbisyo kapag ligtas itong gamitin.
maghurno
Ang susunod na yugto ay ang mga serbisyo sa packaging bago ang pag-deploy.
Pagbuo ng application. Ayon sa mga klasiko - sa isang imahe ng Docker.
Pagbuo ng mga Helm chart para sa mismong serbisyo at mga kaugnay na mapagkukunan. Kabilang ang para sa mga database at cache. Awtomatikong nilikha ang mga ito alinsunod sa app.toml config na nabuo sa yugto ng CLI-push.
Paglikha ng mga tiket para sa mga admin na magbukas ng mga port (Kapag kailangan).
Pagpapatakbo ng mga pagsubok sa unit at pagkalkula ng saklaw ng code. Kung ang saklaw ng code ay mas mababa sa tinukoy na threshold, malamang na hindi na lalayo pa ang serbisyo - sa pag-deploy. Kung ito ay nasa gilid ng katanggap-tanggap, kung gayon ang serbisyo ay bibigyan ng isang "pessimizing" na koepisyent: kung gayon, kung walang pagpapabuti sa tagapagpahiwatig sa paglipas ng panahon, ang developer ay makakatanggap ng isang abiso na walang pag-unlad sa mga tuntunin ng mga pagsubok ( at may kailangang gawin tungkol dito).
Accounting para sa memorya at mga limitasyon ng CPU. Pangunahing nagsusulat kami ng mga microservice sa Golang at pinapatakbo ang mga ito sa Kubernetes. Kaya't isang subtlety na nauugnay sa kakaiba ng wikang Golang: bilang default, kapag nagsisimula, ang lahat ng mga core sa makina ay ginagamit, kung hindi mo tahasang itinakda ang variable ng GOMAXPROCS, at kapag ang ilang mga naturang serbisyo ay inilunsad sa parehong makina, magsisimula ang mga ito. upang makipagkumpetensya para sa mga mapagkukunan, nakakasagabal sa bawat isa. Ipinapakita ng mga graph sa ibaba kung paano nagbabago ang oras ng pagpapatupad kung patakbuhin mo ang application nang walang pagtatalo at sa karera para sa mode ng mapagkukunan. (Ang mga pinagmumulan ng mga graph ay dito).
Ang oras ng pagpapatupad, mas kaunti ay mas mahusay. Maximum: 643ms, minimum: 42ms. Naki-click ang larawan.
Oras para sa operasyon, mas kaunti ay mas mabuti. Maximum: 14091 ns, minimum: 151 ns. Naki-click ang larawan.
Sa yugto ng paghahanda ng pagpupulong, maaari mong itakda ang variable na ito nang tahasan o maaari mong gamitin ang library automaxprocs mula sa mga lalaki mula sa Uber.
I-deploy
β’ Pagsusuri ng mga kombensiyon. Bago ka magsimulang maghatid ng mga service assemblies sa iyong nilalayong kapaligiran, kailangan mong suriin ang sumusunod:
- Mga endpoint ng API.
β Pagsunod sa mga tugon ng mga endpoint ng API sa schema.
β Format ng log.
β Pagtatakda ng mga header para sa mga kahilingan sa serbisyo (kasalukuyang ginagawa ito ng netramesh)
β Pagtatakda ng token ng may-ari kapag nagpapadala ng mga mensahe sa bus ng kaganapan. Ito ay kinakailangan upang masubaybayan ang pagkakakonekta ng mga serbisyo sa buong bus. Maaari kang magpadala ng parehong idempotent na data sa bus, na hindi nagpapataas ng koneksyon ng mga serbisyo (na mabuti), at ng data ng negosyo na nagpapalakas sa koneksyon ng mga serbisyo (na napakasama!). At sa puntong nagiging isyu ang koneksyong ito, nakakatulong ang pag-unawa kung sino ang sumulat at nagbabasa ng bus upang maayos na paghiwalayin ang mga serbisyo.
Wala pang masyadong convention sa Avito, pero lumalawak ang pool nila. Kung mas maraming ganoong kasunduan ang available sa isang form na mauunawaan at mauunawaan ng team, mas madaling mapanatili ang pagkakapare-pareho sa pagitan ng mga microservice.
Mga sintetikong pagsubok
β’ Closed loop testing. Para dito gumagamit kami ngayon ng open source Hoverfly.io. Una, itinatala nito ang tunay na pagkarga sa serbisyo, pagkatapos - sa isang saradong loop - tinutularan ito.
β’ Stress Testing. Sinusubukan naming dalhin ang lahat ng mga serbisyo sa pinakamainam na pagganap. At ang lahat ng mga bersyon ng bawat serbisyo ay dapat sumailalim sa pagsubok sa pag-load - sa paraang ito ay mauunawaan natin ang kasalukuyang pagganap ng serbisyo at ang pagkakaiba sa mga nakaraang bersyon ng parehong serbisyo. Kung, pagkatapos ng isang pag-update ng serbisyo, ang pagganap nito ay bumaba ng isa at kalahating beses, ito ay isang malinaw na senyales para sa mga may-ari nito: kailangan mong maghukay sa code at itama ang sitwasyon.
Ginagamit namin ang nakolektang data, halimbawa, upang maipatupad nang tama ang awtomatikong pag-scale at, sa huli, sa pangkalahatan ay nauunawaan kung gaano ka-scalable ang serbisyo.
Sa panahon ng pagsubok sa pag-load, sinusuri namin kung ang pagkonsumo ng mapagkukunan ay nakakatugon sa mga itinakdang limitasyon. At pangunahing nakatuon kami sa mga sukdulan.
a) Tinitingnan namin ang kabuuang pagkarga.
- Masyadong maliit - malamang na ang isang bagay ay hindi gagana kung ang load ay biglang bumaba ng maraming beses.
- Masyadong malaki - kailangan ang pag-optimize.
b) Tinitingnan namin ang cutoff ayon sa RPS.
Dito tinitingnan natin ang pagkakaiba sa pagitan ng kasalukuyang bersyon at ng nauna at ang kabuuang dami. Halimbawa, kung ang isang serbisyo ay gumagawa ng 100 rps, kung gayon ito ay alinman sa hindi magandang pagkakasulat, o ito ang pagiging tiyak nito, ngunit sa anumang kaso, ito ay isang dahilan upang tingnan ang serbisyo nang napakalapit.
Kung, sa kabaligtaran, mayroong masyadong maraming RPS, kung gayon marahil mayroong ilang uri ng bug at ang ilan sa mga endpoint ay tumigil sa pagpapatupad ng payload, ngunit ang iba ay na-trigger lang. return true;
Mga pagsubok sa canary
Pagkatapos naming makapasa sa mga synthetic na pagsubok, sinubukan namin ang microservice sa isang maliit na bilang ng mga user. Nagsisimula kaming maingat, na may maliit na bahagi ng nilalayong madla ng serbisyo - mas mababa sa 0,1%. Sa yugtong ito, napakahalaga na ang tamang teknikal at mga sukatan ng produkto ay kasama sa pagsubaybay upang maipakita nila ang problema sa serbisyo sa lalong madaling panahon. Ang pinakamababang oras para sa isang canary test ay 5 minuto, ang pangunahing isa ay 2 oras. Para sa mga kumplikadong serbisyo, manu-manong itinakda namin ang oras.
Suriin natin:
β ΠΌΠ΅ΡΡΠΈΠΊΠΈ, ΡΠΏΠ΅ΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π΄Π»Ρ ΡΠ·ΡΠΊΠ°, Π² ΡΠ°ΡΡΠ½ΠΎΡΡΠΈ, Π²ΠΎΡΠΊΠ΅ΡΡ php-fpm;
β mga error sa Sentry;
- mga katayuan ng tugon;
β oras ng pagtugon, eksakto at karaniwan;
β latency;
β mga pagbubukod, naproseso at hindi nahawakan;
β mga sukatan ng produkto.
β’ Pagsusukat. Kapag inilunsad namin ang isang serbisyo sa produksyon, sinusubaybayan namin kung paano ito nasusukat. Sa aming karanasan, ang pagsubaybay lamang sa mga tagapagpahiwatig ng CPU ay hindi epektibo. Gumagana ang auto scaling gamit ang RPS benchmarking sa purong anyo nito, ngunit para lang sa ilang partikular na serbisyo, gaya ng online streaming. Kaya tinitingnan muna namin ang mga sukatan ng produkto na tukoy sa application.
Bilang resulta, kapag nag-scale, sinusuri namin ang:
β ΠΏΠΎΠΊΠ°Π·Π°ΡΠ΅Π»ΠΈ CPU ΠΈ RAM,
β ang bilang ng mga kahilingan sa pila,
- oras ng pagtugon,
β pagtataya batay sa naipon na makasaysayang data.
Pagkatapos maisagawa ang microservice, maaari kaming mag-attach ng mga trigger dito.
ΠΠΎΡ ΡΠΈΠΏΠΈΡΠ½ΡΠ΅ ΡΠΈΡΡΠ°ΡΠΈΠΈ, Π² ΠΊΠΎΡΠΎΡΡΡ ΡΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ ΡΡΠΈΠ³Π³Π΅ΡΡ.
β May nakitang potensyal na mapanganib na paglilipat.
β Ang mga update sa seguridad ay inilabas.
β Ang serbisyo mismo ay hindi na-update sa loob ng mahabang panahon.
β Ang pagkarga sa serbisyo ay kapansin-pansing nabawasan o ang ilan sa mga sukatan ng produkto nito ay nasa labas ng normal na hanay.
β Hindi na natutugunan ng serbisyo ang mga bagong kinakailangan sa platform.
Ang ilan sa mga nag-trigger ay may pananagutan para sa katatagan ng operasyon, ang ilan - bilang isang function ng pagpapanatili ng system - halimbawa, ang ilang serbisyo ay hindi na-deploy sa loob ng mahabang panahon at ang base na imahe nito ay tumigil sa pagpasa sa mga pagsusuri sa seguridad.
Dashboard
Sa madaling salita, ang dashboard ay ang control panel ng aming buong PaaS.
Isang punto ng impormasyon tungkol sa serbisyo, na may data sa saklaw ng pagsubok nito, ang bilang ng mga larawan nito, ang bilang ng mga kopya ng produksyon, mga bersyon, atbp.
Isang solong punto ng view ng lahat ng mga kaganapan sa mga serbisyo.
Sa kabuuan
Bago ipakilala ang PaaS, maaaring gumugol ng ilang linggo ang isang bagong developer sa pag-unawa sa lahat ng mga tool na kinakailangan para maglunsad ng microservice sa produksyon: Kubernetes, Helm, ang aming mga internal na feature ng TeamCity, pagse-set up ng mga koneksyon sa mga database at cache sa isang fault-tolerant na paraan, atbp. Ngayon ay tumatagal ng ilang oras upang basahin ang quickstart at gawin ang serbisyo mismo.
Nagbigay ako ng ulat sa paksang ito para sa HighLoad++ 2018, maaari mo itong panoorin video ΠΈ pagtatanghal.
Bonus track para sa mga nagbabasa hanggang dulo
Kami sa Avito ay nag-aayos ng panloob na tatlong araw na pagsasanay para sa mga developer mula sa Chris Richardson, ΡΠΊΡΠΏΠ΅ΡΡΠ° ΠΏΠΎ ΠΌΠΈΠΊΡΠΎΡΠ΅ΡΠ²ΠΈΡΠ½ΠΎΠΉ Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ΅. Π₯ΠΎΡΠΈΠΌ ΠΏΠΎΠ΄Π°ΡΠΈΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ ΡΡΠ°ΡΡΠΈΡ Π² Π½ΡΠΌ ΠΊΠΎΠΌΡ-ΡΠΎ ΠΈΠ· ΡΠΈΡΠ°ΡΠ΅Π»Π΅ΠΉ ΡΡΠΎΠ³ΠΎ ΠΏΠΎΡΡΠ°. Dito Ang programa ng pagsasanay ay nai-post.
Ang pagsasanay ay magaganap mula Agosto 5 hanggang 7 sa Moscow. Ito ay mga araw ng trabaho na ganap na abala. Ang tanghalian at pagsasanay ay nasa aming opisina, at ang napiling kalahok ay magbabayad para sa paglalakbay at tirahan mismo.
Maaari kang mag-aplay para sa pakikilahok sa google form na ito. Mula sa iyo - ang sagot sa tanong kung bakit kailangan mong dumalo sa pagsasanay at impormasyon kung paano makipag-ugnayan sa iyo. Sagot sa English, dahil si Chris ang pipili ng kalahok na dadalo mismo sa training.
Iaanunsyo namin ang pangalan ng kalahok sa pagsasanay sa isang update sa post na ito at sa mga social network na Avito para sa mga developer (AvitoTech sa Facebook, Vkontakte, Twitter) hindi lalampas sa Hulyo 19.