Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

På RIT 2019 præsenterede vores kollega Alexander Korotkov en rapport om automatisering af udvikling i CIAN: for at forenkle livet og arbejdet, bruger vi vores egen platform Integro. Den sporer livscyklussen for opgaver, fjerner rutineopgaver fra udviklerne og reducerer betydeligt antallet af fejl i produktionen. I dette indlæg vil vi supplere Alexanders rapport og fortælle, hvordan vi gik fra simple scripts til integration af open source-produkter gennem vores egen platform, og hvad vores dedikerede automatiseringsteam arbejder med.
 

Nulniveau

»Der findes ikke noget nulniveau, jeg kender ikke til det«
Mester Shifu fra animationsfilmen «Kung Fu Panda»

Automatisering i CIAN begyndte 14 år efter virksomhedens grundlæggelse. På det tidspunkt var der 35 personer i udviklingsteamet. Det er svært at tro, ikke? Selvfølgelig eksisterede automatisering på en eller anden måde, men et særskilt fokus på kontinuerlig integration og levering af kode begyndte først at tage form i 2015. 

På det tidspunkt havde vi en kæmpe monolit bestående af Python, C# og PHP, der var implementeret på Linux-/Windows-servere. For at deployere dette monster havde vi et sæt scripts, som vi kørte manuelt. Der var også opbygningen af monolitten, som medførte smerte og lidelse på grund af konflikter ved sammensmeltning af grene, fejlrettelser og genopbygning med »en anden samling opgaver i builden«. Forenklet så processen sådan ud:

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

Dette tilfredsstillede os ikke, og vi ønskede at skabe en gentagelig, automatiseret og kontrolleret proces for opbygning og deployment. Til dette havde vi brug for et CI/CD-system, og vi overvejede mellem den gratis version af Teamcity og den gratis Jenkins, da vi allerede havde arbejdet med dem, og begge opfyldte vores funktionsmæssige behov. Vi valgte Teamcity som et mere moderne produkt. På det tidspunkt brugte vi endnu ikke mikroservicearkitektur og forventede ikke et stort antal opgaver og projekter.

Vi når frem til ideen om vores eget system

Implementeringen af Teamcity fjernede kun en del af det manuelle arbejde: der var stadig behov for at oprette Pull Requests, fremme opgaver gennem statusser i Jira og vælge opgaver til releaser. Dette var noget, Teamcity-systemet ikke kunne håndtere. Vi skulle vælge vejen for yderligere automatisering. Vi overvejede at arbejde med scripts i Teamcity eller skifte til eksterne automatiseringssystemer. Men i sidste ende besluttede vi, at vi havde brug for den maksimale fleksibilitet, som kun en egen løsning kan give. Således opstod den første version af vores interne automatiseringssystem kaldet Integro.

Teamcity beskæftiger sig med automatisering på niveauet til start af bygge- og deploy-processer, mens Integro fokuserede på overordnet automatisering af udviklingsprocesser. Det var nødvendigt at kombinere arbejdet med opgaver i Jira med behandlingen af den relaterede kildekode i Bitbucket. På dette tidspunkt begyndte Integro at udvikle sine egne workflows til håndtering af opgaver af forskellige typer. 

På grund af den stigende automatisering i forretningsprocesserne er antallet af projekter og runs i Teamcity steget. Her opstod et nyt problem: én gratis instans af Teamcity var ikke længere tilstrækkelig (3 agenter og 100 projekter), vi tilføjede endnu en instans (yderligere 3 agenter og 100 projekter), og derefter endnu en. I sidste ende endte vi med et system bestående af flere klynger, som var svære at administrere:

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

Da spørgsmålet om en fjerde instans opstod, indså vi, at det ikke kunne fortsætte sådan, da de samlede omkostninger ved at understøtte 4 instanser ikke længere kunne forsvares. Der opstod spørgsmålet om at købe en betalt version af Teamcity eller vælge den gratis Jenkins. Vi lavede beregninger vedrørende instanser og automatiseringsplaner og besluttede, at vi ville overgå til Jenkins. Efter et par uger skiftede vi til Jenkins og slap for en del af hovedpinen i forbindelse med understøttelsen af flere instanser af Teamcity. På den måde kunne vi fokusere på udviklingen af Integro og tilpasningen af Jenkins til vores behov.

Med stigningen i grundlæggende automatisering (i form af automatisk oprettelse af Pull Requests, indsamling og publicering af Code coverage og andre kontroller) opstod der et stærkt ønske om at maksimere afgivelsen fra manuelle udgivelser og overlade dette arbejde til robotter. Derudover begyndte virksomheden at flytte til mikrotjenester, som krævede hyppige udgivelser, der blev udført separat fra hinanden. På denne måde kom vi gradvist til automatiserede udgivelser af vores mikrotjenester (monolitten udgiver vi stadig manuelt på grund af kompleksiteten i processen). Men som det ofte sker, opstod der en ny udfordring. 

Vi automatiserer testning

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

På grund af automatiseringen af udgivelser er udviklingsprocesserne blevet hurtigere, delvist på bekostning af at springe nogle testfaser over. Dette har ført til midlertidigt tab af kvalitet. Det lyder banalt, men sammen med accelerationen af udgivelserne var det nødvendigt også at ændre produktudviklingsmetodologien. Vi skulle overveje automatisering af testning, indføre personlig ansvarlighed (her taler vi om "at tage ideen til sig", ikke økonomiske bøder) for udviklerne for den kode, de udgiver, og fejlene i den, samt tage beslutning om udgivelse/ikke udgivelse af opgaver gennem automatisk deployment. 

Ved at løse kvalitetsproblemerne kom vi til to vigtige beslutninger: vi begyndte at udføre kanarietestning og implementerede automatisk overvågning af baggrundsfejl med automatisk respons ved overskridelser. Den første beslutning gjorde det muligt at finde åbenlyse fejl, før koden reelt kom i produktion, mens den anden reducerede responstiden på problemer i produktion. Der opstår selvfølgelig fejl, men vi bruger det meste af tiden og ressourcerne ikke på at rette dem, men på at minimere dem. 

Automatiseringsteamet

Vi har nu et team på 130 udviklere, og vi fortsætter med at vokse. Teamet for kontinuerlig integration og levering af kode (herefter kaldet Deploy and Integration eller DI-teamet) består af 7 personer og arbejder inden for 2 områder: udvikling af automatiseringsplatformen Integro og DevOps. 

DevOps er ansvarlig for Dev/Beta-miljøer af CIAN-webstedet, Integro-miljøer, hjælper udviklerne med at løse problemer og udvikler nye tilgange til skalering af miljøer. Integro udviklingsretning arbejder både med Integro selv og med tilknyttede tjenester, såsom plugins til Jenkins, Jira, Confluence, og udvikler også nyttige værktøjer og applikationer til udviklerteams. 

DI-teamet arbejder sammen med Platform-teamet, der er ansvarlig for udviklingen af arkitektur, biblioteker og udviklingstilgange inden for virksomheden. Derudover kan enhver udvikler inden for CIAN bidrage til automatiseringen, for eksempel ved at lave mikroautomatisering til teamets behov eller dele et godt idé om, hvordan man kan gøre automatiseringen endnu bedre.

Lagkage af automatisering i CIAN

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

Alle systemer involveret i automatiseringen kan opdeles i flere lag:

  1. Eksterne systemer (Jira, Bitbucket osv.). Disse bruges af udviklerteamene.
  2. Integro-platformen. Oftest arbejder udviklerne ikke direkte med den, men det er den, der understøtter hele automatiseringen.
  3. Leveringstjenester, orkestrering og opdagelse (for eksempel Jenkins, Consul, Nomad). Ved hjælp af dem implementerer vi kode på serverne og sikrer, at tjenesterne fungerer sammen.
  4. Det fysiske niveau (servere, OS, ledsagende software). På dette niveau kører vores kode. Det kan være både en fysisk server og en virtuel (LXC, KVM, Docker).

Baseret på dette koncept deler vi ansvarsområderne inden for DI-teamet. De to første lag falder ind under Integro udviklingsretning, mens de to sidste lag er under DevOps. Denne opdeling muliggør fokus på opgaver og forstyrrer ikke samarbejdet, da vi er tæt på hinanden og konstant deler viden og erfaring.

Integro

