
A RIT 2019-en kollégánk, Alexander Korotkov tartott előadást a CIÁN fejlesztésének automatizálásáról: az élet és a munka megkönnyítése érdekében saját Integro platformunkat használjuk. Ez követi a feladatok életciklusát, leveszi a rutinszerű feladatokat a fejlesztőkről, és jelentősen csökkenti a production környezetben megjelenő hibák számát. Ebben a posztban kiegészítjük Alexander előadását, és elmondjuk, hogyan jutottunk el az egyszerű szkriptektől az open source termékek integrálásáig a saját platformunkon, és mivel foglalkozik egy külön automatizáló csapat.
Nulla szint
"Nulla szint nem létezik, ilyet nem ismerek"
Shifu mester a "Kung-fu Panda" című mesefilmben
A CIÁN-ban a fejlesztés automatizálása 14 évvel a cég megalapítása után indult. Akkoriban 35 fős fejlesztőcsapatunk volt. Nehezen hihető, ugye? Természetesen valamilyen formában az automatizálás korábban is létezett, de a folyamatos integráció és a kód szállításának önálló irányzata 2015-ben kezdett el kibontakozni.
Abban az időben egy hatalmas monolitunk volt, Python, C# és PHP nyelveken, Linux/Windows szervereken futtatva. Ehhez a szörnyeteghez egy kézi futtatású szkriptekből álló gyűjteményünk volt. Volt egy monolit összeállítás is, amely fájdalmakat és szenvedéseket okozott az ágazati ágazatok összeolvadásakor, a hibák javításakor és a "másik feladatkészlettel való újraépítéskor". Egyszerűen így nézett ki a folyamat:

Ez nem volt megfelelő számunkra, és egy ismételhető, automatizált és irányított építési és telepítési folyamatot akartunk létrehozni. Ehhez CI/CD rendszerre volt szükségünk, és a Teamcity ingyenes verziója és a Jenkins ingyenes verziója között választottunk, mivel mindkettővel dolgoztunk, és mindkettő megfelelt a funkciósorozatunknak. A Teamcityt választottuk, mint frissebb terméket. Akkor még nem használtunk mikroszolgáltatások architektúráját, és nem számítottunk nagy mennyiségű feladatra és projektre.
Eljutottunk a saját rendszerünk ötletéhez
A Teamcity bevezetése csak a kézi munka egy részét távolította el: még mindig szükség volt Pull Requestek létrehozására, a feladatok státuszának előrehaladására a Jira-ban, valamint a kiadásokhoz szükséges feladatok kiválasztására. Ezt a Teamcity rendszere már nem tudta kezelni. Ki kellett választani a további automatizálás irányát. Megfontoltuk a szkriptek használatát a Teamcity-ban, vagy a harmadik fél automatizáló rendszereire való áttérést. Végül úgy döntöttünk, hogy a maximális rugalmasságra van szükség, amelyet csak a saját megoldás nyújt. Így született meg az Integro nevű belső automatizáló rendszer első verziója.
A Teamcity a build és deploy folyamatok indításának automatizálásával foglalkozik, míg az Integro a fejlesztési folyamatok magasabb szintű automatizálására összpontosít. Össze kellett vonni a Jira-ban lévő feladatok kezelését a kapcsolódó forráskód Bitbucket-ben való feldolgozásával. Ezen a ponton belül az Integro saját workflow-kat kezdett kialakítani a különböző típusú feladatok kezelésére.
A vállalati folyamatok automatizálásának növekedése miatt megnőtt a Teamcity-ben lévő projektek és run-ok száma. Ez új problémát vetett fel: az ingyenes Teamcity instance már nem volt elegendő (3 ügynök és 100 projekt), ezért egy újabb instance-ot adtunk hozzá (még 3 ügynök és 100 projekt), majd még egyet. Végül egy több klaszterből álló rendszert kaptunk, amelynek kezelése nehézkessé vált.

