Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Në RIT 2019, kolegu ynë Alexander Korotkov bëri raportin rreth automatizimit të zhvillimit në CIAN: për të thjeshtuar jetën dhe punën, ne përdorim platformën tonë Integro. Ai gjurmon ciklin jetësor të detyrave, lehtëson zhvilluesit nga operacionet rutinë dhe redukton ndjeshëm numrin e gabimeve në prodhim. Në këtë postim, ne do të plotësojmë raportin e Aleksandrit dhe do t'ju tregojmë se si kaluam nga skriptet e thjeshta në kombinimin e produkteve me burim të hapur përmes platformës sonë dhe çfarë bën ekipi ynë i veçantë i automatizimit.
 

Niveli zero

“Nuk ka gjĂ« tĂ« tillĂ« si niveli zero, unĂ« nuk e di njĂ« gjĂ« tĂ« tillĂ«â€
Master Shifu nga filmi "Kung Fu Panda"

Automatizimi nĂ« CIAN filloi 14 vjet pas themelimit tĂ« kompanisĂ«. NĂ« atĂ« kohĂ« kishte 35 persona nĂ« ekipin e zhvillimit. VĂ«shtirĂ« tĂ« besohet, apo jo? Sigurisht, automatizimi ekzistonte nĂ« njĂ«farĂ« forme, por njĂ« drejtim i veçantĂ« pĂ«r integrimin e vazhdueshĂ«m dhe dĂ«rgimin e kodit filloi tĂ« merrte formĂ« nĂ« 2015. 

Në atë kohë, ne kishim një monolit të madh Python, C# dhe PHP të vendosur në Linux/Windows servera. Për të vendosur këtë përbindësh, kishim një sërë skriptesh që i ekzekutuam manualisht. Kishte edhe ndërtimin monolitik, i cili sillte dhimbje dhe vuajtje për shkak të konflikteve gjatë bashkimit të degëve, rregullimit të defekteve dhe rindërtimit "me një sërë detyrash të ndryshme në ndërtim". Thjeshtuar, procesi dukej kështu:

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Ne nuk ishim të kënaqur me këtë dhe donim të ndërtonim një proces ndërtimi dhe vendosjeje të përsëritshëm, të automatizuar dhe të menaxhueshëm. Për këtë na nevojitej një sistem CI/CD dhe zgjodhëm midis versionit falas të Teamcity dhe versionit falas të Jenkins, pasi kemi punuar me ta dhe të dy na përshtateshin përsa i përket grupit të funksioneve. Ne zgjodhëm Teamcity si një produkt më të fundit. Në atë kohë, ne ende nuk kishim përdorur arkitekturën e mikroservisit dhe nuk prisnim një numër të madh detyrash dhe projektesh.

Ne arrijmë te ideja e sistemit tonë

Zbatimi i Teamcity hoqi vetëm një pjesë të punës manuale: ajo që mbetet është krijimi i Kërkesave për tërheqje, promovimi i çështjeve sipas statusit në Jira dhe përzgjedhja e çështjeve për lëshim. Sistemi Teamcity nuk mund ta përballonte më këtë. Ishte e nevojshme të zgjidhej rruga e automatizimit të mëtejshëm. Ne shqyrtuam opsionet për të punuar me skriptet në Teamcity ose kalimin në sistemet e automatizimit të palëve të treta. Por në fund vendosëm se na duhej fleksibilitet maksimal, të cilin vetëm zgjidhja jonë mund ta sigurojë. Kështu u shfaq versioni i parë i sistemit të automatizimit të brendshëm të quajtur Integro.

Teamcity merret me automatizimin nĂ« nivelin e nisjes sĂ« proceseve tĂ« ndĂ«rtimit dhe vendosjes, ndĂ«rsa Integro u fokusua nĂ« automatizimin e nivelit tĂ« lartĂ« tĂ« proceseve tĂ« zhvillimit. Ishte e nevojshme tĂ« kombinohej puna me çështjet nĂ« Jira me pĂ«rpunimin e kodit burimor tĂ« lidhur nĂ« Bitbucket. NĂ« kĂ«tĂ« fazĂ«, Integro filloi tĂ« kishte flukset e veta tĂ« punĂ«s pĂ«r tĂ« punuar me detyra tĂ« llojeve tĂ« ndryshme. 

