Testimi në prodhim: Canary Deployment

Kanaria - një zog i vogël që këndon vazhdimisht. Këto zogj janë të ndjeshëm ndaj metanit dhe gazit të karbonit. Edhe nga përqendrimi më i vogël i gazrave të tepërt në ajër, ata humbasin vetëdijen ose vdesin. Gjuetarët e arit dhe minatorët i merrnin zogjtë me vete për të punuar: sa kohë që kanaritë këndojnë, mund të punohet; nëse ndalojnë - ka gaz në minierë dhe është koha për t'u larguar. Minatorët flijonin zogun e vogël për të dalë të gjallë nga minierat.

Testimi në prodhim: Canary Deployment

Një praktikë e tillë ka gjetur vendin e saj edhe në IT. Për shembull, në detyrën standarde të vendosjes së një versioni të ri të shërbimit ose aplikacionit në prodhim, duke testuar para kësaj. Mjedisi testues mund të jetë tepër i shtrenjtë, testet automatike nuk mbulojnë gjithçka që dëshirohet, dhe të mos testosh dhe të flijosh cilësinë është e rrezikshme. Pikërisht në këto raste ndihmon qasja e Canary Deployment, kur pak trafik real të prodhimit dërgohet në versionin e ri. Ky qasje ndihmon me siguri të kontrollosh versionin e ri në prodhim, duke flijuar pak për një qëllim të madh. Më shumë informacion se si funksionon qasja, çfarë përfitimesh ka dhe si të realizohet do të japë Andrey Markelov (Andrey_V_Markelov), në shembullin e implementimit në kompaninë Infobip.

Andrey Markelov — inxhinier kryesor në Infobip, ka 11 vjet që merret me zhvillimin e aplikacioneve në Java në fushën e financave dhe telekomunikacionit. Zhvillon produkte Open Source, merr pjesë aktivisht në Komunitetin Atlassian dhe shkruan mjete për produktet Atlassian. Evangelist për Prometheus, Docker dhe Redis.

Luaj videon

Rreth kompanisë Infobip

Kjo është një platformë globale e telekomunikacionit, e cila i mundëson bankave, tregtisë me pakicë, dyqaneve online dhe kompanive të transportit të dërgojnë mesazhe klientëve të tyre përmes SMS, njoftimeve push, email-eve dhe mesazheve me zë. Në këtë biznes, stabiliteti dhe besueshmëria janë të rëndësishme për të siguruar që klientët të marrin mesazhet në kohë.

IT-infrastruktura e Infobip në numra:

  • 15 qendra të të dhënave në të gjithë botën;
  • 500 shërbime unike në përdorim;
  • 2500 ekzemplarë shërbimesh, shumë më tepër se nën ekipet;
  • 4.5 TB trafik mujor;
  • 4.5 miliardë numra telefonikë;

Biznesi po rritet dhe me të rritet numri i lëshimeve. Ne realizojmë 60 lëshime në ditë, sepse klientët duan më shumë mundësi dhe kapacitet. Por kjo është e komplikuar — ka shumë shërbime dhe pak ekipe. Duhet të shkruajmë me shpejtësi kod, i cili duhet të funksionojë në prodhim pa gabime.

Lëshimet

Një lansim tipik tek ne ndodh kështu. Për shembull, ekzistojnë shërbimet A, B, C, D dhe E, secili prej tyre zhvillohet nga një ekip të veçantë.

Testimi në prodhim: Canary Deployment

Në një moment, ekipi i shërbimit A vendos të implementojë një version të ri, por ekipet e shërbimeve B, C, D dhe E nuk janë të informuara për këtë. Ekzistojnë dy mundësi për veprimin e ekipit të shërbimit A.

Do të realizojë një lansim incremental: fillimisht do të zëvendësojë një version dhe pastaj versionin tjetër.

Testimi në prodhim: Canary Deployment

Por ekziston një mundësi tjetër: ekipi do të gjejë burime të tjera dhe makina, do të implementojë versionin e ri dhe pastaj do të kalojë në router, dhe versioni do të fillojë të funksionojë në prodhim.

Testimi në prodhim: Canary Deployment

