A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

A 2019-es RIT-en Alexander Korotkov kollégánk készített jelentés a fejlesztés automatizálásáról a CIAN-nál: az élet és a munka egyszerűsítése érdekében saját Integro platformunkat használjuk. Nyomon követi a feladatok életciklusát, mentesíti a fejlesztőket a rutinműveletek alól, és jelentősen csökkenti a hibák számát a gyártásban. Ebben a bejegyzésben Alexander beszámolóját fogjuk kiegészíteni, és elmondjuk, hogyan jutottunk el az egyszerű szkriptektől a nyílt forráskódú termékek saját platformon keresztüli kombinálásáig, és mit csinál külön automatizálási csapatunk.
 

Nulla szint

"Nincs olyan, hogy nulla szint, én nem ismerek ilyet"
Shifu mester a "Kung Fu Panda" című filmből

A CIAN automatizálása 14 évvel a cég alapítása után kezdődött. Ekkor 35 fő volt a fejlesztőcsapatban. Nehéz elhinni, igaz? Természetesen az automatizálás bizonyos formában létezett, de 2015-ben kezdett kirajzolódni egy külön irány a folyamatos integrációra és a kódszállításra. 

Abban az időben hatalmas Python, C# és PHP monolittal rendelkeztünk, Linux/Windows szervereken telepítve. Ennek a szörnyetegnek a telepítéséhez volt egy sor szkriptünk, amelyet manuálisan futtattunk. Megtörtént a monolit összeszerelése is, amely fájdalmat és szenvedést hozott az ágak összevonása, a hibák kijavítása és az újjáépítés „más feladatsorral az építésben” konfliktusaiból. Egy egyszerűsített folyamat így nézett ki:

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

Nem voltunk elégedettek ezzel, és egy megismételhető, automatizált és felügyelhető összeállítási és telepítési folyamatot akartunk építeni. Ehhez szükségünk volt egy CI/CD rendszerre, és a Teamcity ingyenes verziója és a Jenkins ingyenes verziója között választottunk, mivel ezekkel dolgoztunk, és mindkettő megfelelt nekünk a funkciókészletet tekintve. Újabb termékként a Teamcityt választottuk. Ekkor még nem használtunk mikroszolgáltatási architektúrát, és nem számítottunk nagy számú feladatra és projektre.

Eljutunk a saját rendszerünk gondolatához

A Teamcity megvalósítása a manuális munkának csak egy részét szüntette meg: maradt a Pull Requests létrehozása, a problémák státusz szerinti promóciója a Jira-ban és a kiadások kiválasztása. A Teamcity rendszere ezzel már nem tudott megbirkózni. A további automatizálás útját kellett választani. Megfontoltuk a szkriptekkel való munkavégzés lehetőségeit a Teamcityben vagy a harmadik féltől származó automatizálási rendszerekre való átállást. De végül úgy döntöttünk, hogy maximális rugalmasságra van szükségünk, amit csak a saját megoldásunk tud biztosítani. Így jelent meg a belső automatizálási rendszer első verziója, az Integro.

A Teamcity az építési és telepítési folyamatok elindításának szintjén foglalkozik az automatizálással, míg az Integro a fejlesztési folyamatok legmagasabb szintű automatizálására összpontosított. A Jira problémáival kapcsolatos munkát össze kellett kapcsolni a kapcsolódó forráskód Bitbucketben való feldolgozásával. Ebben a szakaszban az Integro elkezdett saját munkafolyamatokkal dolgozni a különböző típusú feladatokhoz. 

Az üzleti folyamatok automatizálásának növekedése miatt a Teamcityben megnőtt a projektek és futások száma. Így jött egy új probléma: egy ingyenes Teamcity példány nem volt elég (3 ügynök és 100 projekt), hozzáadtunk még egy példányt (3 további ügynök és 100 projekt), majd még egyet. Ennek eredményeként egy több klaszterből álló rendszerhez jutottunk, amelyet nehéz volt kezelni:

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

