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 të Python, C# dhe PHP, të vendosur në serverët Linux/Windows. Për të vendosur këtë përbindësh, ne kishim një grup skriptesh që i ekzekutuam manualisht. Kishte gjithashtu montimin e monolitit, i cili solli dhimbje dhe vuajtje për shkak të konflikteve gjatë bashkimit të degëve, korrigjimit të defekteve dhe rindërtimit "me një grup të ndryshëm detyrash në ndërtim". Një proces i thjeshtuar 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

Shto një koment