Në çdo rast, pas lançimit, pothuajse gjithmonë shfaqen probleme, edhe nëse versioni është testuar. Testimi mund të bëhet manualisht, mund të jetë automatizuar, ose mund të mos testohet fare — problemet do të shfaqen gjithsesi. Mënyra më e lehtë dhe e saktë për t'i zgjidhur ato është të rikthehesh në një version funksional. Pas kësaj, mund të merremi me dëmin, arsyet dhe t'i korrigjojmë ato.

Pra, çfarë duam?

Problemet nuk na duhen. Nëse klientët i zbulojnë ato më shpejt se ne, do të dëmtohet reputacioni ynë. Prandaj duhet të gjejmë problemet më shpejt se klientët. Duke punuar në parashikim, ne minimizojmë dëmin.

Në të njëjtën kohë, ne duam të përshpejtojmë deploy-in, kështu që ai të ndodhë shpejt, lehtë, natyrshëm dhe pa stres për ekipin. Inxhinierët, inxhinierët DevOps dhe programuesit duhet ruajtur — lëshimi i një versioni të ri është stresuese. Ekipi nuk është një burim i shpenzueshëm, ne synojmë të përdorim në mënyrë efikase burimet njerëzore.

Problemet e deploy-it

Trafikun e klientëve është të paparashikueshëm. Nuk është e mundur të parashikohet kur trafiku i klientëve do të jetë minimal. Ne nuk e dimë se ku dhe kur klientët do të fillojnë fushatat e tyre — ndoshta sonte në Indi, e ndoshta nesër në Hong Kong. Duke marrë parasysh diferencën e madhe në kohë, deploy-i madhe në 2 të natës nuk garanton se klientët nuk do të dëmtohen.

Problemet e ofruesve. Mesazherët dhe ofruesit janë partnerët tanë. Ndonjëherë ata kanë ndonjë çrregullim që shkakton gabime gjatë deploy-it të versioneve të reja.

Ekipet e shpërndara. Ekipet që zhvillojnë pjesën e klientit dhe backend-in janë në fusha të ndryshme kohore. Për këtë arsye, shpesh nuk mund të bien dakord me njëri-tjetrin.

Qendrat e të dhënave nuk mund të ripërsëriten në skenë. Në një qendër të të dhënave ka 200 rack-e — nuk është e mundur të ripërsëritet kjo në sandbox as në mënyrë të përafërt.

Ndërprerjetnuk lejohen! Ne kemi një nivel të pranueshëm të disponueshmërisë (Error Budget), kur punojmë 99.99% të kohës, për shembull, dhe pjesët e mbetura janë «e drejta për gabim». Arritja e 100% besueshmërisë është e pamundur, por është e rëndësishme të monitorohen vazhdimisht rëniet dhe ndalesat.

Zgjidhjet klasike

Shkrimi i kodit pa defekte. Kur isha një zhvillues i ri, menaxherët më afronin me kërkesën për të bërë një riliz pa defekte, por kjo nuk është gjithmonë e mundur.

Shkrimi i testeve. Testet funksionojnë, por nganjëherë fare nuk veprojnë siç do biznesi. Blerja e parave nuk është një detyrë e testeve.

Testimi në skenë. Gjatë 3.5 viteve të punës sime në Infobip, nuk kam parë asnjëherë që gjendja e skenës të përputhej edhe pjesërisht me prodhimin.

Testimi në prodhim: Canary Deployment

Madje provuam të zhvillonim këtë ide: fillimisht kishim një skenë, pastaj një paraprodhim, dhe më pas një paraprodhim të paraprodhimit. Por as kjo nuk ndihmoi — ato nuk përputheshin as në kapacitet. Me skenën mund të garantonim funksionalitetin bazë, por nuk e dinim se si do të funksiononte nën ngarkesa.

Riliz realizohet nga ai që e zhvilloi. Kjo është një praktikë e mirë: madje edhe nëse dikush ndryshon titullin e komentit, ai e shton menjëherë në prodhim. Kjo ndihmon në zhvillimin e përgjegjësisë dhe në mos harrimin e ndryshimeve të bëra.

Janë të pranishme edhe vështirësi të tjera. Për zhvilluesit, kjo është një stres — të kalosh shumë kohë për të verifikuar gjithçka manualisht.

Lëshime të harmonizuara. Ky variant zakonisht propozohet nga menaxhmenti: "Le të biem dakord që çdo ditë të testoni dhe të shtoni versione të reja". Kjo nuk funksionon: gjithmonë ka një ekip që pret të tjerët ose e kundërta.

