Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Na RIT 2019 vyrobil náš kolega Alexander Korotkov zpráva o automatizaci vývoje ve společnosti CIAN: pro zjednodušení života a práce používáme vlastní platformu Integro. Sleduje životní cyklus úkolů, zbavuje vývojáře rutinních operací a výrazně snižuje počet chyb ve výrobě. V tomto příspěvku doplníme Alexandrovu zprávu a řekneme vám, jak jsme přešli od jednoduchých skriptů ke kombinování open source produktů prostřednictvím naší vlastní platformy a co dělá náš samostatný automatizační tým.
 

Úroveň nula

"Neexistuje nic takového jako nulová úroveň, nic takového neznám"
Mistr Shifu z filmu "Kung Fu Panda"

Automatizace ve společnosti CIAN začala 14 let po založení společnosti. V té době bylo ve vývojovém týmu 35 lidí. Těžko uvěřit, že? Automatizace samozřejmě v nějaké podobě existovala, ale v roce 2015 se začal formovat samostatný směr pro nepřetržitou integraci a doručování kódu. 

V té době jsme měli obrovský monolit Pythonu, C# a PHP nasazený na serverech Linux/Windows. K nasazení tohoto monstra jsme měli sadu skriptů, které jsme spouštěli ručně. Došlo také na montáž monolitu, která přinášela bolest a utrpení kvůli konfliktům při slučování větví, opravách defektů a přestavbě „s jinou sadou úkolů ve stavbě“. Zjednodušený proces vypadal takto:

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Nebyli jsme s tím spokojeni a chtěli jsme vytvořit opakovatelný, automatizovaný a spravovatelný proces sestavení a nasazení. K tomu jsme potřebovali CI/CD systém a vybírali jsme mezi bezplatnou verzí Teamcity a bezplatnou verzí Jenkins, protože jsme s nimi pracovali a obě nám vyhovovaly z hlediska sady funkcí. Jako novější produkt jsme zvolili Teamcity. V té době jsme ještě nepoužívali mikroservisní architekturu a neočekávali velké množství úkolů a projektů.

Dostáváme se k myšlence vlastního systému

Implementace Teamcity odstranila pouze část manuální práce: co zůstalo, je vytváření Pull Requests, podpora problémů podle stavu v Jira a výběr problémů k vydání. Systém Teamcity už si s tím nedokázal poradit. Bylo nutné zvolit cestu další automatizace. Zvažovali jsme možnosti práce se skripty v Teamcity nebo přechod na automatizační systémy třetích stran. Nakonec jsme se ale rozhodli, že potřebujeme maximální flexibilitu, kterou může poskytnout pouze naše vlastní řešení. Tak se objevila první verze vnitřního automatizačního systému s názvem Integro.

Teamcity se zabývá automatizací na úrovni spouštění procesů build a deployment, zatímco Integro se zaměřilo na špičkovou automatizaci vývojových procesů. Bylo nutné spojit práci s problematikou v Jira se zpracováním souvisejícího zdrojového kódu v Bitbucketu. V této fázi začalo Integro mít vlastní pracovní postupy pro práci s úkoly různého typu. 

Vzhledem k nárůstu automatizace obchodních procesů se zvýšil počet projektů a běhů v Teamcity. Přišel tedy nový problém: jedna bezplatná instance Teamcity nestačila (3 agenti a 100 projektů), přidali jsme další instanci (3 další agenti a 100 projektů), pak další. V důsledku toho jsme skončili se systémem několika clusterů, který bylo obtížné spravovat:

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Když vyvstala otázka 4. instance, uvědomili jsme si, že takto nemůžeme dál žít, protože celkové náklady na podporu 4 instancí už nebyly v žádném limitu. Vyvstala otázka ohledně nákupu placeného Teamcity nebo výběru bezplatného Jenkins. Provedli jsme výpočty instancí a plánů automatizace a rozhodli jsme se, že budeme žít na Jenkins. Po několika týdnech jsme přešli na Jenkins a odstranili jsme některé bolesti hlavy spojené s údržbou více instancí Teamcity. Proto jsme se mohli soustředit na vývoj Integro a přizpůsobení Jenkins pro sebe.

S růstem základní automatizace (v podobě automatického vytváření Pull Requests, shromažďování a zveřejňování pokrytí kódu a dalších kontrol) existuje silná touha co nejvíce opustit ruční uvolňování a dát tuto práci robotům. Společnost navíc začala v rámci společnosti přecházet na mikroslužby, které vyžadovaly časté vydávání a odděleně od sebe. Takto jsme postupně došli k automatickým releasům našich mikroslužeb (momentálně kvůli složitosti procesu uvolňujeme monolit ručně). Ale jak už to tak bývá, vyvstala nová složitost. 

Automatizujeme testování

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Díky automatizaci verzí se vývojové procesy zrychlily, částečně kvůli přeskakování některých testovacích fází. A to vedlo k dočasné ztrátě kvality. Zní to triviálně, ale spolu se zrychlováním releasů bylo nutné změnit metodiku vývoje produktu. Bylo nutné myslet na automatizaci testování, vštěpování osobní odpovědnosti (zde mluvíme o „přijetí myšlenky do hlavy“, nikoli peněžních pokutách) vývojáře za uvolněný kód a chyby v něm, stejně jako rozhodnutí uvolnit/neuvolnit úlohu prostřednictvím automatického nasazení. 

Odstraněním problémů s kvalitou jsme dospěli ke dvěma důležitým rozhodnutím: začali jsme provádět testování kanárů a zavedli jsme automatické sledování chybového pozadí s automatickou reakcí na jeho překročení. První řešení umožnilo najít zjevné chyby ještě před úplným uvolněním kódu do výroby, druhé zkrátilo dobu odezvy na problémy ve výrobě. Chyby se samozřejmě stávají, ale většinu času a úsilí nevěnujeme jejich nápravě, ale minimalizaci. 

Automatizační tým

V současné době zaměstnáváme 130 vývojářů a pokračujeme růst. Tým pro kontinuální integraci a doručování kódu (dále jen Deploy and Integration nebo DI tým) tvoří 7 lidí a pracuje ve 2 směrech: vývoj automatizační platformy Integro a DevOps. 

DevOps je zodpovědná za prostředí Dev/Beta webu CIAN, prostředí Integro, pomáhá vývojářům řešit problémy a vyvíjí nové přístupy ke škálování prostředí. Vývojový směr Integro se zabývá jak samotným Integrem, tak souvisejícími službami, například pluginy pro Jenkins, Jira, Confluence, a také vyvíjí pomocné utility a aplikace pro vývojové týmy. 

Tým DI spolupracuje s týmem Platform, který interně vyvíjí architekturu, knihovny a vývojové přístupy. Zároveň může jakýkoli vývojář v rámci CIAN přispět k automatizaci, například vytvořit mikroautomatizaci podle potřeb týmu nebo sdílet skvělý nápad, jak automatizaci ještě vylepšit.

Vrstvený koláč automatizace ve společnosti CIAN

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Všechny systémy zapojené do automatizace lze rozdělit do několika vrstev:

  1. Externí systémy (Jira, Bitbucket atd.). Pracují s nimi vývojové týmy.
  2. Platforma Integro. Nejčastěji s ním vývojáři nepracují přímo, ale je to to, co udržuje veškerou automatizaci v chodu.
  3. Doručovací, orchestrační a vyhledávací služby (například Jeknins, Consul, Nomad). S jejich pomocí nasazujeme kód na servery a zajišťujeme vzájemnou spolupráci služeb.
  4. Fyzická vrstva (servery, OS, související software). Náš kód funguje na této úrovni. Může se jednat o fyzický server nebo virtuální server (LXC, KVM, Docker).

Na základě tohoto konceptu rozdělujeme oblasti odpovědnosti v rámci DI týmu. První dvě úrovně jsou v oblasti odpovědnosti za směr vývoje Integro a poslední dvě úrovně jsou již v oblasti odpovědnosti DevOps. Toto oddělení nám umožňuje soustředit se na úkoly a nenarušuje interakci, protože jsme si blízcí a neustále si vyměňujeme znalosti a zkušenosti.

Neporušené