Amikor a negyedik instance kérdése felmerült, rájöttünk, hogy így tovább nem lehet élni, mivel a négy instance fenntartásának összköltsége már nem volt elfogadható. Felmerült a kérdés, hogy vásároljunk-e fizetős Teamcity-t, vagy válasszuk a ingyenes Jenkins-t. Elvégeztük az instance-ok és a automatizálási tervek számításait, és úgy döntöttünk, hogy a Jenkins-t választjuk. Néhány hét elteltével áttértünk a Jenkins-re, és megszabadultunk a Teamcity több instance-ának fenntartásával járó fejfájások egy részétől. Így már a fejlesztésre és az Integro finomhangolására tudtunk összpontosítani, valamint a Jenkins testreszabására.
A bázisautomatizálás növekedésével (mint a Pull Requestek automatikus létrehozása, a code coverage gyűjtése és közzététele, valamint más ellenőrzések) erős vágy ébredt arra, hogy a lehető legnagyobb mértékben felhagyjunk a kézi kiadásokkal és átadjuk ezt a munkát a robotoknak. Ezenkívül a cégen belül elkezdődött a mikroservices-re való átállás, ami gyakori kiadásokat igényelt, ráadásul külön-külön. Így fokozatosan eljutottunk a mikroservices automatikus kiadásához (a monolitikus alkalmazást egyelőre manuálisan adjuk ki a folyamat bonyolultsága miatt). De, mint általában, új nehézségek merültek fel.
Automatizáljuk a tesztelést

A kiadások automatizálása felgyorsította a fejlesztési folyamatokat, részben néhány tesztelési szakasz kihagyása révén. Ez időszakos minőségromláshoz vezetett. Bár banálisnak hangzik, a kiadások felgyorsításával együtt a termékfejlesztési módszertanát is meg kellett változtatni. Gondolkodni kellett a tesztelés automatizálásáról, a fejlesztők személyes felelősségi körének megerősítéséről (itt a „gondolatban való elfogadás” a téma, nem pénzbírságok), valamint a feladatok kiadásának/ki nem adásának eldöntéséről automatikus telepítés útján.
A minőségi problémák megszüntetése érdekében két fontos döntésre jutottunk: canary tesztelést kezdtünk el végezni, és automatikus hibafonó-monitorozást vezettünk be automatikus reagálással a határok túllépése esetén. Az első döntés lehetővé tette a nyilvánvaló hibák megtalálását, mielőtt a kód teljesen bekerült volna a production környezetbe, a második csökkentette a problémákra való reagálás idejét a productionban. Hibák természetesen előfordulnak, de az időnk és energiánk nagyobb részét nem a kijavításra, hanem a minimalizálásra fordítjuk.
Automatizáló csapat
Jelenleg 130 fejlesztőből álló csapatunk van, és továbbra is . A folyamatos integrációért és a kód szállításáért felelős csapat (továbbiakban Deploy and Integration vagy DI csapat) 7 főből áll, és két irányban dolgozik: az Integro automatizálási platform fejlesztésén és a DevOps-on.
A DevOps válasza a CIAN webhely Dev/Beta környezetére, az Integro környezetre, segít a fejlesztőknek a problémák megoldásában és új megközelítéseket dolgoz ki a környezetek skálázásához. Az Integro fejlesztési terület az Integro-t és kapcsolódó szolgáltatásokat is érinti, például a Jenkins, Jira és Confluence bővítményeket, valamint segédprogramokat és alkalmazásokat fejleszt a fejlesztői csapatok számára.
A DI csapat együttműködik a Platform csapattal, amely a vállalaton belüli architektúra, könyvtárak és fejlesztési megközelítések kidolgozásával foglalkozik. Emellett bármely fejlesztő a CIAN-on belül hozzájárulhat az automatizáláshoz, például mikroautomatizálást végezhet a csapat igényeinek megfelelően, vagy megoszthatja egy szuper ötletet arról, hogyan lehetne még jobb az automatizálás.
Az automatizálás rétegzett tortája a CIAN-nál

Minden rendszer, amely az automatizálásban részt vesz, több rétegre osztható:
- Külső rendszerek (Jira, Bitbucket stb.). Ezeket a fejlesztői csapatok használják.
- Integro Platform. A fejlesztők általában nem dolgoznak vele közvetlenül, de ez támogatja az összes automatizálási működést.
- Szállítási, orkestrációs és felfedezési szolgáltatások (például Jenkins, Consul, Nomad). Ezek segítségével telepítjük a kódot a szervereken, és biztosítjuk, hogy a szolgáltatások működjenek egymással.
- Fizikai szint (szerverek, operációs rendszerek, kapcsolódó szoftverek). Ezen a szinten működik a kódunk. Ez lehet fizikai szerver vagy virtuális (LXC, KVM, Docker).
Ezekre a koncepciókra alapozva osztjuk fel a felelősségi köröket a DI csapaton belül. Az első két szint az Integro fejlesztési irányzatának felelősségi körébe tartozik, a két utolsó szint pedig a DevOps felelőssége. Ez a felosztás lehetővé teszi, hogy a feladatokra összpontosítsunk, és ne zavarja az együttműködést, mivel közel vagyunk egymáshoz, és folyamatosan megosztjuk a tudást és a tapasztalatokat.
Integro
Fókuszáljunk az Integro-ra, és kezdjük a technológiai stack-kel:
- CentOs 7
- Docker + Nomad + Consul + Vault
- Java 11 (a régi Integro monolit Java 8-on marad)
- Spring Boot 2.X + Spring Cloud Config
- PostgreSql 11
- RabbitMQ
- Apache Ignite
- Camunda (beágyazott)
- Grafana + Graphite + Prometheus + Jaeger + ELK
- Web UI: React (CSR) + MobX
- SSO: Keycloak
Követjük a mikroszolgáltatás-alapú fejlesztés elvét, bár van egy legacy részünk az Integro korábbi monolitikus verziójával. Minden mikroszolgáltatás a saját docker-konténerében fut, a szolgáltatások HTTP-kéréseken és RabbitMQ-üzeneteken kommunikálnak egymással. A mikroszolgáltatások a Consul segítségével találják meg egymást, és végrehajtják a hozzáférést az SSO-n (Keycloak, OAuth 2/OpenID Connect) keresztül.

Valós példaként vegyük szemügyre a Jenkins kezelést, amely a következő lépésekből áll:
- A workflow kezelő mikroszolgáltatás (továbbiakban Flow-mikroszolgáltatás) el szeretné indítani a Jenkins építkezést. Ehhez a Consul segítségével megtalálja a Jenkins integrációs mikroszolgáltatás IP:PORT-ját, és aszinkron kérést küld neki a Jenkins építkezésének elindítására.
- A Jenkins-mikroszolgáltatás a kérés megérkezése után létrehoz egy Job ID-t, amely alapján később az eredmény azonosítható lesz. Ezen kívül elindítja a Jenkins építkezést a REST API hívásával.
- A Jenkins végrehajtja az építkezést, és befejezése után webhook-ot küld az eredményekkel a Jenkins-mikroszolgáltatásnak.
- A Jenkins-mikroszolgáltatás, miután megkapta a webhook-ot, egy üzenetet hoz létre a kérés feldolgozásának befejezéséről, és csatolja hozzá a végrehajtás eredményeit. Az elkészített üzenet a RabbitMQ-ba kerül.
- A RabbitMQ-n keresztül a közzétett üzenet eljut a Flow-mikroszolgáltatáshoz, amely megtudja a feladatának feldolgozásának eredményét, összevetve a kérés és a megkapott üzenet Job ID-ját.
Jelenleg körülbelül 30 mikroszolgáltatásunk van, amelyeket több csoportra bonthatunk:
- Konfigurációk kezelése.
- Felhasználók tájékoztatása és interakciók (messengerek, e-mail).
- Forráskóddal való munka.
- Integráció a telepítési eszközökkel (jenkins, nomad, consul, stb.).
- Monitorozás (kiadások, hibák stb.).
- Webes segédprogramok (UI a tesztkörnyezetek kezelésére, statisztika gyűjtésére stb.).
- Integráció feladatkezelőkkel és hasonló rendszerekkel.
- Workflow kezelése különböző feladatokhoz.
Feladat workflow
Az Integro automatizálja a feladat életciklusához kapcsolódó tevékenységeket. Egyszerűsített módon a feladat életciklusa alatt a Jira-ban található feladat workflow-ját értjük. Fejlesztési folyamataikban néhány a projekttől, feladattípustól és a konkrét feladatnál kiválasztott lehetőségektől függő workflow-változat létezik.
Tekintsük át a leggyakrabban használt workflow-t:

A diagram with a gear indicates that the transition is triggered automatically by Integro, while a human figure signifies that the transition is triggered manually by a person. Let’s consider several pathways through which a task can progress in this workflow.
Fully manual testing on DEV+BETA without canary tests (this is usually how we release monolith):