Për shkak të rritjes së automatizimit në proceset e biznesit, numri i projekteve dhe ekzekutimeve në Teamcity është rritur. Kështu erdhi një problem i ri: një shembull falas i Teamcity nuk ishte i mjaftueshëm (3 agjentë dhe 100 projekte), shtuam një shembull tjetër (3 agjentë të tjerë dhe 100 projekte), pastaj një tjetër. Si rezultat, ne përfunduam me një sistem me disa grupime, i cili ishte i vështirë për t'u menaxhuar:

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Kur lindi çështja e një shkalle të katërt, ne kuptuam se nuk mund të vazhdonim të jetonim kështu, sepse kostot totale të mbështetjes së 4 instancave nuk ishin më brenda asnjë kufiri. U ngrit pyetja në lidhje me blerjen e Teamcity me pagesë ose zgjedhjen e Jenkins falas. Ne bëmë llogaritjet për instancat dhe planet e automatizimit dhe vendosëm që do të jetonim në Jenkins. Pas disa javësh, ne kaluam te Jenkins dhe eliminuam disa nga dhimbjet e kokës që lidhen me mbajtjen e rasteve të shumta të Teamcity. Prandaj, ne ishim në gjendje të përqendroheshim në zhvillimin e Integro dhe personalizimin e Jenkins për veten tonë.

Me rritjen e automatizimit bazĂ« (nĂ« formĂ«n e krijimit automatik tĂ« kĂ«rkesave pĂ«r tĂ«rheqje, mbledhjes dhe publikimit tĂ« mbulimit tĂ« kodit dhe kontrolleve tĂ« tjera), ekziston njĂ« dĂ«shirĂ« e fortĂ« pĂ«r tĂ« braktisur sa mĂ« shumĂ« lĂ«shimet manuale dhe pĂ«r t'ia dhĂ«nĂ« kĂ«tĂ« punĂ« robotĂ«ve. PĂ«rveç kĂ«saj, kompania filloi tĂ« kalonte nĂ« mikroshĂ«rbime brenda kompanisĂ«, tĂ« cilat kĂ«rkonin lĂ«shime tĂ« shpeshta, dhe veçmas nga njĂ«ra-tjetra. KĂ«shtu arritĂ«m gradualisht te lĂ«shimet automatike tĂ« mikroshĂ«rbimeve tona (momentalisht po e lĂ«shojmĂ« monolitin manualisht pĂ«r shkak tĂ« kompleksitetit tĂ« procesit). Por, siç ndodh zakonisht, lindi njĂ« kompleksitet i ri. 

Ne automatizojmë testimin

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

PĂ«r shkak tĂ« automatizimit tĂ« lĂ«shimeve, proceset e zhvillimit janĂ« pĂ«rshpejtuar, pjesĂ«risht pĂ«r shkak tĂ« kapĂ«rcimit tĂ« disa fazave tĂ« testimit. Dhe kjo çoi nĂ« njĂ« humbje tĂ« pĂ«rkohshme tĂ« cilĂ«sisĂ«. TingĂ«llon e parĂ«ndĂ«sishme, por sĂ« bashku me pĂ«rshpejtimin e lĂ«shimeve, ishte e nevojshme tĂ« ndryshohej metodologjia e zhvillimit tĂ« produktit. Ishte e nevojshme tĂ« mendohej pĂ«r automatizimin e testimit, futjen e pĂ«rgjegjĂ«sisĂ« personale (kĂ«tu po flasim pĂ«r "pranimin e idesĂ« nĂ« kokĂ«", jo gjobat monetare) tĂ« zhvilluesit pĂ«r kodin e lĂ«shuar dhe defektet nĂ« tĂ«, si dhe vendimin pĂ«r tĂ« lĂ«sho/mos lĂ«sho njĂ« detyrĂ« pĂ«rmes vendosjes automatike. 