Smoke-testet

Një tjetër mënyrë për të zgjidhur problemet tona me vendosjen. Le të shqyrtojmë se si funksionojnë smoke-testet në shembullin e mëparshëm, kur ekipi A dëshiron të vendosë një version të ri.

Së pari, ekipi vendos një instancë në prodhim. Mesazhet në instancë nga mock imiton trafikun e vërtetë, në mënyrë që ai të përputhet me trafikun normal të përditshëm. Nëse gjithçka shkon mirë, ekipi kalon versionin e ri në trafikun e përdoruesve.

Testimi në prodhim: Canary Deployment

Varianti i dytë — të vendosësh me pajisje shtesë. Ekipi e teston atë në prodhim, pastaj e kalon, dhe gjithçka funksionon.

Testimi në prodhim: Canary Deployment

Disavantazhet e smoke-testeve:

  • Testet nuk duhet t'i besohet. Ku mund të merrni të njëjtin trafik si në prodhim? Mund të përdoret trafiku i djeshëm ose i javës së kaluar, por nuk është gjithmonë në përputhje me atë aktual.
  • E komplikuar për t'u mbajtur. Do të duhet të mbani llogari testimi, t'i riktheheni ato vazhdimisht para çdo shpërndarjeje, kur në depo dërgohen regjistrime aktive. Kjo është më e komplikuar se të shkruani një test në sandkushin tuaj.

Bonusi i vetëm këtu është mund të kontrolloni performancën.

Çrelease Canary

Për shkak të disavantazheve të testeve smoke, filluam të përdorim çrelease Canary.

Një praktikë, e ngjashme me atë siç minerët e përdornin kanarinat për të treguar nivelin e gazrave, thahej edhe në IT. Ne lëshojmë pak trafik real prodhimi në versionin e ri, duke u përpjekur të përputhemi me Marrëveshjen e Nivelit të Shërbimit (SLA). SLA është e drejta jonë për gabim, që mund ta përdorim një herë në vit (ose gjatë një periudhe tjetër). Nëse gjithçka shkon mirë, do të shtojmë më shumë trafik. Nëse jo — do të kthejmë versionet e mëparshme.

Testimi në prodhim: Canary Deployment

Implementimi dhe nuancat

Si e implementuam çrelease Canary? Për shembull, një grup klientësh dërgon mesazhe përmes shërbimit tonë.

Testimi në prodhim: Canary Deployment

Deployimi kalon kështu: heqim një nyje nga balancuesi (1), ndryshojmë versionin (2) dhe veçmas dërgojmë pak trafik (3).

Testimi në prodhim: Canary Deployment

Në përgjithësi, në grup do të jenë të lumtur të gjithë, edhe nëse një përdorues është i pakënaqur. Nëse gjithçka shkon mirë — ndërruam të gjitha versionet.

Testimi në prodhim: Canary Deployment

Do ta shfaq më skematikisht si duket kjo për mikroshërbimet në shumicën e rasteve.

Ka një Zbulim Shërbimi dhe dy shërbime të tjera: S1N1 dhe S2. Shërbimi i parë (S1N1) informon Zbulimin e Shërbimit kur nis dhe Zbulimi i Shërbimit e ruan atë. Shërbimi i dytë me dy nyje (S2N1 dhe S2N2) gjithashtu informon Zbulimin e Shërbimit në fillim.

Testimi në prodhim: Canary Deployment

Shërbimi i dytë për të parin funksionon si server. I pari kërkon nga Zbulimi i Shërbimit informacion mbi serverët e saj, dhe kur e merr — i kërkon dhe verifikon ata ("kontrolli i shëndetit"). Kur ta kontrollojë, do t'i dërgojë mesazhe.

Kur dikush do të shkarkojë një version të ri të shërbimit të dytë, ai njofton Zbulimin e Shërbimit që nyja e dytë do të jetë nyja canary: për të do të dërgohet më pak trafik, sepse tani do të kalojë deploy. Heqim nyjën canary nga balancuesi dhe shërbimi i parë nuk dërgon trafik tek ajo.

Testimi në prodhim: Canary Deployment

Ne jemi duke i ndryshuar versionin dhe Shërbimi i Zbulimit e di se nodi i dytë tani është canary — mund t'i japim asaj një ngarkesë më të ulët (5%). Nëse gjithçka shkon mirë, ne ndryshojmë versionin, rikthejmë ngarkesat dhe vazhdojmë punën.