Lad os fokusere på Integro og starte med teknologistakken:

  • CentOs 7
  • Docker + Nomad + Consul + Vault
  • Java 11 (den gamle monolit Integro forbliver på Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • RabbitMQ 
  • Apache Ignite
  • Camunda (indlejret)
  • Grafana + Graphite + Prometheus + Jaeger + ELK
  • Web UI: React (CSR) + MobX
  • SSO: Keycloak

Vi følger princippet om mikrotjenesteudvikling, selvom vi stadig har legacy i form af monoliten fra den tidlige version af Integro. Hver mikrotjeneste kører i sin egen Docker-container, og tjenesterne kommunikere med hinanden via HTTP-anmodninger og RabbitMQ-beskeder. Mikrotjenesterne finder hinanden gennem Consul og sender forespørgsler til det, idet de autoriserer via SSO (Keycloak, OAuth 2/OpenID Connect).

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

Som et konkret eksempel vil vi overveje interaktionen med Jenkins, som består af følgende trin:

  1. Workflow-mikrotjenesten ønsker at starte en bygning i Jenkins. For at gøre dette finder den via Consul IP:PORT for mikrotjenesten, der integrerer med Jenkins (Jenkins-mikrotjenesten), og sender en asynkron forespørgsel til den for at starte bygningen i Jenkins.
  2. Jenkins-mikrotjenesten, efter at have modtaget anmodningen, genererer og returnerer et Job ID, som senere kan bruges til at identificere resultatet af arbejdet. Samtidig starter den bygningen i Jenkins via REST API-opkaldet.
  3. Jenkins udfører bygningen, og efter dens afslutning sender den en webhook med resultaterne af udførelsen til Jenkins-mikrotjenesten.
  4. Jenkins-mikrotjenesten, efter at have modtaget webhooken, genererer en meddelelse om afslutningen af behandlingen af anmodningen og vedhæfter resultaterne af udførelsen. Den genererede meddelelse sendes til RabbitMQ-køen.
  5. Gennem RabbitMQ når den publicerede meddelelse Flow-mikrotjenesten, som får kendskab til resultatet af behandlingen af sin opgave ved at sammenligne Job ID fra anmodningen og den modtagne meddelelse.

I øjeblikket har vi omkring 30 mikrotjenester, som kan opdeles i flere grupper:

  1. Håndtering af konfigurationer.
  2. Informering og interaktion med brugere (messenger, email).
  3. Arbejdet med kildekoden.
  4. Integration med deploy-værktøjer (Jenkins, Nomad, Consul osv.).
  5. Overvågning (af udgivelser, fejl osv.).
  6. Web-nyttigheder (UI til styring af testmiljøer, indsamling af statistik osv.).
  7. Integration med opgavestyringssystemer og lignende.
  8. Styring af workflow for forskellige opgaver.

Opgave workflow

Integro automatiserer handlinger relateret til livscyklussen for en opgave. Forenklet, under livscyklussen for en opgave, forstår vi workflow for opgaven i Jira. I vores udviklingsprocesser er der flere varianter af workflow afhængigt af projektet, opgavetype og de valgmuligheder, der er valgt i den specifikke opgave. 

Lad os overveje det workflow, vi bruger oftest:

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

I diagrammet peger gearhjulet på, at overgangen (transition) kaldes automatisk af Integro, mens figuren af en person betyder, at overgangen kaldes manuelt af en person. Lad os se på nogle måder, hvorpå opgaven kan bevæge sig gennem dette workflow.

Fuldstændig manuel testning på DEV+BETA uden kanarietests (som vi normalt udgiver monoliter):

Fra scripts til egen platform: hvordan vi automatiserede udviklingen i CIAN

Der kan være andre kombinationer af transitioner. Nogle gange kan vejen, som opgaven tager, vælges gennem muligheder i Jira.

Opgavens bevægelse

Lad os se på de grundlæggende trin, der udføres, når opgaven bevæger sig gennem workflow "Test på DEV + kanarietests":

1. Udvikleren eller PM opretter en opgave.

2. Udvikleren tager opgaven i arbejde. Efter afslutningen flytter han den til status IN REVIEW.

3. Jira sender en Webhook til Jira-mikrotjenesten (som er ansvarlig for integrationen med Jira).

4. Jira-mikrotjenesten sender en forespørgsel til Flow-tjenesten (som er ansvarlig for interne workflows, hvor arbejdet udføres) for at starte workflowet.

5. Inden i Flow-tjenesten:

  • Anmeldere sættes til opgaven (Users-mikrotjenesten, som kender alt om brugerne + Jira-mikrotjenesten).
  • Gennem Source-mikrotjenesten (som kender til repositoryer og grene, men ikke arbejder med selve koden) udføres søgningen efter repositoryer, hvor der er en gren for vores opgave (for at forenkle søgningen, matcher grenens navn opgavens nummer i Jira). Oftest har opgaven kun én gren i ét repository, hvilket forenkler køstyringen ved deployment og reducerer sammenhængen mellem repositoryerne.
  • For hver fundet gren udføres følgende rækkefølge af handlinger:

    i) Sammenfletning med master-grenen (Git-mikrotjenesten til arbejde med kode).
    ii) Grenen spærrer for ændringer fra udvikleren (Bitbucket-mikrotjenesten).
    iii) Der oprettes en Pull Request til denne gren (Bitbucket-mikrotjenesten).
    iv) Der sendes en meddelelse om den nye Pull Request til udviklernes chats (Notify-mikrotjenesten til at håndtere underretninger).
    v) Bygning, testning og deployment af opgaven på DEV startes (Jenkins-mikrotjenesten til arbejde med Jenkins).
    vi) Hvis alle de foregående punkter er afsluttet med succes, sætter Integro sin godkendelse i Pull Request (Bitbucket-mikrotjenesten).

  • Integro venter på godkendelse i Pull Request fra de udpegede anmeldere.
  • Når alle nødvendige godkendelser er modtaget (inklusive positively beståede automatiserede tests), flytter Integro opgaven til status Test on Dev (Jira-mikrotjenesten).