Amikor felmerült a 4. eset kérdése, rájöttünk, hogy így nem lehet tovább élni, mert 4 eset támogatásának összköltsége már nincs határon belül. Felmerült a kérdés a fizetős Teamcity megvásárlásával vagy az ingyenes Jenkins kiválasztásával kapcsolatban. Számításokat végeztünk a példányokról és az automatizálási tervekről, és úgy döntöttünk, hogy Jenkinsen élünk. Néhány hét után átváltottunk a Jenkinsre, és megszüntettük a több Teamcity-példány karbantartásával kapcsolatos fejfájást. Ezért az Integro fejlesztésére és a Jenkins saját testreszabására összpontosíthattunk.

Az alapvető automatizálás növekedésével (a Pull Requests automatikus létrehozása, a kódlefedettség és egyéb ellenőrzések összegyűjtése és közzététele formájában) erős a vágy, hogy a lehető legnagyobb mértékben elhagyják a kézi kiadásokat, és ezt a munkát a robotoknak adják. Emellett a cég elkezdett áttérni a mikroszolgáltatásokra a cégen belül, ami gyakori kiadást igényelt, és egymástól elkülönítve. Így jutottunk el fokozatosan a mikroszolgáltatásaink automatikus kiadásáig (a folyamat bonyolultsága miatt jelenleg manuálisan adjuk ki a monolitot). De ahogy az lenni szokott, új bonyodalmak merültek fel. 

A tesztelést automatizáljuk

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

A kiadások automatizálása miatt felgyorsultak a fejlesztési folyamatok, részben egyes tesztelési szakaszok kihagyása miatt. Ez pedig átmeneti minőségromláshoz vezetett. Triviálisan hangzik, de a megjelenések felgyorsulásával együtt szükség volt a termékfejlesztési módszertan megváltoztatására. Gondolkodni kellett a tesztelés automatizálásán, a fejlesztő személyes felelősségvállalásán (itt „az ötlet elfogadásáról van szó, nem pénzbírságról”) a kiadott kódért és a benne lévő hibákért, valamint a feladat felszabadítása/nem felszabadítása automatikus telepítéssel. 

A minőségi problémákat kiküszöbölve két fontos döntésre jutottunk: elkezdtük a kanári tesztelést, és bevezettük a hibaháttér automatikus monitorozását a túllépésre való automatikus reagálással. Az első megoldás lehetővé tette a nyilvánvaló hibák megtalálását még a kód teljes gyártásba kerülése előtt, a második pedig csökkentette a válaszidőt a termelési problémákra. Hibák persze előfordulnak, de időnk és erőfeszítésünk nagy részét nem a kijavításukkal, hanem a minimalizálásukkal töltjük. 

Automatizálási csapat

Jelenleg 130 fejlesztőből állunk, és folytatjuk . A folyamatos integrációt és kódszállítást végző csapat (továbbiakban Deploy and Integration vagy DI csapat) 7 főből áll, és 2 irányban működik: az Integro automatizálási platform és a DevOps fejlesztése. 

A DevOps felelős a CIAN webhely Dev/Beta környezetéért, az Integro környezetért, segíti a fejlesztőket a problémák megoldásában, és új megközelítéseket dolgoz ki a környezetek méretezésére. Az Integro fejlesztési iránya magával az Integróval és a kapcsolódó szolgáltatásokkal is foglalkozik, például a Jenkins, Jira, Confluence bővítményeivel, valamint segédprogramokat és alkalmazásokat fejleszt a fejlesztőcsapatok számára. 

A DI csapat együttműködik a Platform csapattal, amely belsőleg fejleszti az architektúrát, a könyvtárakat és a fejlesztési megközelítéseket. Ugyanakkor a CIAN bármely fejlesztője hozzájárulhat az automatizáláshoz, például mikroautomatizálást készíthet a csapat igényeinek megfelelően, vagy megoszthat egy jó ötletet, hogyan teheti még jobbá az automatizálást.

Az automatizálás réteg tortája a CIAN-nál

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