Për të realizuar gjithë këtë, na nevojiten:

  • balancimi;
  • monitorimi, pasi është e rëndësishme të dimë se çfarë pritet nga çdo përdorues dhe si funksionojnë shërbimet tona në detaje;
  • analiza e versioneve, për të kuptuar sa mirë do të funksionojë versione e re në prodhim;
  • automatizimi — shkruajmë sekuencën e implementimit (deployment pipeline).

Testimi në prodhim: Canary Deployment

Balancimi

Ky është hapi i parë që duhet ta mendojmë. Ka dy strategji balancimi.

Më i thjeshti është kur një nodë gjithmonë është canary.Kjo nodë gjithmonë merr më pak trafik dhe ne fillojmë nëpërmjet saj. Në rast se ndodhin probleme, ne e krahasojmë performancën e saj para dhe gjatë implementimit. Për shembull, nëse gabimet janë dyfishuar, atëherë dëmimi është dyfishuar gjithashtu.

Noda canary caktohet gjatë procesit të implementimit.Kur implementimi të përfundojë dhe ne ta heqim statusin nga ajo si nodë canary, balanca e trafikëve do të rikthehet. Me një numër më të vogël makinash do të arrijmë një shpërndarje të drejtë.

Monitorimi

The cornerstone of canary releases. We must understand precisely why we are doing this and what metrics we want to collect.

Examples of metrics we collect from our services.

  • The number of errors, which are logged. This is an obvious indicator that everything is functioning as it should. Overall, it's a good metric.
  • Request execution time (latency). This metric is monitored by everyone because everyone wants to work quickly.
  • Queue size (throughput).
  • The number of successful responses per second.
  • The execution time for 95% of all requests.
  • Business metrics: how much money the business earns over a specific time or user churn. These metrics for our new version may be more important than those added by engineers.

Examples of metrics in most popular monitoring systems.

Counter. This is some increasing value, for example, the number of errors. This metric is easy to interpolate and study the graph: yesterday there were 2 errors, and today there are 500, so something went wrong.

Numri i gabimeve për minutë ose për sekondë është një tregues thelbësor që mund të llogaritet duke përdorur Counter. Këto të dhëna japin një pasqyrë të qartë të performancës së sistemit në distancë. Le të shqyrtojmë një shembull nga grafiku i numrit të gabimeve për sekondë për dy versione të sistemit në prodhim.

Testimi në prodhim: Canary Deployment

Në versionin e parë pati pak gabime, ndoshta auditi nuk funksiononte. Në versionin e dytë, gjërat janë shumë më keq. Mund të themi me siguri që ka probleme, prandaj duhet të kthejmë këtë version pas.

Gauge. Metrikat ngjajnë me Counter, por ne regjistrojmë vlera që mund të rriten ose ulen. Për shembull, koha e ekzekutimit të kërkesave ose madhësia e radhës.

Në grafik është shembulli i kohës së përgjigjes (latency). Nga grafiku duket se versionet janë të ngjashme, dhe mund të punojmë me to. Por nëse shohim me kujdes, vërejmë se si ndryshon sasia. Nëse koha e ekzekutimit të kërkesave rritet me shtimin e përdoruesve, është menjëherë e qartë se ka probleme — më parë nuk kishte ashtu.

Testimi në prodhim: Canary Deployment

Përmbledhje. Një nga treguesit më të rëndësishëm për biznesin janë percentile. Metrika tregon se në 95% të rasteve sistemi ynë funksionon ashtu siç ne duam. Mund të pranojmë disa probleme, sepse e kuptojmë tendencën e përgjithshme, sa mirë ose keq po shkon gjithçka.

Mjetet

ELK Stack. Realizimi i kanarit mund të bëhet duke përdorur Elasticsearch — regjistrojmë në të gabimet kur ndodhin ngjarje. Me një thirrje të thjeshtë API mund të marrim numrin e gabimeve në çdo moment dhe të krahasojmë me periudhat e kaluara: GET /applg/_cunt?q=level:errr.

Prometheus. Ka performuar mirë në Infobip. Ai lejon realizimin e metrikeve multidimensionale, sepse përdoren etiketat.