Zaměřme se na Integro a začněme s technologickým zásobníkem:

  • CentOS 7
  • Docker + Nomad + Consul + Vault
  • Java 11 (starý monolit Integro zůstane na Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • RabbitMQ 
  • Apache Ignite
  • Camunda (vložené)
  • Grafana + Grafit + Prometheus + Jaeger + ELK
  • Webové uživatelské rozhraní: React (CSR) + MobX
  • SSO: Klíčenka

Dodržujeme princip vývoje mikroslužeb, i když máme dědictví v podobě monolitu rané verze Integro. Každá mikroslužba běží ve vlastním kontejneru Docker a služby spolu komunikují prostřednictvím HTTP požadavků a zpráv RabbitMQ. Mikroslužby se navzájem najdou prostřednictvím služby Consul a podají mu žádost a předají autorizaci prostřednictvím jednotného přihlašování (Keycloak, OAuth 2/OpenID Connect).

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Jako příklad ze skutečného života zvažte interakci s Jenkinsem, která se skládá z následujících kroků:

  1. Mikroslužba pro správu workflow (dále jen mikroslužba Flow) chce spustit sestavení v Jenkins. K tomu pomocí Consul vyhledá IP:PORT mikroslužby pro integraci s Jenkins (dále jen mikroslužba Jenkins) a odešle jí asynchronní požadavek na spuštění sestavení v Jenkins.
  2. Po obdržení požadavku mikroslužba Jenkins vygeneruje a odpoví Job ID, které lze následně použít k identifikaci výsledku práce. Zároveň spouští sestavení v Jenkins prostřednictvím volání REST API.
  3. Jenkins provede sestavení a po dokončení odešle webhook s výsledky provedení mikroslužbě Jenkins.
  4. Mikroslužba Jenkins po přijetí webhooku vygeneruje zprávu o dokončení zpracování požadavku a připojí k ní výsledky provedení. Vygenerovaná zpráva je odeslána do fronty RabbitMQ.
  5. Prostřednictvím RabbitMQ se publikovaná zpráva dostane do mikroslužby Flow, která se o výsledku zpracování svého úkolu dozví tak, že porovná Job ID z požadavku a přijaté zprávy.

Nyní máme asi 30 mikroslužeb, které lze rozdělit do několika skupin:

  1. Správa konfigurace.
  2. Informace a interakce s uživateli (messengers, mail).
  3. Práce se zdrojovým kódem.
  4. Integrace s nástroji pro nasazení (jenkins, nomad, konzul atd.).
  5. Monitorování (uvolnění, chyby atd.).
  6. Webové nástroje (UI pro správu testovacích prostředí, sběr statistik atd.).
  7. Integrace s nástroji pro sledování úkolů a podobnými systémy.
  8. Správa workflow pro různé úkoly.

Úkoly pracovního postupu

Integro automatizuje činnosti související s životním cyklem úkolu. Zjednodušeně řečeno, životní cyklus úkolu bude chápán jako pracovní postup úkolu v Jira. Naše vývojové procesy mají několik variací pracovního postupu v závislosti na projektu, typu úkolu a možnostech vybraných v konkrétním úkolu. 

Podívejme se na pracovní postup, který používáme nejčastěji:

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

V diagramu ozubené kolo ukazuje, že přechod je volán automaticky Integro, zatímco lidská postava ukazuje, že přechod je volán ručně osobou. Podívejme se na několik cest, kterými se může úkol v tomto pracovním postupu ubírat.

Kompletně manuální testování na DEV+BETA bez kanárských testů (obvykle takto vydáváme monolit):

Od skriptů k naší vlastní platformě: jak jsme automatizovali vývoj ve společnosti CIAN

Mohou existovat i jiné kombinace přechodů. Někdy lze cestu, kterou se problém bude ubírat, vybrat pomocí voleb v Jira.

Pohyb úkolu

Podívejme se na hlavní kroky, které se provádějí, když úkol prochází pracovním postupem „DEV Testing + Canary Tests“:

1. Vývojář nebo PM vytvoří úlohu.

2. Vývojář vezme úkol do práce. Po dokončení se přepne do stavu IN REVIEW.

3. Jira odešle webhook mikroslužbě Jira (odpovědné za integraci s Jirou).

4. Mikroslužba Jira odešle službě Flow (odpovědné za interní pracovní postupy, ve kterých se práce provádí) požadavek na spuštění pracovního postupu.

5. Uvnitř služby Flow:

  • K úkolu jsou přiřazeni recenzenti (Uživatelská mikroslužba, která o uživatelích ví vše + mikroslužba Jira).
  • Přes mikroslužbu Source (ví o úložištích a větvích, ale nepracuje se samotným kódem) se vyhledávají repozitáře, které obsahují větev naší problematiky (pro zjednodušení hledání se název větve shoduje s problematikou číslo v Jira). Úloha má nejčastěji pouze jednu větev v jednom úložišti, což zjednodušuje správu fronty nasazení a snižuje konektivitu mezi repozitáři.
  • Pro každou nalezenou větev se provede následující sekvence akcí:

    i) Aktualizace hlavní větve (mikroslužba Git pro práci s kódem).
    ii) Pobočka je blokována před změnami vývojářem (mikroslužba Bitbucket).
    iii) Pro tuto pobočku (mikroslužba Bitbucket) je vytvořen požadavek Pull.
    iv) Zpráva o novém Pull Request je odeslána do vývojářských chatů (Notify microservice pro práci s upozorněními).
    v) Úlohy sestavení, testování a nasazení se spouštějí na DEV (mikroslužba Jenkins pro práci s Jenkinsem).
    vi) Pokud jsou všechny předchozí kroky úspěšně dokončeny, integro vloží svůj souhlas do žádosti o stažení (mikroslužba Bitbucket).

  • Integro čeká na schválení v žádosti o stažení od určených recenzentů.
  • Jakmile obdrží všechna potřebná schválení (včetně automatických testů, které prošly kladně), Integro převede úkol do stavu Test on Dev (mikroslužba Jira).

6. Testeři úkol otestují. Pokud nenastanou žádné problémy, úloha se přenese do stavu Připraveno k sestavení.

7. Integro „vidí“, že úloha je připravena k vydání, a zahájí její nasazení v kanárském režimu (Jenkins microservice). Připravenost k propuštění je určena souborem pravidel. Úkol je například v požadovaném stavu, u jiných úkolů nejsou žádné zámky, aktuálně nejsou žádné aktivní nahrávání této mikroslužby atd.

8. Úkol je převeden do stavu Kanárské ostrovy (mikroslužba Jira).

9. Jenkins spustí úlohu nasazení prostřednictvím Nomad v kanárském režimu (obvykle 1-3 instance) a o nasazení informuje službu sledování vydání (mikroslužba DeployWatch).

10. Mikroslužba DeployWatch shromažďuje pozadí chyby a v případě potřeby na něj reaguje. Pokud dojde k překročení chybového pozadí (norma pozadí se vypočítá automaticky), vývojáři jsou upozorněni prostřednictvím mikroslužby Notify. Pokud po 5 minutách vývojář neodpoví (klikne na Vrátit nebo Zůstat), spustí se automatické vrácení instancí kanárků. Pokud není překročeno pozadí, musí vývojář ručně spustit nasazení úlohy do produkce (kliknutím na tlačítko v uživatelském rozhraní). Pokud do 60 minut vývojář nespustí nasazení do produkčního prostředí, pak budou z bezpečnostních důvodů vráceny zpět i instance canary.

11. Po spuštění nasazení do produkce:

  • Úloha je převedena do stavu Výroba (mikroservis Jira).
  • Mikroslužba Jenkins zahájí proces nasazení a upozorní mikroslužbu DeployWatch o nasazení.
  • Mikroslužba DeployWatch kontroluje, zda byly aktualizovány všechny kontejnery v produkci (vyskytly se případy, kdy nebyly aktualizovány všechny).
  • Prostřednictvím mikroslužby Notify se do produkce odesílá upozornění o výsledcích nasazení.

12. Pokud je zjištěno nesprávné chování mikroslužeb, vývojáři budou mít 30 minut na to, aby zahájili vrácení úlohy z produkce. Po této době bude úloha automaticky sloučena do hlavní (mikroslužba Git).

13. Po úspěšném sloučení do hlavního se stav úlohy změní na Uzavřeno (mikroslužba Jira).

Diagram se netváří jako zcela detailní (ve skutečnosti je kroků ještě více), ale umožňuje posoudit míru integrace do procesů. Toto schéma nepovažujeme za ideální a vylepšujeme procesy podpory automatického uvolňování a nasazení.

Co je další

Máme velké plány na rozvoj automatizace, například odstranění manuálních operací během vydávání monolitů, zlepšení monitorování během automatického nasazení a zlepšení interakce s vývojáři.

Tady se ale zatím zastavme. Mnoha tématům jsme se v recenzi automatizace věnovali povrchně, některá se vůbec nedotkla, takže dotazy rádi zodpovíme. Čekáme na návrhy, co podrobně pokrýt, napište do komentářů.

Zdroj: www.habr.com

Přidat komentář