Az automatizálásban részt vevő összes rendszer több rétegre osztható:

  1. Külső rendszerek (Jira, Bitbucket stb.). Fejlesztői csapatok dolgoznak velük.
  2. Integro platform. Leggyakrabban a fejlesztők nem közvetlenül dolgoznak vele, de ez az, ami az összes automatizálást működésben tartja.
  3. Szállítási, hangszerelési és felfedezési szolgáltatások (például Jeknins, Consul, Nomad). Segítségükkel kódot telepítünk a szerverekre, és biztosítjuk, hogy a szolgáltatások együttműködjenek egymással.
  4. Fizikai réteg (szerverek, operációs rendszer, kapcsolódó szoftverek). Kódunk ezen a szinten működik. Ez lehet fizikai vagy virtuális szerver (LXC, KVM, Docker).

E koncepció alapján a DI csapaton belül felosztjuk a felelősségi területeket. Az első két szint az Integro fejlesztési irány felelősségi körébe tartozik, az utolsó két szint pedig már a DevOps felelősségi körébe tartozik. Ez az elkülönülés lehetővé teszi számunkra, hogy a feladatokra összpontosítsunk, és nem zavarja az interakciót, hiszen közel vagyunk egymáshoz, és folyamatosan cseréljük tudásunkat és tapasztalatainkat.

Teljes

Koncentráljunk az Integróra, és kezdjük a technológiai köteggel:

  • CentOs 7
  • Docker + Nomád + Konzul + Vault
  • Java 11 (a régi Integro monolit a Java 8-on marad)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • Nyúl MQ 
  • Apache Ignite
  • Camunda (beágyazott)
  • Grafana + Grafit + Prometheus + Jaeger + ELK
  • Webes felhasználói felület: React (CSR) + MobX
  • SSO: Kulcsköpeny

Ragaszkodunk a mikroszolgáltatások fejlesztésének elvéhez, bár örökségünk van az Integro korai verziójának monolitja formájában. Minden mikroszolgáltatás a saját Docker-tárolójában fut, és a szolgáltatások HTTP-kérésekkel és RabbitMQ-üzenetekkel kommunikálnak egymással. A mikroszolgáltatások a Consulon keresztül találják meg egymást, és kérelmet intéznek hozzá, az SSO-n (Keycloak, OAuth 2/OpenID Connect) keresztül továbbítva az engedélyt.

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

Valós példaként vegyük fontolóra a Jenkins-szel való interakciót, amely a következő lépésekből áll:

  1. A munkafolyamat-kezelési mikroszolgáltatás (a továbbiakban: Flow mikroszolgáltatás) egy buildet szeretne futtatni a Jenkinsben. Ehhez a Consul segítségével megkeresi a Jenkins-szel való integrációhoz szükséges mikroszolgáltatás IP:PORT-címét (a továbbiakban: Jenkins mikroszolgáltatás), és aszinkron kérést küld neki, hogy indítsa el a Jenkins-ben való felépítést.
  2. A kérés beérkezése után a Jenkins mikroszolgáltatás generál és válaszol egy Job ID-vel, amely segítségével azonosítható a munka eredménye. Ugyanakkor egy REST API-híváson keresztül elindítja a Jenkins buildjét.
  3. Jenkins elvégzi az összeállítást, és a befejezés után elküld egy webhookot a végrehajtás eredményeivel a Jenkins mikroszolgáltatásnak.
  4. A Jenkins mikroszolgáltatás, miután megkapta a webhookot, üzenetet generál a kérésfeldolgozás befejezéséről, és ehhez csatolja a végrehajtási eredményeket. A generált üzenet a RabbitMQ sorba kerül.
  5. A RabbitMQ-n keresztül a közzétett üzenet eljut a Flow mikroszolgáltatáshoz, amely a kérésből és a kapott üzenetből származó Job ID egyeztetésével értesül a feladat feldolgozásának eredményéről.

