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ë Aleksandër Korotkov bëri raport për automatizimin e zhvillimit në CIAN: që të thjeshtojmë jetën dhe punën, ne përdorim platformën tonë të brendshme Integro. Ajo ndjek ciklin jetësor të detyrave, eliminon operacionet rutinë nga zhvilluesit dhe zvogëlon ndjeshëm numrin e defekteve në prodhim. Në këtë postim, ne do të plotësojmë referatin e Aleksandrit dhe do të tregojmë si kaluam nga skriptet e thjeshta në bashkimin e produkteve open source përmes platformës sonë dhe me çfarë merret një ekip i veçantë për automatizimin.
 

Niveli zero

"Niveli zero nuk ekziston, unë nuk e di një të tillë"
Mësuesi Shifu nga filmi "Kung Fu Panda"

Automatizimi në CIAN filloi pas 14 vjetësh nga themelimi i kompanisë. Atëherë ekipi i zhvillimit kishte 35 njerëz. E vështirë për t'u besuar, apo jo? Sigurisht, në njëfarë forme automatizimi tashmë ekzistonte, por një drejtim i veçantë për integrimin dhe shpërndarjen e vazhdueshme të kodit filloi të formohej pikërisht në vitin 2015. 

Në atë kohë ne kishim një monolit të madh nga Python, C# dhe PHP, i instaluar në serverat Linux/Windows. Për të bërë deploy të këtij monstër ne kishim një set skriptesh, që i aktivizonim manualisht. Ishte gjithashtu ndërtimi i monolitit, që sillte dhimbje dhe vuajtje për shkak të konflikteve gjatë bashkimit të degëve, rregullimit të defekteve dhe rin ndërtimit "me një set tjetër detyrash në ndërtim". Procesi dukej thjesht kështu:

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

Kjo nuk na kënaqte, dhe ne dëshironim të ndërtuonim një proces ndërtimi dhe shpërndarjeje të ripërsëritshëm, të automatizuar dhe të menaxhuar. Për këtë na duhej një sistem CI/CD, dhe ne zgjodhëm midis versionit falas të Teamcity dhe Jenkins të lirë, pasi kishim punuar me to dhe të dy na plotësonin me të njëjtat funksionalitete. Zgjodhëm Teamcity si produkt më të ri. Atëherë ne ende nuk kishim përdorur arkitekturën mikroshërbimesh dhe nuk prisnim shumë detyra dhe projekte.

Arrijmë te ideja për një sistem të brendshëm

Zgjedhja e Teamcity hoqi vetëm një pjesë të punës manuale: mbeti ende krijimi i Pull Request-eve, avancimi i detyrave sipas statusit në Jira, zgjedhja e detyrave për rrelease. Me këtë, sistemi Teamcity nuk munde më të përballojë. Duhej të zgjidheshin rrugët e mëtejshme të automatizimit. Ne shqyrtuam mundësitë e punës me skriptet në Teamcity ose kalimin në sisteme të jashtme të automatizimit. Por në fund, vendosëm se na duhej fleksibiliteti maksimal, të cilin e jep vetëm një zgjidhje e brendshme. Kështu u shfaq versioni i parë i sistemit të brendshëm të automatizimit të quajtur Integro.

Teamcity merret me automatizimin në nivelin e fillimit të proceseve të ndërtimit dhe dep_limit, ndërsa Integro është fokusuar në automatizimin e proceseve të zhvillimit në nivel më të lartë. Duhej të bashkonte punën me detyrat në Jira me përpunimin e kodit burimor të lidhur në Bitbucket. Në këtë fazë, brenda Integro filluan të shfaqen workflow të veta për të punuar me detyra të tipave të ndryshëm. 

Për shkak të rritjes së automatizimit në proceset e biznesit, numri i projekteve dhe run-eve në Teamcity u rrit. Kështu erdhi një problem i ri: një instancë falas e Teamcity nuk mjaftonte (3 agjentë dhe 100 projekte), ne shtuam një instancë tjetër (edhe 3 agjentë dhe 100 projekte), pastaj edhe një tjetër. Në fund morëm një sistem prej disa klasteresh, të cilin ishte e vështirë ta menaxhonim:

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