6. Testere udfører test af opgaven. Hvis der ikke er problemer, overfører de opgaven til status Ready For Build.

7. Integro "ser", at opgaven er klar til udgivelse, og starter dens deployment i kanarytilstand (Jenkins-mikrotjeneste). Udgivelsesparathed bestemmes af et sæt regler. For eksempel, opgaven er i den nødvendige status, der er ingen blokeringer på andre opgaver, der er ingen aktive udrulninger af denne mikrotjeneste osv.

8. Opgaven overføres til status Canary (Jira-mikrotjeneste).

9. Jenkins starter deployment af opgaven i kanarytilstand gennem Nomad (normalt 1-3 instanser) og underretter udrulningsmonitoreringstjenesten (DeployWatch-mikrotjeneste) om udrulningen.

10. DeployWatch-mikrotjeneste indsamler fejlbaggrund og reagerer på den, hvis det er nødvendigt. Ved overskridelse af fejlbaggrunden (normal baggrund beregnes automatisk) underrettes udviklerne gennem Notify-mikrotjeneste. Hvis udvikleren ikke reagerer inden for 5 minutter (trykker Revert eller Stay), starter den automatiske tilbagerulning af kanaryinstanserne. Hvis baggrunden ikke er overskredet, skal udvikleren manuelt starte deployment af opgaven til Production (ved at trykke på knappen i UI). Hvis udvikleren ikke har startet deployment til Production inden for 60 minutter, vil kanaryinstanserne også blive rullet tilbage af sikkerhedsmæssige årsager.

11. Efter start af deployment til Production:

  • Opgaven overføres til status Production (Jira-mikrotjeneste).
  • Jenkins-mikrotjeneste starter deploymentprocessen og underretter DeployWatch-mikrotjeneste om udrulningen.
  • DeployWatch-mikrotjeneste tjekker, om alle containere på Production er blevet opdateret (der har været tilfælde, hvor ikke alt blev opdateret).
  • Der sendes en meddelelse om resultaterne af deploymenten til Production via Notify-mikrotjeneste.

12. Udviklerne har 30 minutter til at starte tilbagerulning af opgaven fra Production i tilfælde af, at der opdages ukorrekt adfærd fra mikrotjenesten. Efter denne tidsperiode vil opgaven automatisk blive flettet ind i master (Git-mikrotjeneste).

13. Efter en vellykket sammenfletning i master vil opgavens status blive ændret til Closed (Jira-mikrotjeneste).

Skemaet gør ikke krav på fuldstændig detaljeringsgrad (i virkeligheden er der flere trin), men giver mulighed for at vurdere graden af integration i processerne. Vi anser ikke dette skema for ideelt og forbedrer processerne for automatisk support af udgivelser og deployment.

vil være udsolgt

Vi har store planer for udvikling af automatisering, såsom at opgive manuelle operationer ved udgivelser af monolitter, forbedre overvågningen ved automatisk deployment og forbedre samarbejdet med udviklerne.

Men vi stopper her for nu. Vi har berørt mange emner i vores oversigt over automatisering overfladisk, og nogle har slet ikke været nævnt, så vi svarer gerne på spørgsmål. Vi ser frem til forslag til, hvad vi skal dække i detaljer, så skriv i kommentarerne.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster