Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Na RIT 2019, naš kolega Alexander Korotkov napravio je izvješće o automatizaciji razvoja u CIAN-u: za pojednostavljenje života i rada koristimo vlastitu Integro platformu. Prati životni ciklus zadataka, oslobađa programere rutinskih operacija i značajno smanjuje broj grešaka u proizvodnji. U ovom ćemo postu nadopuniti Alexanderovo izvješće i reći vam kako smo prešli s jednostavnih skripti na kombiniranje proizvoda otvorenog koda putem vlastite platforme i što radi naš zasebni tim za automatizaciju.
 

Nulta razina

"Ne postoji takva stvar kao što je nulta razina, ja to ne znam"
Majstor Shifu iz filma "Kung Fu Panda"

Automatizacija u CIAN-u započela je 14 godina nakon osnivanja tvrtke. Tada je u razvojnom timu bilo 35 ljudi. Teško za povjerovati, zar ne? Naravno, automatizacija je postojala u nekom obliku, ali poseban smjer za kontinuiranu integraciju i isporuku koda počeo se oblikovati 2015. godine. 

U to smo vrijeme imali ogroman monolit Pythona, C# i PHP-a, raspoređenih na Linux/Windows poslužiteljima. Za implementaciju ovog čudovišta imali smo skup skripti koje smo pokretali ručno. Bilo je tu i sastavljanje monolita, što je donijelo bol i patnju zbog sukoba prilikom spajanja grana, ispravljanja nedostataka i ponovne izgradnje "s drugačijim skupom zadataka u izgradnji". Pojednostavljeni proces je izgledao ovako:

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Nismo bili zadovoljni s tim i htjeli smo izgraditi ponovljiv, automatiziran i upravljiv proces izgradnje i implementacije. Za to nam je trebao CI/CD sustav, a birali smo između besplatne verzije Teamcity-a i besplatne verzije Jenkins-a, jer smo radili s njima i obje su nam odgovarale po skupu funkcija. Izabrali smo Teamcity kao noviji proizvod. U to vrijeme još nismo koristili mikroservisnu arhitekturu i nismo očekivali veliki broj zadataka i projekata.

Dolazimo do ideje vlastitog sustava

Implementacija Teamcityja uklonila je samo dio ručnog rada: ono što ostaje je stvaranje zahtjeva za povlačenjem, promoviranje problema prema statusu u Jiri i odabir problema za puštanje. Sustav Teamcity s tim se više nije mogao nositi. Trebalo je izabrati put dalje automatizacije. Razmotrili smo opcije za rad sa skriptama u Teamcityju ili prebacivanje na sustave automatizacije trećih strana. Ali na kraju smo zaključili da nam treba maksimalna fleksibilnost, koju samo naše vlastito rješenje može pružiti. Tako se pojavila prva verzija internog sustava automatizacije pod nazivom Integro.

Teamcity se bavi automatizacijom na razini pokretanja procesa izgradnje i implementacije, dok se Integro fokusirao na vrhunsku automatizaciju razvojnih procesa. Bilo je potrebno kombinirati rad s problemima u Jiri s obradom pridruženog izvornog koda u Bitbucketu. U ovoj fazi Integro je počeo imati vlastite tijekove rada za rad sa zadacima različitih vrsta. 

Zbog povećanja automatizacije poslovnih procesa, povećao se broj projekata i pokretanja u Teamcityju. Tako je došao novi problem: jedna besplatna Teamcity instanca nije bila dovoljna (3 agenta i 100 projekata), dodali smo još jednu instancu (još 3 agenta i 100 projekata), pa još jednu. Kao rezultat toga, dobili smo sustav od nekoliko klastera, kojim je bilo teško upravljati:

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Kada se postavilo pitanje 4. stupnja, shvatili smo da ne možemo dalje ovako živjeti, jer ukupni troškovi izdržavanja 4. stupnja više nisu bili u granicama. Postavilo se pitanje o kupnji plaćenog Teamcityja ili odabiru besplatnog Jenkinsa. Napravili smo izračune na instancama i planove automatizacije i odlučili da ćemo živjeti na Jenkinsu. Nakon nekoliko tjedana prebacili smo se na Jenkins i eliminirali dio glavobolje povezane s održavanjem više instanci Teamcityja. Stoga smo se mogli usredotočiti na razvoj Integra i prilagođavanje Jenkinsa sebi.

S porastom osnovne automatizacije (u obliku automatskog stvaranja zahtjeva za povlačenjem, prikupljanja i objavljivanja pokrivenosti koda i drugih provjera), postoji snažna želja da se što je više moguće napusti ručna izdanja i da se taj posao prepusti robotima. Osim toga, tvrtka je počela prelaziti na mikroservise unutar tvrtke, što je zahtijevalo česta izdanja, i to odvojeno jedna od druge. Tako smo postupno došli do automatskih izdanja naših mikroservisa (monolit trenutno izdajemo ručno zbog složenosti procesa). No, kako to obično biva, pojavila se nova komplikacija. 

Automatiziramo testiranje

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Zbog automatizacije izdanja ubrzani su razvojni procesi, dijelom i zbog preskakanja nekih faza testiranja. A to je dovelo do privremenog gubitka kvalitete. Zvuči trivijalno, ali uz ubrzanje izdanja, bilo je potrebno promijeniti i metodologiju razvoja proizvoda. Trebalo je razmišljati o automatizaciji testiranja, usađivanju osobne odgovornosti (ovdje je riječ o “prihvaćanju ideje u glavu”, a ne o novčanim kaznama) programera za objavljeni kod i bugove u njemu, kao i odluku da otpuštanje/neotpuštanje zadatka putem automatske implementacije. 

Otklanjajući probleme s kvalitetom, došli smo do dvije važne odluke: počeli smo provoditi kanarsko testiranje i uveli automatsko praćenje pozadine pogreške s automatskim odgovorom na njezin višak. Prvo rješenje omogućilo je pronalaženje očitih pogrešaka prije nego što je kod u potpunosti pušten u proizvodnju, drugo je smanjilo vrijeme odgovora na probleme u proizvodnji. Pogreške se, naravno, događaju, ali većinu vremena i truda trošimo ne na njihovo ispravljanje, već na njihovo smanjenje. 

Tim za automatizaciju

Trenutno zapošljavamo 130 programera i nastavljamo rasti. Tim za kontinuiranu integraciju i isporuku koda (u daljnjem tekstu Deploy and Integration ili DI tim) sastoji se od 7 ljudi i radi u 2 smjera: razvoj platforme za automatizaciju Integro i DevOps. 

DevOps je odgovoran za Dev/Beta okruženje CIAN stranice, Integro okruženje, pomaže programerima u rješavanju problema i razvija nove pristupe skaliranju okruženja. Smjer razvoja Integra bavi se i samim Integrom i povezanim uslugama, na primjer, dodacima za Jenkins, Jira, Confluence, a također razvija pomoćne uslužne programe i aplikacije za razvojne timove. 

DI tim surađuje s timom Platforme, koji interno razvija arhitekturu, biblioteke i razvojne pristupe. U isto vrijeme, svaki programer unutar CIAN-a može doprinijeti automatizaciji, na primjer, napraviti mikroautomatizaciju koja će odgovarati potrebama tima ili podijeliti cool ideju o tome kako automatizaciju učiniti još boljom.

Layer cake automatizacije u CIAN-u

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Svi sustavi uključeni u automatizaciju mogu se podijeliti u nekoliko slojeva:

  1. Vanjski sustavi (Jira, Bitbucket, itd.). S njima rade razvojni timovi.
  2. Integro platforma. Najčešće, programeri ne rade s njim izravno, ali to je ono što održava svu automatizaciju u radu.
  3. Usluge dostave, orkestracije i otkrivanja (na primjer, Jeknins, Consul, Nomad). Uz njihovu pomoć postavljamo kod na poslužitelje i osiguravamo da usluge međusobno rade.
  4. Fizički sloj (poslužitelji, OS, povezani softver). Naš kod funkcionira na ovoj razini. To može biti ili fizički poslužitelj ili virtualni (LXC, KVM, Docker).

Na temelju ovog koncepta dijelimo područja odgovornosti unutar DI tima. Prve dvije razine su u području odgovornosti smjera razvoja Integra, a posljednje dvije razine su već u području odgovornosti DevOps-a. Ova odvojenost omogućuje nam da se usredotočimo na zadatke i ne ometa interakciju, jer smo blizu jedni drugima i stalno razmjenjujemo znanja i iskustva.

Netaknut

Usredotočimo se na Integro i započnimo s tehnološkim nizom:

  • CentOS 7
  • Docker + Nomad + Consul + Vault
  • Java 11 (stari Integro monolit će ostati na Javi 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • Zec MQ 
  • Apache Ignite
  • Camunda (ugrađeno)
  • Grafana + Graphite + Prometheus + Jaeger + ELK
  • Web sučelje: React (CSR) + MobX
  • SSO: Ogrtač ključa

Držimo se principa razvoja mikroservisa, iako imamo nasljeđe u obliku monolita rane verzije Integra. Svaki mikroservis radi u vlastitom Docker spremniku, a servisi međusobno komuniciraju putem HTTP zahtjeva i RabbitMQ poruka. Mikroservisi se međusobno pronalaze putem Consul-a i postavljaju mu zahtjev, prosljeđujući autorizaciju kroz SSO (Keycloak, OAuth 2/OpenID Connect).

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Kao primjer iz stvarnog života, razmotrite interakciju s Jenkinsom koja se sastoji od sljedećih koraka:

  1. Mikroservis za upravljanje tijek rada (u daljnjem tekstu mikroservis Flow) želi pokrenuti izgradnju u Jenkinsu. Da bi to učinio, koristi Consul kako bi pronašao IP:PORT mikroservisa za integraciju s Jenkinsom (u daljnjem tekstu Jenkins mikroservis) i šalje mu asinkroni zahtjev za pokretanje izgradnje u Jenkinsu.
  2. Nakon primitka zahtjeva, mikroservis Jenkins generira i odgovara ID-om posla, koji se zatim može koristiti za identifikaciju rezultata rada. U isto vrijeme pokreće izgradnju u Jenkinsu putem REST API poziva.
  3. Jenkins izvodi izgradnju i, nakon dovršetka, šalje webhook s rezultatima izvršenja mikroservisu Jenkins.
  4. Mikroservis Jenkins, primivši webhook, generira poruku o završetku obrade zahtjeva i prilaže joj rezultate izvršenja. Generirana poruka šalje se u RabbitMQ red čekanja.
  5. Preko RabbitMQ-a objavljena poruka dolazi do mikroservisa Flow koji saznaje o rezultatu obrade svog zadatka tako što povezuje Job ID iz zahtjeva i primljene poruke.

Sada imamo oko 30 mikroservisa, koji se mogu podijeliti u nekoliko grupa:

  1. Konfiguracijski menadžment.
  2. Informiranje i interakcija s korisnicima (messengeri, pošta).
  3. Rad s izvornim kodom.
  4. Integracija s alatima za implementaciju (jenkins, nomad, consul, itd.).
  5. Praćenje (izdanja, greške, itd.).
  6. Web pomoćni programi (UI za upravljanje testnim okruženjima, prikupljanje statistike itd.).
  7. Integracija sa task trackerima i sličnim sustavima.
  8. Upravljanje tijekovima rada za različite zadatke.

Radni zadaci

Integro automatizira aktivnosti povezane sa životnim ciklusom zadatka. Pojednostavljeno rečeno, životni ciklus zadatka shvaćat će se kao radni tijek zadatka u Jiri. Naši razvojni procesi imaju nekoliko varijacija tijeka rada ovisno o projektu, vrsti zadatka i opcijama odabranim u pojedinom zadatku. 

Pogledajmo tijek rada koji najčešće koristimo:

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Na dijagramu zupčanik označava da Integro automatski poziva prijelaz, dok ljudska figura označava da prijelaz ručno poziva osoba. Pogledajmo nekoliko putova kojima zadatak može ići u ovom tijeku rada.

Potpuno ručno testiranje na DEV+BETA bez canary testova (obično ovako izdajemo monolit):

Od skripti do vlastite platforme: kako smo automatizirali razvoj u CIAN-u

Mogu postojati i druge prijelazne kombinacije. Ponekad se putem opcija u Jiri može odabrati put kojim će problem ići.

Kretanje zadatka

Pogledajmo glavne korake koji se izvode kada se zadatak kreće kroz tijek rada “DEV Testing + Canary Tests”:

1. Programer ili PM stvara zadatak.

2. Programer preuzima zadatak na posao. Nakon završetka prelazi u status U PREGLEDU.

3. Jira šalje Webhook mikroservisu Jira (odgovornom za integraciju s Jirom).

4. Mikroservis Jira šalje zahtjev servisu Flow (odgovornom za interne tijekove rada u kojima se obavlja rad) za pokretanje tijeka rada.

5. Unutar usluge Flow:

  • Zadatku su dodijeljeni recenzenti (Korisnički mikroservis koji zna sve o korisnicima + mikroservis Jira).
  • Preko mikroservisa Source (zna za repozitorije i grane, ali ne radi sa samim kodom) vrši se pretraga repozitorija koji sadrže granu našeg izdanja (radi pojednostavljenja pretrage, naziv grane se poklapa s problemom broj u Jira). Najčešće zadatak ima samo jednu granu u jednom repozitoriju; to pojednostavljuje upravljanje redom čekanja za implementaciju i smanjuje povezanost između repozitorija.
  • Za svaku pronađenu granu izvodi se sljedeći niz radnji:

    i) Ažuriranje glavne grane (Git mikroservis za rad s kodom).
    ii) Razvojni programer (Bitbucket microservice) blokira promjene u grani.
    iii) Pull Request se kreira za ovu granu (Bitbucket microservice).
    iv) Poruka o novom Pull Requestu šalje se u chatove programera (mikroservis Obavijesti za rad s obavijestima).
    v) Zadaci izgradnje, testiranja i postavljanja pokreću se na DEV-u (Jenkins mikroservis za rad s Jenkinsom).
    vi) Ako su svi prethodni koraci uspješno dovršeni, tada Integro stavlja svoje odobrenje u Pull Request (Bitbucket microservice).

  • Integro čeka odobrenje u zahtjevu za povlačenje od strane određenih recenzenata.
  • Čim se zaprime sva potrebna odobrenja (uključujući automatizirane testove koji su prošli pozitivno), Integro prebacuje zadatak u status Test on Dev (Jira microservice).

6. Testeri testiraju zadatak. Ako nema problema, tada se zadatak prebacuje u status Ready For Build.

7. Integro “vidi” da je zadatak spreman za puštanje i započinje njegovu implementaciju u canary modu (Jenkins mikroservis). Spremnost za puštanje određena je nizom pravila. Na primjer, zadatak je u traženom statusu, nema zaključavanja drugih zadataka, trenutno nema aktivnih prijenosa ove mikroservise itd.

8. Zadatak se prebacuje u status Canary (mikroservis Jira).

9. Jenkins pokreće zadatak implementacije kroz Nomad u canary modu (obično 1-3 instance) i obavještava servis za praćenje izdanja (Mikroservis DeployWatch) o implementaciji.

10. Mikroservis DeployWatch prikuplja pozadinu pogreške i reagira na nju, ako je potrebno. Ako je pozadina pogreške prekoračena (pozadinska norma izračunava se automatski), programeri se obavještavaju putem mikroservisa Notify. Ako nakon 5 minuta razvojni programer ne odgovori (klikne Vrati ili Ostani), pokreće se automatsko vraćanje instanci Canary. Ako pozadina nije prekoračena, razvojni programer mora ručno pokrenuti implementaciju zadatka u proizvodnju (klikom na gumb u korisničkom sučelju). Ako razvojni programer nije pokrenuo implementaciju u produkciju u roku od 60 minuta, canary instance će također biti vraćene iz sigurnosnih razloga.

11. Nakon pokretanja implementacije u proizvodnju:

  • Zadatak se prebacuje u status proizvodnje (mikroservis Jira).
  • Mikroservis Jenkins pokreće proces implementacije i obavještava mikroservis DeployWatch o implementaciji.
  • Mikroservis DeployWatch provjerava jesu li svi spremnici na Productionu ažurirani (bilo je slučajeva kada nisu svi ažurirani).
  • Putem mikroservisa Obavijesti, obavijest o rezultatima implementacije šalje se Produkciji.

12. Programeri će imati 30 minuta da počnu vraćati zadatak iz proizvodnje ako se otkrije neispravno ponašanje mikroservisa. Nakon tog vremena, zadatak će se automatski spojiti u glavni (Git mikroservis).

13. Nakon uspješnog spajanja u master, status zadatka će se promijeniti u Zatvoreno (mikroservis Jira).

Dijagram se ne pretvara da je potpuno detaljan (u stvarnosti ima još više koraka), ali vam omogućuje procjenu stupnja integracije u procese. Ne smatramo ovu shemu idealnom i poboljšavamo procese automatskog izdavanja i podrške za implementaciju.

što dalje

Imamo velike planove za razvoj automatizacije, na primjer, uklanjanje ručnih operacija tijekom monolitnih izdanja, poboljšanje nadzora tijekom automatske implementacije i poboljšanje interakcije s programerima.

No, zaustavimo se ovdje za sada. Mnoge smo teme u recenziji automatizacije obradili površno, neke nismo uopće dotakli pa ćemo rado odgovoriti na pitanja. Čekamo prijedloge što detaljnije pokriti, napišite u komentarima.

Izvor: www.habr.com

Dodajte komentar