Kur doli në pyetje një instancë e katërt, ne kuptuam që nuk mund të jetonim më kështu, sepse shpenzimet totale për mbështetje e katër instancave nuk përballoheshin më. U hodh në diskutim blerja e një Teamcity me pagesë ose zgjedhja e Jenkins-it falas. Ne bëmë llogaritjet për instancat dhe planet për automatizimin dhe vendosëm që do të jetonim me Jenkins. Pasi kaluam disa javë, u kaluam në Jenkins dhe u liruar nga një pjesë e dhimbjes së kokës lidhur me mbështetjen e disa instancave të Teamcity. Kështu, arritëm të përqendrohemi në zhvillimin e Integro dhe përshtatjen e Jenkins sipas nevojave tona.

Me rritjen e automatizimit bazë (në formën e krijimit automatik të Pull Request-eve, grumbullimit dhe publikimit të Code coverage dhe kontrollesh të tjera) u shfaq një dëshirë e fortë për të minimizuar në maksimum lëshimet manuale dhe për t'i dhënë këtë punë robotëve. Përveç kësaj, brenda kompanisë filloi kalimi në mikroshërbime, të cilat kërkonin lëshime të shpeshta, dhe përveç kësaj, të ndara nga njëra-tjetra. Kështu ne gradualisht arritëm te lëshimet automatike të mikroshërbimeve tona (monoliti e lëshojmë akoma manualisht për shkak të kompleksitetit të procesit). Por, siç ndodh zakonisht, lindën vështirësi të reja. 

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 u përshpejtuan, pjesërisht përmes anashkalimit të disa fazave të testimit. Dhe kjo çoi në humbje të përkohshme të cilësisë. Duket banale, por së bashku me përshpejtimin e lëshimeve, duhej të ndryshonim edhe metodologjinë e zhvillimit të produktit. Duhej të mendonim për automatizimin e testimit, për forcimin e përgjegjësisë personale (këtu flasim për 'pranimin e ideve në mendje', dhe jo për gjoba financiare) të zhvilluesit për kodin e lëshuar dhe për gabitë që ndodhen në të, si dhe për një zgjidhje për lëshimin/nuk lëshimin e detyrave përmes automatizimeve të deploy. 

Duke eliminuar problemet me cilësinë, arritëm dy vendime të rëndësishme: filluam të bëjmë testim kanar dhe kemi implementuar monitorim automatik të gabimeve me reagim automatike në rast se kalon një nivel të caktuar. Vendimi i parë lehtësoi gjetjen e gabimeve evidente para se kodi të arrijë në production, ndërsa vendimi i dytë zvogëloi kohën e reagimit ndaj problemeve në production. Gabimet, natyrisht, ndodhin, por ne shpenzojmë shumicën e kohës dhe forcës jo për t'i korrigjuar ato, por për t'i minimizuar. 

Ekipi i automatizimit

Tani kemi njĂ« staf prej 130 zhvilluesish, dhe ne vazhdojmĂ« tĂ« rritemi. Ekipi pĂ«r integrimin dhe dorĂ«zimin e vazhdueshĂ«m tĂ« kodit (nĂ« vazhdim — ekipi Deploy and Integration ose DI) pĂ«rbĂ«het nga 7 veta dhe punon nĂ« 2 drejtime: zhvillimin e platformĂ«s sĂ« automatizimit Integro dhe DevOps. 

DevOps është përgjegjës për ambientet Dev/Beta të faqes CIAN, ambientet Integro, ndihmon zhvilluesit në zgjidhjen e problemeve dhe zhvillon qasje të reja për shkallëzimin e ambienteve. Direksioni i zhvillimit të Integro merret si me Integro-n vetë, ashtu edhe me shërbime të ngjashme, si për shembull me plugin-et për Jenkins, Jira, Confluence, dhe gjithashtu zhvillon mjete ndihmëse dhe aplikacione për ekipet e zhvilluesve. 

Ekipi DI punon së bashku me ekipin e Platformës, i cili merret me zhvillimin e arkitekturës, librarive dhe qasjeve të zhvillimit brenda kompanisë. Bashkë me këtë, çdo zhvillues brenda CIAN-it mund të kontribuojë në automatizim, për shembull, të bëjë mikroautomatik min për nevojat e ekipit ose të ndajë një ide interesante, si të bëjë automatizimin akoma më të mirë.