Mund të përdorim level, instance, service, t'i kombinojmë ato në një sistem. Me ndihmën e offset mund të shohim, për shembull, vlerën e një sasi para një jave vetëm me një komandë. GET /api/v1/query?query={query}, ku {query}:

rate(logback_appender_total{ 
    level="error",  
    instance=~"$instance" 
}[5m] offset $offset_value)

Analiza e versioneve

Ka disa strategji për analizën e versioneve.

Shiko vetëm metrikat e nodit kanar. Një nga variantet më të thjeshta: kemi vendosur një version të ri dhe studiojmë vetëm funksionimin e tij. Por nëse inxhinieri gjatë kësaj kohe fillon të studiojë log-et, duke e rikthyer vazhdimisht faqen, atëherë ky zgjidhje nuk ka asnjë ndryshim nga të tjerat.

Nodi kanar krahasohet me çdo nod tjetër. Ky është një krahasim me instancat e tjera, të cilat punojnë me trafik të plotë. Për shembull, nëse me trafik të vogël situata është më e keqe, ose jo më e mirë sesa në instancat reale, atëherë ka diçka që nuk shkon.

Canary-noda krahasohet me veten e saj në të kaluarën. Nodët e dedikuar për canary mund të krahasohen me të dhënat historike. Për shembull, nëse javën e kaluar gjithçka ishte mirë, atëherë mund të orientoheni në këto të dhëna për të kuptuar situatën aktuale.

Automatizimi

Ne duam t’i lirojmë inxhinierët nga krahasimi manual, prandaj është e rëndësishme të realizojmë automatizimin. Procesi i depolimit zakonisht duket kështu:

  • startojmë;
  • heqim nodën nga balancuesi;
  • vendosim canary-nodën;
  • ndezim balancuesin tashmë me një sasi të kufizuar të trafikut;
  • krahasojmë.

Testimi në prodhim: Canary Deployment

Në këtë fazë ne realizojmë krahasimin automatik. Si mund të duket dhe pse është më mirë sesa kontrolli pas depolimit, do ta shqyrtojmë me një shembull nga Jenkins.

Ky është një pipeline për Groovy.

while (System.currentTimeMillis() < endCanaryTs) {
    def isOk = compare(srv, canary, time, base, offset, metrics)
    if (isOk) {
        sleep DEFAULT SLEEP
    }   else {
        echo "Canary dështoi, nevojitet rikthim"  
        return false
    }
}

Në këtë cikël vendosim se do të krahasojmë nodin e ri për një orë. Nëse procesi canary nuk ka përfunduar ende — thërrasim funksionin. Ai tregon nëse gjithçka është në rregull apo jo: def isOk = compare(srv, canary, time, base, offset, metrics).

Nëse gjithçka është në rregull — sleep DEFAULT SLEEP, për shembull, për një sekondë, dhe vazhdojmë. Nëse jo, dalim — deploy nuk kishte sukses.

Përshkrimi i metrikeve. Le të shohim si mund të duket funksioni compare në shembullin e DSL.

metric(
    'errorCounts',
    'rate(errorCounts{node=~"$canaryInst"}[5m] offset $offset)',
    {   baseValue, canaryValue ->
        if (canaryValue > baseValue * 1.3) return false 
        return true
    }
)

Supozoni se po krahasojmë numrin e gabimeve dhe duam të gjejmë numrin e gabimeve për sekondë për 5 minutat e fundit.

Ne kemi dy vlera: baza dhe nodin canary. Vlera e nodin canary është aktuale. Baza është baseValue — kjo është vlera e çdo nodi tjetër që nuk është canary. Krahasoni vlerat me njëri-tjetrin sipas formulës që e vendosim sipas përvojës dhe vëzhgimeve tona. Nëse vlera canaryValue është e keqe, allora deploy nuk kishte sukses, dhe ne kthehemi mbrapsht.

Pse është e nevojshme gjithë kjo?

Njeriu nuk mund të kontrollojë qindra dhe mijëra metrika, aq më shumë për ta bërë këtë shpejt. Krahasimi automatizuar ndihmon për të kontrolluar të gjitha metrikat dhe paralajmëron shpejt për problemet. Koha e njoftimit është kritike: nëse ndodhi diçka në dy sekondat e fundit, dëmi do të jetë më i vogël se sa nëse do të kishte ndodhur 15 minuta më parë. Ndërsa dikush do të vërejtë problemin, do të shkruajë në mbështetje, dhe mbështetja na kërkon që të rikthejmë, mund të humbasim klientë.

Nëse procesi kaloi dhe gjithçka ishte në rregull, ne automatizojmë deploy-n e të gjitha nodëve të tjera. Në këtë kohë, inxhinierët nuk bëjnë asgjë. Vetëm kur ata vendosin të lëshojnë canary, vendosin se cilat metrika të marrin, sa kohë të bëjnë krahasimin, cila strategji të përdorin.

Testimi në prodhim: Canary Deployment

Nëse ndodhin probleme — ne automatikisht rikthejmë nodën canary, punojmë me versionet e mëparshme dhe korrigjojmë gabimet që janë gjetur. Përmes metrikeve është e lehtë t'i gjejmë dhe të shohim dëmin nga versioni i ri.

Pengesat

Të realizosh këtë, sigurisht, nuk është e lehtë. Në radhë të parë, nevojitet një sistem i përgjithshëm monitorimi. Inxhinierët kanë metrikat e tyre, mbështetja dhe analistët kanë të tjera, biznesi ka të tretat. Një sistem i përbashkët është gjuha e përbashkët me të cilën flasin biznesi dhe zhvillimi.

Duhet ta kontrollojmë në praktikë stabiliteti i metrikeve. Kontrolli ndihmon për të kuptuar, cilat janë metrika minimale që nevojiten për të siguruar cilësinë.

Si ta arrijmë këtë? Të përdorim shërbimin canary jo në momentin e publikimit. Shtojmë në versionin e vjetër një shërbim që në çdo moment mund të marrë ndonjë nod të ndarë, të ulë trafikun pa publikuar. Më pas bëjmë krahasime: studiojmë gabimet dhe kërkojmë atë kufi kur arrijmë cilësinë.

Testimi në prodhim: Canary Deployment

Çfarë dobi kemi marrë nga publikimet canary

Minimizuam përqindjen e dëmit nga gabimet. Shumica e gabimeve të publikimit ndodhin për shkak të moskonsistencës së ndonjë të dhëne ose prioritizimi. Këto gabime janë reduktuar ndjeshëm, sepse mund të zgjidhim problemin në sekonda të para.

Optimizuam punën e ekipeve. Një fillestar ka "të drejtën për të bërë gabim": ata mund të publikojnë në prodhim pa frikë për të gabuar, krijohet një iniciativë shtesë dhe një motivim për të punuar. Nëse ata dështojnë, atëherë kjo nuk do të jetë kritike, dhe ata nuk do të shkarkohen.

Automatizuam publikimin. Ky nuk është më një proces manual, si më parë, por një proces i vërtetë i automatizuar. Por zgjat më shumë.

Ndarë metrikat e rëndësishme. E gjithë kompania, duke filluar nga biznesi dhe inxhinierët, kupton se çfarë është me të vërtetë e rëndësishme në produktin tonë, cilat janë metrikat, për shembull, humbja dhe fitimi i përdoruesve. Ne kontrollojmë procesin: testojmë metrikat, shtojmë të reja, shohim se si funksionojnë të vjetra, për të ndërtuar një sistem që do të prodhojë para më mirë.

Kemi shumë praktika dhe sisteme të shkëlqyera që na ndihmojnë. Megjithatë, ne përpiqemi të jemi profesionistë dhe të bëjmë punën tonë me cilësi, pavarësisht nëse kemi një sistem që na ndihmon apo jo.

Qasjet dhe praktikat inxhinierike — fokusi kryesor i konferencës TechLead Conf. Nëse keni arritur suksese në rrugën drejt përsosmërisë teknike dhe jeni gati të tregoni se çfarë ju ndihmoi në këtë, — dërgoni një aplikim për të folur.

Planifikojmë të organizojmë TechLead Conf 8 qershor. Ne kuptojmë se tani është e vështirë të merrni vendime për pjesëmarrjen në konferencë. Por, në të njëjtën kohë, mendojmë se karantina nuk është arsye për të ndalur komunikimin profesional dhe zhvillimin. Prandaj, në çdo rast do të gjejmë mënyrën për të diskutuar sfidat e liderit teknik dhe qasjet për t'i zgjidhur ato — nëse është e nevojshme, do të kalojmë në online dhe do të zhvillojmë networking atje!

Burimi: habr.com

Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë 🔥 Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë | ProHoster