Jelenleg körülbelül 30 mikroszolgáltatásunk van, amelyek több csoportra oszthatók:

  1. Konfiguráció-menedzsment.
  2. Információ és interakció a felhasználókkal (üzenetküldők, levél).
  3. Munka a forráskóddal.
  4. Integráció telepítési eszközökkel (jenkins, nomad, consul stb.).
  5. Monitoring (kiadások, hibák stb.).
  6. Webes segédprogramok (UI tesztkörnyezetek kezelésére, statisztikák gyűjtésére stb.).
  7. Integráció feladatkövetőkkel és hasonló rendszerekkel.
  8. Munkafolyamat-kezelés különböző feladatokhoz.

Munkafolyamat-feladatok

Az Integro automatizálja a feladat életciklusához kapcsolódó tevékenységeket. Leegyszerűsítve egy feladat életciklusa egy feladat munkafolyamataként fogható fel a Jira-ban. Fejlesztési folyamatainknak számos munkafolyamat-változata van a projekttől, a feladat típusától és az adott feladatban kiválasztott opcióktól függően. 

Nézzük meg az általunk leggyakrabban használt munkafolyamatot:

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

Az ábrán a fogaskerék jelzi, hogy az Integro automatikusan hívja az átmenetet, míg az emberi alak azt jelzi, hogy egy személy manuálisan hívja meg az átmenetet. Tekintsünk több utat, amelyen egy feladat ebben a munkafolyamatban járhat.

Teljesen manuális tesztelés DEV+BETA-n kanári tesztek nélkül (általában így adunk ki egy monolitot):

A szkriptektől a saját platformunkig: hogyan automatizáltuk a fejlesztést a CIAN-nál

Lehetnek más átmeneti kombinációk is. Néha a probléma elérési utat a Jira beállításaival lehet kiválasztani.

Feladat mozgás

Nézzük meg azokat a fő lépéseket, amelyeket akkor hajtanak végre, amikor egy feladat áthalad a „DEV-tesztelés + Kanári tesztek” munkafolyamaton:

1. A fejlesztő vagy a PM létrehozza a feladatot.

2. A fejlesztő munkához látja a feladatot. A befejezés után átvált IN REVIEW állapotba.

3. A Jira webhookot küld a Jira mikroszolgáltatásnak (amely a Jira-val való integrációért felelős).

4. A Jira mikroszolgáltatás kérelmet küld a Flow szolgáltatásnak (amely a munkavégzés belső munkafolyamataiért felelős) a munkafolyamat elindítására.

5. A Flow szolgáltatáson belül:

  • A felülvizsgálók hozzá vannak rendelve a feladathoz (Users mikroszolgáltatás, amely mindent tud a felhasználókról + Jira mikroszolgáltatás).
  • A Forrás mikroszolgáltatáson keresztül (tud a repositóriumokról és elágazásokról, de magával a kóddal nem működik) olyan adattárakat keresnek, amelyek a kérdésünk egy ágát tartalmazzák (a keresés egyszerűsítése érdekében az ág neve egybeesik a problémával szám Jira-ban). Leggyakrabban egy feladatnak csak egy ága van egy lerakatban; ez leegyszerűsíti a telepítési sor kezelését és csökkenti a lerakatok közötti kapcsolatot.
  • Minden egyes talált ág esetében a következő műveletsor kerül végrehajtásra:

    i) A fő ág frissítése (Git mikroszolgáltatás a kóddal való munkához).
    ii) Az ágat a fejlesztő letiltja a változtatásoktól (Bitbucket mikroszolgáltatás).
    iii) Lehívási kérelem jön létre ehhez az ághoz (Bitbucket mikroszolgáltatás).
    iv) Egy új Pull Request-ről szóló üzenetet küldenek a fejlesztői csevegésekre (Notify microservice az értesítésekkel való munkavégzéshez).
    v) Az építési, tesztelési és üzembe helyezési feladatok elindulnak a DEV-n (Jenkins mikroszolgáltatás a Jenkins-szel való együttműködéshez).
    vi) Ha az összes előző lépést sikeresen végrehajtotta, akkor az Integro elhelyezi a jóváhagyást a Pull Request-ben (Bitbucket mikroszolgáltatás).

  • Az Integro a jóváhagyási kérelmet várja a kijelölt ellenőröktől.
  • Amint az összes szükséges jóváhagyás megérkezett (beleértve az automatizált teszteket is, amelyek pozitív eredménnyel zárultak), az Integro áthelyezi a feladatot Test on Dev (Jira mikroszolgáltatás) állapotba.