Tortë e shumëfishtë e automatizimit në CIAN

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

Të gjitha sistemet e angazhuara në automatizim mund të ndahen në disa nivele:

  1. Sistemet e jashtme (Jira, Bitbucket etj.). Ekipet e zhvillimit punojnë me to.
  2. Platforma Integro. Zakonisht, zhvilluesit nuk punojnë drejtpërsëdrejti me të, por pikërisht ajo mbështet punën e gjithë automatizimit.
  3. Shërbimet e dorëzimit, orkestrimit dhe zbulimit (për shembull, Jenkins, Consul, Nomad). Me to ne vendosim kodin në serverat dhe sigurojmë funksionimin e shërbimeve me njëra-tjetrën.
  4. Niveli fizik (servera, OS, softuer i ngjashëm). Në këtë nivel funksionon kodi ynë. Kjo mund të jetë një server fizik, ashtu si dhe një virtual (LXC, KVM, Docker).

Duke u bazuar në këtë koncept ne ndajmë zonat e përgjegjësive brenda ekipit DI. Dy nivelet e para bien në zonën e përgjegjësisë së drejtimeve të zhvillimit të Integro, ndërsa dy nivelet e fundit janë në zonën e përgjegjësisë të DevOps. Kjo ndarje lejon përqendrimin në detyra dhe nuk pengon bashkëpunimin, pasi jemi afër njëri-tjetrit dhe vazhdimisht shkëmbejmë dije dhe përvojë.

Integro