Duke eliminuar problemet e cilĂ«sisĂ«, arritĂ«m nĂ« dy vendime tĂ« rĂ«ndĂ«sishme: filluam tĂ« kryenim testimin e kanarinĂ«s dhe futĂ«m monitorimin automatik tĂ« sfondit tĂ« gabimit me njĂ« pĂ«rgjigje automatike ndaj tepricĂ«s sĂ« tij. Zgjidhja e parĂ« bĂ«ri tĂ« mundur gjetjen e gabimeve tĂ« dukshme pĂ«rpara se kodi tĂ« lĂ«shohej plotĂ«sisht nĂ« prodhim, e dyta zvogĂ«loi kohĂ«n e pĂ«rgjigjes ndaj problemeve nĂ« prodhim. Gabimet, natyrisht, ndodhin, por ne shpenzojmĂ« pjesĂ«n mĂ« tĂ« madhe tĂ« kohĂ«s dhe pĂ«rpjekjeve tona jo pĂ«r t'i korrigjuar ato, por pĂ«r t'i minimizuar ato. 

Ekipi i Automatizimit

Aktualisht kemi njĂ« staf prej 130 zhvilluesish dhe vazhdojmĂ« rritet. Ekipi pĂ«r integrimin e vazhdueshĂ«m dhe shpĂ«rndarjen e kodit (nĂ« tekstin e mĂ«tejmĂ« referuar si ekipi Deploy and Integration ose DI) pĂ«rbĂ«het nga 7 persona dhe punon nĂ« 2 drejtime: zhvillimi i platformĂ«s sĂ« automatizimit Integro dhe DevOps. 

DevOps Ă«shtĂ« pĂ«rgjegjĂ«s pĂ«r mjedisin Dev/Beta tĂ« faqes CIAN, mjedisin Integro, ndihmon zhvilluesit tĂ« zgjidhin problemet dhe zhvillojnĂ« qasje tĂ« reja pĂ«r mjediset e shkallĂ«zimit. Drejtimi i zhvillimit tĂ« Integro merret si me vetĂ« Integro ashtu edhe me shĂ«rbimet pĂ«rkatĂ«se, pĂ«r shembull, shtojcat pĂ«r Jenkins, Jira, Confluence, dhe gjithashtu zhvillon shĂ«rbime ndihmĂ«se dhe aplikacione pĂ«r ekipet e zhvillimit. 

Ekipi DI punon në bashkëpunim me ekipin e Platformës, i cili zhvillon nga brenda arkitekturën, bibliotekat dhe qasjet e zhvillimit. Në të njëjtën kohë, çdo zhvillues brenda CIAN mund të kontribuojë në automatizim, për shembull, të bëjë mikro-automatizim për t'iu përshtatur nevojave të ekipit ose të ndajë një ide interesante se si ta bëjmë automatizimin edhe më të mirë.

Tortë me shtresa të automatizimit në CIAN

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Të gjitha sistemet e përfshira në automatizim mund të ndahen në disa shtresa:

  1. Sistemet e jashtme (Jira, Bitbucket, etj.). Ekipet e zhvillimit punojnë me ta.
  2. Platforma Integro. Më shpesh, zhvilluesit nuk punojnë me të drejtpërdrejt, por është ajo që e mban të gjithë automatizimin të funksionojë.
  3. Shërbimet e dorëzimit, orkestrimit dhe zbulimit (për shembull, Jeknins, Konsulli, Nomad). Me ndihmën e tyre, ne vendosim kodin në serverë dhe sigurojmë që shërbimet të funksionojnë me njëri-tjetrin.
  4. Shtresa fizike (serverët, OS, programet përkatëse). Kodi ynë funksionon në këtë nivel. Ky mund të jetë ose një server fizik ose virtual (LXC, KVM, Docker).

Bazuar në këtë koncept, ne ndajmë fushat e përgjegjësisë brenda ekipit të DI. Dy nivelet e para janë në fushën e përgjegjësisë së drejtimit të zhvillimit Integro dhe dy nivelet e fundit janë tashmë në fushën e përgjegjësisë së DevOps. Kjo ndarje na lejon të përqendrohemi në detyra dhe nuk ndërhyn në ndërveprim, pasi jemi afër njëri-tjetrit dhe vazhdimisht shkëmbejmë njohuri dhe përvojë.

E paprekur

Le të përqendrohemi te Integro dhe të fillojmë me grumbullin e teknologjisë:

  • CentOs 7
  • Docker + Nomad + Konsull + Vault
  • Java 11 (monoliti i vjetĂ«r Integro do tĂ« mbetet nĂ« Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • LepuriMQ 
  • Apache Ignite
  • Camunda (e ngulitur)
  • Grafana + Grafit + Prometeu + Jaeger + ELK
  • Ueb UI: React (CSR) + MobX
  • ZSHS: ÇelĂ«sa

Ne i pĂ«rmbahemi parimit tĂ« zhvillimit tĂ« mikroshĂ«rbimeve, megjithĂ«se kemi trashĂ«gimi nĂ« formĂ«n e njĂ« monoliti tĂ« njĂ« versioni tĂ« hershĂ«m tĂ« Integro. Çdo mikroshĂ«rbim funksionon nĂ« kontejnerin e vet Docker dhe shĂ«rbimet komunikojnĂ« me njĂ«ri-tjetrin nĂ«pĂ«rmjet kĂ«rkesave HTTP dhe mesazheve RabbitMQ. MikroshĂ«rbimet e gjejnĂ« njĂ«ri-tjetrin pĂ«rmes Konsullit dhe i bĂ«jnĂ« njĂ« kĂ«rkesĂ«, duke kaluar autorizimin pĂ«rmes SSO (Keycloak, OAuth 2/OpenID Connect).

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Si një shembull i jetës reale, merrni parasysh ndërveprimin me Jenkins, i cili përbëhet nga hapat e mëposhtëm:

  1. Mikroshërbimi i menaxhimit të rrjedhës së punës (në tekstin e mëtejmë i referuar si mikroshërbimi Flow) dëshiron të ekzekutojë një ndërtim në Jenkins. Për ta bërë këtë, ai përdor Konsullin për të gjetur IP:PORTin e mikroshërbimit për integrimin me Jenkins (në tekstin e mëtejmë si mikroshërbim Jenkins) dhe i dërgon atij një kërkesë asinkrone për të filluar ndërtimin në Jenkins.
  2. Pas marrjes së një kërkese, mikroshërbimi Jenkins gjeneron dhe përgjigjet me një ID të punës, e cila më pas mund të përdoret për të identifikuar rezultatin e punës. Në të njëjtën kohë, ai aktivizon ndërtimin në Jenkins nëpërmjet një thirrjeje REST API.
  3. Jenkins kryen ndërtimin dhe, pas përfundimit, dërgon një uebhook me rezultatet e ekzekutimit te mikroshërbimi Jenkins.
  4. Mikroshërbimi Jenkins, pasi ka marrë webhook-in, gjeneron një mesazh në lidhje me përfundimin e përpunimit të kërkesës dhe i bashkangjit rezultatet e ekzekutimit. Mesazhi i krijuar dërgohet në radhën e RabbitMQ.
  5. Nëpërmjet RabbitMQ, mesazhi i publikuar arrin në mikroshërbimin Flow, i cili mëson për rezultatin e përpunimit të detyrës së tij duke përputhur Job ID-në nga kërkesa dhe mesazhi i marrë.

Tani kemi rreth 30 mikroshërbime, të cilat mund të ndahen në disa grupe:

  1. Menaxhimi i konfigurimit.
  2. Informacioni dhe ndërveprimi me përdoruesit (lajmëtarët, posta).
  3. Puna me kodin burimor.
  4. Integrimi me mjetet e vendosjes (jenkins, nomad, konsull, etj.).
  5. Monitorimi (lëshimet, gabimet, etj.).
  6. Shërbimet në internet (UI për menaxhimin e mjediseve të testimit, mbledhjen e statistikave, etj.).
  7. Integrimi me gjurmuesit e detyrave dhe sisteme të ngjashme.
  8. Menaxhimi i rrjedhës së punës për detyra të ndryshme.

Detyrat e rrjedhës së punës

Integro automatizon aktivitetet qĂ« lidhen me ciklin jetĂ«sor tĂ« detyrĂ«s. NĂ« terma tĂ« thjeshtuar, cikli i jetĂ«s sĂ« njĂ« detyre do tĂ« kuptohet si rrjedha e punĂ«s e njĂ« detyre nĂ« Jira. Proceset tona tĂ« zhvillimit kanĂ« disa variacione tĂ« rrjedhĂ«s sĂ« punĂ«s nĂ« varĂ«si tĂ« projektit, llojit tĂ« detyrĂ«s dhe opsioneve tĂ« zgjedhura nĂ« njĂ« detyrĂ« tĂ« caktuar. 

Le të shohim rrjedhën e punës që përdorim më shpesh:

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Në diagram, ingranazhi tregon se tranzicioni thirret automatikisht nga Integro, ndërsa figura njerëzore tregon që kalimi thirret manualisht nga një person. Le të shohim disa rrugë që mund të marrë një detyrë në këtë rrjedhë pune.

Testim plotësisht manual në DEV+BETA pa teste kanarinë (zakonisht kështu lëshojmë një monolit):

Nga skriptet në platformën tonë: si e automatizuam zhvillimin në CIAN

Mund të ketë kombinime të tjera tranzicioni. Ndonjëherë rruga që do të marrë një problem mund të zgjidhet përmes opsioneve në Jira.

Lëvizja e detyrës

Le të shohim hapat kryesorë që kryhen kur një detyrë kalon nëpër fluksin e punës "Testimi DEV + Testet Kanarie":

1. Zhvilluesi ose PM krijon detyrën.

2. Zhvilluesi e merr detyrĂ«n nĂ« punĂ«. Pas pĂ«rfundimit, ai kalon nĂ« statusin NË RISHIKIM.

3. Jira dërgon një Webhook te mikroshërbimi Jira (përgjegjës për integrimin me Jira).

4. Mikroshërbimi Jira i dërgon një kërkesë shërbimit Flow (përgjegjës për rrjedhat e brendshme të punës në të cilat kryhet puna) për të nisur rrjedhën e punës.

5. Brenda shërbimit Flow:

  • Rishikuesit janĂ« caktuar pĂ«r detyrĂ«n (MikroshĂ«rbimi i pĂ«rdoruesve qĂ« di gjithçka pĂ«r pĂ«rdoruesit + MikroshĂ«rbimi Jira).
  • NĂ«pĂ«rmjet mikroshĂ«rbimit Burimi (ai di pĂ«r depo dhe degĂ«, por nuk funksionon me vetĂ« kodin), bĂ«het njĂ« kĂ«rkim pĂ«r depo qĂ« pĂ«rmbajnĂ« njĂ« degĂ« tĂ« emetimit tonĂ« (pĂ«r tĂ« thjeshtuar kĂ«rkimin, emri i degĂ«s pĂ«rkon me çështjen numri nĂ« Jira). MĂ« shpesh, njĂ« detyrĂ« ka vetĂ«m njĂ« degĂ« nĂ« njĂ« depo; kjo thjeshton menaxhimin e radhĂ«s sĂ« vendosjes dhe redukton lidhjen midis depove.
  • PĂ«r secilĂ«n degĂ« tĂ« gjetur, kryhet sekuenca e mĂ«poshtme e veprimeve:

    i) Përditësimi i degës kryesore (Git microservice për të punuar me kod).
    ii) Dega është e bllokuar nga ndryshimet nga zhvilluesi (microservice Bitbucket).
    iii) Krijohet një kërkesë tërheqjeje për këtë degë (mikroshërbimi Bitbucket).
    iv) Një mesazh në lidhje me një kërkesë të re tërheqjeje dërgohet në bisedat e zhvilluesve (Njoftoni microservice për punën me njoftimet).
    v) Ndërtimi, testimi dhe vendosja e detyrave filluan në DEV (mikroshërbimi Jenkins për të punuar me Jenkins).
    vi) Nëse të gjithë hapat e mëparshëm janë përfunduar me sukses, atëherë Integro vendos miratimin e tij në Kërkesën për tërheqje (mikroshërbimi Bitbucket).

  • Integro pret miratimin nĂ« kĂ«rkesĂ«n pĂ«r tĂ«rheqje nga rishikuesit e caktuar.
  • Sapo tĂ« jenĂ« marrĂ« tĂ« gjitha miratimet e nevojshme (duke pĂ«rfshirĂ« testet e automatizuara kanĂ« kaluar pozitivisht), Integro e transferon detyrĂ«n nĂ« statusin Test on Dev (Jira microservice).