6. Tesztelők tesztelik a feladatot. Ha nincs probléma, akkor a feladat átkerül a Ready For Build állapotba.

7. Az Integro „látja”, hogy a feladat készen áll a kiadásra, és elindítja a telepítését kanári módban (Jenkins mikroszolgáltatás). Az elengedésre való felkészültséget egy sor szabály határozza meg. Például a feladat a szükséges állapotban van, nincsenek zárolások más feladatoknál, jelenleg nincs aktív feltöltés ebből a mikroszolgáltatásból stb.

8. A feladat átkerül Canary státuszba (Jira mikroszolgáltatás).

9. Jenkins elindít egy telepítési feladatot a Nomadon keresztül Canary módban (általában 1-3 példány), és értesíti a kiadásfigyelő szolgáltatást (DeployWatch mikroszolgáltatás) a telepítésről.

10. A DeployWatch mikroszolgáltatás összegyűjti a hiba hátterét, és szükség esetén reagál rá. A hibaháttér túllépése esetén (a háttérnorma automatikusan számítódik), a fejlesztők értesítést kapnak a Notify mikroszolgáltatáson keresztül. Ha 5 perc elteltével a fejlesztő nem válaszol (kattintott a Visszaállítás vagy a Stay gombra), akkor elindul a kanári példányok automatikus visszaállítása. Ha nem lépi túl a hátteret, akkor a fejlesztőnek manuálisan kell elindítania a feladat üzembe helyezését az éles verzióban (a felhasználói felület egy gombjára kattintva). Ha a fejlesztő 60 percen belül nem indította el az üzembe helyezést az éles verzióban, akkor biztonsági okokból a Canary példányokat is visszaállítjuk.

11. Az éles üzembe helyezés elindítása után:

  • A feladat átkerül Gyártási állapotba (Jira mikroszolgáltatás).
  • A Jenkins mikroszolgáltatás elindítja a telepítési folyamatot, és értesíti a DeployWatch mikroszolgáltatást a telepítésről.
  • A DeployWatch mikroszolgáltatás ellenőrzi, hogy az összes tároló frissült-e az éles verzióban (volt, hogy nem mindegyik frissítve lett).
  • A Notify mikroszolgáltatáson keresztül az üzembe helyezés eredményéről értesítést küldenek a termelésnek.

12. A fejlesztőknek 30 percük van arra, hogy megkezdjék a feladat visszaállítását a gyártásból, ha helytelen mikroszolgáltatási viselkedést észlelnek. Ezen idő letelte után a feladat automatikusan egyesítésre kerül a masterbe (Git mikroszolgáltatás).

13. A masterbe történő sikeres egyesülés után a feladat állapota Lezárt (Jira mikroszolgáltatás) értékre változik.

A diagram nem avatkozik teljesen részletességre (a valóságban még több lépés van), de lehetővé teszi a folyamatokba való integráltság mértékének felmérését. Nem tartjuk ideálisnak ezt a sémát, és fejlesztjük az automatikus kiadás és a telepítés támogatásának folyamatait.

Mi a következő

Nagy terveink vannak az automatizálás fejlesztésével kapcsolatban, például a kézi műveletek megszüntetése a monolit kiadások során, a felügyelet javítása az automatikus telepítés során, valamint a fejlesztőkkel való interakció javítása.

De most itt álljunk meg. Az automatizálási áttekintésben sok témát felületesen érintettünk, néhányat egyáltalán nem érintettünk, ezért szívesen válaszolunk a kérdésekre. Javaslatokat várunk, hogy mivel foglalkozzunk részletesen, írjátok meg kommentben.

Forrás: will.com

Hozzászólás