Të përqendrohemi në Integro dhe të fillojmë me tech stack-un:

  • CentOs 7
  • Docker + Nomad + Consul + Vault
  • Java 11 (monoliti i vjetĂ«r i Integro do tĂ« mbetet nĂ« Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • RabbitMQ 
  • Apache Ignite
  • Camunda (embedded)
  • Grafana + Graphite + Prometheus + Jaeger + ELK
  • Web UI: React (CSR) + MobX
  • SSO: Keycloak

Neveç t'i pĂ«rmbahemi parimit tĂ« zhvillimit mikroshĂ«rbimeve, megjithĂ«se ne kemi njĂ« trashĂ«gimi nĂ« formĂ«n e njĂ« monoliti nga versioni i hershĂ«m i Integro. Çdo mikroshĂ«rbim funksionon nĂ« kontejnerin e tij docker, shĂ«rbimet komunikojnĂ« me njĂ«ra-tjetrĂ«n pĂ«rmes kĂ«rkesave HTTP dhe mesazheve RabbitMQ. MikroshĂ«rbimet njĂ«soj e gjejnĂ« njĂ«ra-tjetrĂ«n pĂ«rmes Consul dhe iu dĂ«rgojnĂ« atij kĂ«rkesa, 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 real, le të shqyrtojmë ndërveprimin me Jenkins, i cili përbëhet nga hapat e mëposhtëm:

  1. Mikroshërbimi i menaxhimit të punëve (tani mikroshërbimi Flow) dëshiron të nisë një ndërtim në Jenkins. Për këtë, ai e gjen IP:PORT e mikroshërbimit të integrimit me Jenkins (tani mikroshërbimi Jenkins) përmes Consul dhe i dërgon një kërkesë asinkrone për të filluar ndërtimin në Jenkins.
  2. Mikroshërbimi Jenkins, pas marrjes së kërkesës, formon dhe kthen në përgjigje një ID pune, me të cilën më vonë do të mund të identifikohet rezultati i punës. Përveç kësaj, ai nis ndërtimin në Jenkins përmes thirrjes së REST API.
  3. Jenkins realizon ndërtimin dhe pas përfundimit të tij dërgon një webhook me rezultatet e ekzekutimit në mikroshërbimin Jenkins.
  4. Mikroservisi Jenkins, pasi merr webhook, formon një mesazh përfundimtar të përpunimit të kërkesës dhe e lidh me rezultatet e ekzekutimit. Mesazhi i formuar dërgohet në radhën RabbitMQ.
  5. Përmes RabbitMQ, mesazhi i publikuar arrin në mikroservisin Flow, i cili merr informacion mbi rezultatet e përpunimit të detyrës së tij duke përputhur ID-në e Punës nga kërkesa me mesazhin e marrë.

Aktualisht, kemi rreth 30 mikroservisarë, të cilët mund të ndahen në disa grupe:

  1. Menaxhimi i konfigurimeve.
  2. Informimi dhe ndërveprimi me përdoruesit (mesazherët, posta).
  3. Puna me kodin burimor.
  4. Integrimi me mjetet e shpërndarjes (jenkins, nomad, consul, etj.).
  5. Monitorimi (lëshimeve, gabimeve, etj.).
  6. Web-utilitarët (UI për menaxhimin e ambientëve të testimit, mbledhjen e statistikave, etj.).
  7. Integrimi me sistemet e gjurmimit të detyrave dhe sisteme të ngjashme.
  8. Menaxhimi i flukseve të punës për detyra të ndryshme.

Fluksi i punës i detyrës

Integro automatizon veprimet që lidhen me ciklin jetësor të detyrës. Në mënyrë të thjeshtë, nën ciklin jetësor të detyrës kuptojmë fluksin e punës të detyrës në Jira. Në proceset tona të zhvillimit, kemi disa variacione të flukseve të punës në varësi të projektit, llojit të detyrës dhe opsioneve të zgjedhura për detyrën përkatëse. 

Le të shqyrtojmë fluksin e punës që përdorim më shpesh:

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

Në skemë, ingranazhi tregon se kalimi ndodhi automatikisht nga Integro, ndërsa figura e njeriut tregon se kalimi u aktivizua manualisht nga një person. Le të shqyrtojmë disa rrugë, përmes të cilave një detyrë mund të kalojë në këtë fluks pune.

Testim plotësisht manual në DEV+BETA pa teste kanaribe (zakonisht kështu e lëshojmë monolitin):

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

Mund të ketë edhe kombinime të tjera të kalimeve. Ndonjëherë, rruga që do të ndjekë detyra mund të zgjidhet përmes opsioneve në Jira.

Lëvizja e detyrës

Të shqyrtojmë hapat kryesorë që ndiqen gjatë lëvizjes së detyrës në fluksin e punës "Testimi në DEV + testet kanaribe":

1. Zhvilluesi ose PM krijon një detyrë.

2. Zhvilluesi e merr në punë detyrën. Pas përfundimit, e kalon atë në statusin IN REVIEW.

3. Jira dërgon një Webhook drejt mikroservisit Jira (i cili përgjigjet për integrimin me Jira).

4. Mikroservisi Jira dërgon një kërkesë në shërbimin Flow (i cili është përgjegjës për flukset e brendshme të punës, ku bëhet puna) për të nisur fluksin e punës.

5. Brenda shërbimit Flow:

  • Caktohen rishikuesit pĂ«r detyrĂ«n (mikroservisi Users, i cili di gjithçka pĂ«r pĂ«rdoruesit + mikroservisi Jira).
  • PĂ«rmes mikroservisit Source (i cili di pĂ«r depozitĂ« dhe degĂ«, por nuk punon me kodin vetĂ«), bĂ«het kĂ«rkimi i depozitave nĂ« tĂ« cilat ndodhet dega e detyrĂ«s sonĂ« (pĂ«r tĂ« thjeshtuar kĂ«rkimin, emri i degĂ«s pĂ«rputhet me numrin e detyrĂ«s nĂ« Jira). Shpesh, detyra ka vetĂ«m njĂ« degĂ« nĂ« njĂ« depo tĂ« vetme, duke e thjeshtuar menaxhimin e radhĂ«s pĂ«r shpĂ«rndarje dhe duke reduktuar lidhshmĂ«rinĂ« midis depozitave.
  • PĂ«r çdo degĂ« tĂ« gjetur, ndiqet kjo radhĂ« veprimesh:

    i) Bashkimi i degës master (mikroservisi Git për punën me kodin).
    ii) Dega bllokohet nga ndryshimet nga zhvilluesi (mikroservisi Bitbucket).
    iii) Krijohet një Pull Request në këtë degë (mikroservisi Bitbucket).
    iv) Dërgohet një mesazh për Pull Requestin e ri në bisedat e zhvilluesve (mikroservisi Notify për punën me njoftimet).
    v) Nisin ndërtimin, testimin dhe shpërndarjen e detyrës në DEV (mikroservisi Jenkins për punën me Jenkins).
    vi) Nëse të gjithë hapat e mëparshëm përfundojnë me sukses, Integro vendos miratimin e tij në Pull Request (mikroservisi Bitbucket).

  • Integro pret miratimin nĂ« Pull Request nga rishikuesit e caktuar.
  • SapoharĂ« tĂ« gjitha miratimet e nevojshme (pĂ«rfshirĂ« testet automatizuar qĂ« janĂ« kaluar pozitivisht), Integro e kalon detyrĂ«n nĂ« statusin Test on Dev (mikroservisi Jira).

6. Testuesit kryejnë testimin e detyrës. Nëse nuk ka probleme, ata e kalojnë detyrën në statusin Ready For Build.

7. Integro "sheh" që detyra është gati për lëshim dhe e nis shpërndarjen në mënyrë kanaribe (mikroservisi Jenkins). Gatishmëria për lëshim përcaktohet nga një grup rregullash. Për shembull, detyra është në statusin e duhur, nuk ka bllokime për detyra të tjera, aktualisht nuk ka shpërndarje aktive të këtij mikroservisi, etj.

8. Detyra kalon në statusin Canary (mikroservisi Jira).

9. Jenkins nis shpërndarjen e detyrës në mënyrë kanaribe përmes Nomad (zakonisht 1-3 instanca) dhe njofton shërbimin e monitorimit të lëshimeve (mikroservisi DeployWatch).

10. DeployWatch-mikroservisi mbledh sfondin e gabimeve dhe reagon nëse është e nevojshme. Kur sfondi i gabimeve tejkalohet (norma e sfondit llogaritet automatikisht), zhvilluesit njoftohen përmes mikroservisit Notify. Nëse pas 5 minutash zhvilluesi nuk reagon (në njësi Revert ose Stay), fillon rikthimi automatik i instancave kanaric. Nëse sfondi nuk është tejkaluar, zhvilluesi duhet të niset manualisht procesin e deploy-t në Production (duke klikuar butonin në UI). Nëse brenda 60 minutash zhvilluesi nuk ka nisur deploy në Production, instancat kanaric do të rikthehen gjithashtu për qëllime sigurie.

11. Pas nisjes së deploy-t në Production:

  • Detyra kalon nĂ« statusin Production (mikroservisi Jira).
  • Mikroservisi Jenkins nis procesin e deploy-t dhe njofton pĂ«r hedhjen mikroservisin DeployWatch.
  • Mikroservisi DeployWatch kontrollon qĂ« tĂ« gjitha kontejnerĂ«t nĂ« Production janĂ« pĂ«rditĂ«suar (ka pasur raste kur nuk Ă«shtĂ« pĂ«rditĂ«suar gjithçka).
  • PĂ«rmes mikroservisit Notify dĂ«rgohet njĂ« njoftim mbi rezultatet e deploy-t nĂ« Production.

12. Zhvilluesit do të kenë 30 minuta për të nisur rikthimin e detyrës nga Production në rast se shfaqet një sjellje e gabuar e mikroservisit. Pas këtij afati, detyra do të përfshihet automatikisht në master (mikroservisi Git).

13. Pas një bashkëngjitjeje të suksesshme në master, statusi i detyrës do të ndryshohet në Closed (mikroservisi Jira).

Skema nuk pretendon të jetë komplekse (në realitet ka më shumë hapa), por lejon të vlerësohet shkalla e integrimit në proceset. Ne nuk e mendojmë këtë skemë perfekte dhe përmirësojmë proceset e mbështetjes automatike të rilësave dhe deploy-t.

ÇfarĂ« ndodh mĂ« pas

Kemi plane të mëdha për zhvillimin e automatizimit, për shembull, heqja e operacioneve manuale gjatë rilësave të monolit, përmirësimin e monitorimit gjatë deploy-t automatike, përmirësimin e bashkëpunimit me zhvilluesit.

Por për momentin do të ndalemi këtu. Shumë tema në përmbledhjen e automatizimit i kemi trajtuar sipërfaqësisht, disa nuk i kemi prekur fare, prandaj jemi të gatshëm të përgjigjemi në pyetje. Presim propozime për çfarë duhet të shqyrtojmë në detaje, shkruani në komente.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster