
Në RIT 2019, kolegu ynë Aleksandër Korotkov bëri 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:

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:

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

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Ă« . 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

Të gjitha sistemet e angazhuara në automatizim mund të ndahen në disa nivele:
- Sistemet e jashtme (Jira, Bitbucket etj.). Ekipet e zhvillimit punojnë me to.
- Platforma Integro. Zakonisht, zhvilluesit nuk punojnë drejtpërsëdrejti me të, por pikërisht ajo mbështet punën e gjithë automatizimit.
- 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.
- 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).

Si një shembull real, le të shqyrtojmë ndërveprimin me Jenkins, i cili përbëhet nga hapat e mëposhtëm:
- 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.
- 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.
- Jenkins realizon ndërtimin dhe pas përfundimit të tij dërgon një webhook me rezultatet e ekzekutimit në mikroshërbimin Jenkins.
- 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.
- 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:
- Menaxhimi i konfigurimeve.
- Informimi dhe ndërveprimi me përdoruesit (mesazherët, posta).
- Puna me kodin burimor.
- Integrimi me mjetet e shpërndarjes (jenkins, nomad, consul, etj.).
- Monitorimi (lëshimeve, gabimeve, etj.).
- Web-utilitarët (UI për menaxhimin e ambientëve të testimit, mbledhjen e statistikave, etj.).
- Integrimi me sistemet e gjurmimit të detyrave dhe sisteme të ngjashme.
- 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:

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):

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