6. Testuesit testojnë detyrën. Nëse nuk ka probleme, atëherë detyra transferohet në statusin Ready For Build.

7. Integro "sheh" që detyra është gati për lëshim dhe fillon vendosjen e saj në modalitetin e kanarisë (microservice Jenkins). Gatishmëria për lirim përcaktohet nga një sërë rregullash. Për shembull, detyra është në statusin e kërkuar, nuk ka bravë në detyra të tjera, aktualisht nuk ka ngarkime aktive të këtij mikroshërbimi, etj.

8. Detyra kalon në statusin Kanarie (Microservice Jira).

9. Jenkins nis një detyrë vendosjeje përmes Nomad në modalitetin e kanarisë (zakonisht 1-3 raste) dhe njofton shërbimin e monitorimit të lëshimit (mikroshërbimi DeployWatch) për vendosjen.

10. Mikroshërbimi DeployWatch mbledh sfondin e gabimit dhe reagon ndaj tij, nëse është e nevojshme. Nëse sfondi i gabimit tejkalohet (norma e sfondit llogaritet automatikisht), zhvilluesit njoftohen nëpërmjet microservice Notify. Nëse pas 5 minutash zhvilluesi nuk është përgjigjur (kliko Revert ose Stay), atëherë niset një rikthim automatik i rasteve të kanarinave. Nëse sfondi nuk tejkalohet, atëherë zhvilluesi duhet të nisë manualisht vendosjen e detyrës në Production (duke klikuar një buton në UI). Nëse zhvilluesi nuk e ka nisur vendosjen në Prodhim brenda 60 minutave, atëherë edhe rastet e kanarinave do të rikthehen për arsye sigurie.

11. Pas nisjes së vendosjes në Prodhim:

  • Detyra transferohet nĂ« statusin e prodhimit (Microservice Jira).
  • MikroshĂ«rbimi Jenkins fillon procesin e vendosjes dhe njofton mikroshĂ«rbimin DeployWatch pĂ«r vendosjen.
  • MikroshĂ«rbimi DeployWatch kontrollon qĂ« tĂ« gjithĂ« kontejnerĂ«t nĂ« Production janĂ« pĂ«rditĂ«suar (ka pasur raste kur jo tĂ« gjithĂ« janĂ« pĂ«rditĂ«suar).
  • NĂ«pĂ«rmjet mikroshĂ«rbimit Notify, njĂ« njoftim pĂ«r rezultatet e vendosjes i dĂ«rgohet Prodhimit.

12. Zhvilluesit do të kenë 30 minuta për të filluar të rikthejnë një detyrë nga Prodhimi nëse zbulohet një sjellje e gabuar e mikroshërbimit. Pas kësaj kohe, detyra do të shkrihet automatikisht në master (Git microservice).

13. Pas një bashkimi të suksesshëm në master, statusi i detyrës do të ndryshohet në Closed (Jira microservice).

Diagrami nuk pretendon të jetë plotësisht i detajuar (në realitet ka edhe më shumë hapa), por ju lejon të vlerësoni shkallën e integrimit në procese. Ne nuk e konsiderojmë këtë skemë ideale dhe po përmirësojmë proceset e lëshimit automatik dhe mbështetjes së vendosjes.

Ç'pritet mĂ« tej

Ne kemi plane të mëdha për zhvillimin e automatizimit, për shembull, eliminimin e operacioneve manuale gjatë lëshimeve monolit, përmirësimin e monitorimit gjatë vendosjes automatike dhe përmirësimin e ndërveprimit me zhvilluesit.

Por le të ndalemi këtu për momentin. Ne mbuluam shumë tema në rishikimin e automatizimit sipërfaqësisht, disa nuk u prekën fare, kështu që do të jemi të lumtur t'u përgjigjemi pyetjeve. Ne presim sugjerime se çfarë të mbulojmë në detaje, shkruani në komente.

Burimi: www.habr.com

Bleni njĂ« host tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, serverĂ« VPS VDS đŸ”„ Bleni hosting tĂ« besueshĂ«m tĂ« faqeve tĂ« internetit me mbrojtje DDoS, servera VPS VDS | ProHoster