There can be other combinations of transitions. Sometimes the path that a task will take can be selected through options in Jira.
Task movement
Let’s look at the main steps performed when moving a task through the workflow 'Testing on DEV + canary tests':
1. A developer or PM creates a task.
2. The developer takes the task to work. After completion, they move it to the IN REVIEW status.
3. Jira sends a Webhook to the Jira microservice (responsible for integration with Jira).
4. The Jira microservice sends a request to the Flow service (responsible for internal workflows where work is done) to initiate the workflow.
5. Inside the Flow service:
- Reviewers are assigned to the task (Users microservice, which knows everything about users + Jira microservice).
- The Source microservice (which knows about repositories and branches but does not work with the actual code) searches for repositories that have a branch for our task (to simplify searching, the branch name matches the task number in Jira). Most often, a task has only one branch in one repository, which simplifies queue management for deployment and reduces coupling between repositories.
- For each found branch, the following sequence of actions is performed:
i) Merging with the master branch (Git microservice for working with code).
ii) The branch is locked from changes by the developer (Bitbucket microservice).
iii) A Pull Request is created for this branch (Bitbucket microservice).
iv) A message about the new Pull Request is sent to the developers' chats (Notify microservice for notifications).
v) Build, testing, and deployment of the task on DEV is initiated (Jenkins microservice for working with Jenkins).
vi) If all previous steps were successfully completed, Integro places its Approve in the Pull Request (Bitbucket microservice). - Integro awaits Approve in the Pull Request from the assigned reviewers.
- As soon as all necessary Approvals are received (including successful automated tests), Integro moves the task to Test on Dev status (Jira microservice).
6. A tesztelők tesztelik a feladatot. Ha nincsenek problémák, a feladatot átkonvertálják a Ready For Build státuszba.
7. Az Integro „látja”, hogy a feladat készen áll a kiadásra, és elindítja a telepítést kísérleti módban (Jenkins-mikroszolgáltatás). A kiadásra való készenlét egy szabályrendszer által van meghatározva. Például a feladat a megfelelő státuszban van, nincsenek blokkolások más feladatokra, jelenleg nincsenek aktív telepítések ezen mikroszolgáltatás esetében stb.
8. A feladat a Canary státuszba kerül (Jira-mikroszolgáltatás).
9. A Jenkins a Nomad-on keresztül indítja el a feladat telepítését kísérleti módban (általában 1-3 példány) és értesíti a kiadási monitorozó szolgáltatást (DeployWatch-mikroszolgáltatás).
10. A DeployWatch-mikroszolgáltatás gyűjti a hiba háttéradatokat és reagál rá, ha szükséges. Ha a hibák háttéradata meghaladja a normát (amely automatikusan van kiszámítva), értesítést küldenek a fejlesztőknek a Notify-mikroszolgáltatáson keresztül. Ha 5 percen belül a fejlesztő nem reagál (nem nyomja meg a Revert vagy Stay gombot), automatikus visszaállítás kezdődik a kísérleti példányok esetében. Ha a háttér nem haladja meg a határt, a fejlesztőnek manuálisan kell elindítania a feladat telepítését a Production-ba (a UI gombjának megnyomásával). Ha 60 percen belül a fejlesztő nem indítja el a telepítést a Production-ba, a kísérleti példányok szintén biztonsági okokból visszavonásra kerülnek.
11. A Production telepítés elindítása után:
- A feladat a Production státuszba kerül (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 kiadásról.
- A DeployWatch-mikroszolgáltatás ellenőrzi, hogy a Production-on az összes konténer frissült-e (voltak olyan esetek, amikor nem frissült minden).
- A Notify-mikroszolgáltatáson keresztül értesítést küldenek a Production-ban végzett telepítés eredményeiről.
12. A fejlesztőknek 30 perc áll rendelkezésükre a feladat visszaállítására a Production-ból, ha a mikroszolgáltatás nem megfelelő viselkedését észlelik. Ezen időszak letelte után a feladat automatikusan beolvad a master-be (Git-mikroszolgáltatás).
13. A master-be való sikeres merge után a feladat státusza Closed-ra változik (Jira-mikroszolgáltatás).
A séma nem igényel teljes részletességet (a valóságban több lépés is van), de lehetővé teszi az integráció mértékének értékelését a folyamatokban. Nem tartjuk ezt a sémát tökéletesnek, és folyamatosan javítjuk a kiadások és telepítések automatikus kezelésének folyamatait.
Mi következik
Nagy terveink vannak az automatizálás fejlesztésére, például a kézi műveletek elhagyása a monolit kiadásakor, a megfigyelés javítása az automatikus telepítés során, valamint a fejlesztőkkel való együttműködés javítása.
De itt egyelőre megállunk. Sok témát csak felszínesen érintettünk az automatizálás áttekintésében, egyeseket pedig egyáltalán nem, ezért örömmel válaszolunk a kérdésekre. Várjuk az ötleteket, hogy miről beszéljünk részletesebben, írjatok megjegyzésben.
Forrás: habr.com
