Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

På RIT 2019 gjorde vår kollega Alexander Korotkov Rapportera om automatisering av utveckling på CIAN: för att förenkla livet och arbetet använder vi vår egen Integro-plattform. Det spårar livscykeln för uppgifter, avlastar utvecklare från rutinoperationer och minskar avsevärt antalet buggar i produktionen. I det här inlägget kommer vi att komplettera Alexanders rapport och berätta hur vi gick från enkla skript till att kombinera produkter med öppen källkod via vår egen plattform och vad vårt separata automationsteam gör.
 

Noll nivå

"Det finns inget sådant som en nollnivå, jag vet inte en sådan sak"
Master Shifu från filmen "Kung Fu Panda"

Automatiseringen på CIAN började 14 år efter att företaget grundades. Då fanns det 35 personer i utvecklingsteamet. Svårt att tro, eller hur? Självklart fanns automatisering i någon form, men en separat riktning för kontinuerlig integration och kodleverans började ta form 2015. 

Vid den tiden hade vi en enorm monolit av Python, C# och PHP, utplacerad på Linux/Windows-servrar. För att distribuera detta monster hade vi en uppsättning skript som vi körde manuellt. Det fanns också monteringen av monoliten, som ledde till smärta och lidande på grund av konflikter vid sammanslagning av grenar, korrigering av defekter och återuppbyggnad "med en annan uppsättning uppgifter i byggnaden." En förenklad process såg ut så här:

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

Vi var inte nöjda med detta, och vi ville bygga en repeterbar, automatiserad och hanterbar bygg- och distributionsprocess. För detta behövde vi ett CI/CD-system, och vi valde mellan gratisversionen av Teamcity och gratisversionen av Jenkins, eftersom vi arbetade med dem och båda passade oss vad gäller uppsättningen funktioner. Vi valde Teamcity som en nyare produkt. Vid den tiden hade vi ännu inte använt mikrotjänstarkitektur och förväntade oss inte ett stort antal uppgifter och projekt.

Vi kommer till idén om vårt eget system

Implementeringen av Teamcity tog bara bort en del av det manuella arbetet: vad som återstår är skapandet av Pull Requests, marknadsföring av problem efter status i Jira och urval av frågor för release. Teamcity-systemet klarade inte längre av detta. Det var nödvändigt att välja vägen för ytterligare automatisering. Vi övervägde alternativ för att arbeta med skript i Teamcity eller byta till automationssystem från tredje part. Men till slut bestämde vi oss för att vi behövde maximal flexibilitet, vilket bara vår egen lösning kan ge. Så här såg den första versionen av det interna automationssystemet Integro ut.

Teamcity sysslar med automatisering på nivån för lansering av bygg- och distributionsprocesser, medan Integro fokuserade på automatisering av utvecklingsprocesser på toppnivå. Det var nödvändigt att kombinera arbete med problem i Jira med bearbetning av tillhörande källkod i Bitbucket. I det här skedet började Integro ha egna arbetsflöden för att arbeta med uppgifter av olika slag. 

På grund av den ökade automatiseringen i affärsprocesser har antalet projekt och körningar i Teamcity ökat. Så ett nytt problem kom: en gratis Teamcity-instans räckte inte (3 agenter och 100 projekt), vi lade till en annan instans (3 fler agenter och 100 projekt), sedan en till. Som ett resultat fick vi ett system med flera kluster, vilket var svårt att hantera:

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

När frågan om en 4:e instans dök upp insåg vi att vi inte kunde fortsätta att leva så här, eftersom de totala kostnaderna för att stödja 4 instanser inte längre var inom några gränser. Frågan uppstod om att köpa betald Teamcity eller välja gratis Jenkins. Vi gjorde beräkningar på instanser och automationsplaner och bestämde oss för att vi skulle bo på Jenkins. Efter ett par veckor bytte vi till Jenkins och eliminerade en del av huvudvärken i samband med att underhålla flera Teamcity-instanser. Därför kunde vi fokusera på att utveckla Integro och anpassa Jenkins för oss själva.

Med framväxten av grundläggande automatisering (i form av automatiskt skapande av Pull Requests, insamling och publicering av kodtäckning och andra kontroller) finns det en stark önskan att överge manuella releaser så mycket som möjligt och ge detta arbete till robotar. Dessutom började företaget gå över till mikrotjänster inom företaget, vilket krävde frekventa releaser, och separat från varandra. Det är så vi gradvis kom fram till automatiska släpp av våra mikrotjänster (vi släpper för närvarande monoliten manuellt på grund av processens komplexitet). Men som vanligt uppstod en ny komplexitet. 

Vi automatiserar testning

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

På grund av automatiseringen av releaser har utvecklingsprocesserna accelererat, delvis på grund av att vissa teststeg hoppats över. Och detta ledde till en tillfällig kvalitetsförlust. Det låter trivialt, men tillsammans med accelerationen av releaser var det nödvändigt att ändra metodiken för produktutveckling. Det var nödvändigt att tänka på automatisering av testning, införa personligt ansvar (här pratar vi om att "acceptera idén i huvudet", inte monetära böter) av utvecklaren för den släppta koden och buggar i den, såväl som beslutet att släpp/inte släpp en uppgift genom automatisk driftsättning. 

Genom att eliminera kvalitetsproblem kom vi till två viktiga beslut: vi började utföra kanariefågeltestning och införde automatisk övervakning av felbakgrunden med ett automatiskt svar på dess överskott. Den första lösningen gjorde det möjligt att hitta uppenbara fel innan koden var helt släppt i produktion, den andra minskade svarstiden på problem i produktionen. Misstag händer förstås, men vi spenderar det mesta av vår tid och ansträngning inte på att rätta till dem, utan på att minimera dem. 

Automationsteam

Vi har för närvarande en personal på 130 utvecklare, och vi fortsätter att växa. Teamet för kontinuerlig integration och kodleverans (nedan kallat Deploy and Integration eller DI-teamet) består av 7 personer och arbetar i 2 riktningar: utveckling av Integros automationsplattform och DevOps. 

DevOps ansvarar för Dev/Beta-miljön på CIAN-sajten, Integro-miljön, hjälper utvecklare att lösa problem och utvecklar nya tillvägagångssätt för att skala miljöer. Integros utvecklingsriktning behandlar både själva Integro och relaterade tjänster, till exempel plugins för Jenkins, Jira, Confluence, och utvecklar även hjälpverktyg och applikationer för utvecklingsteam. 

DI-teamet samarbetar med plattformsteamet, som utvecklar arkitekturen, biblioteken och utvecklingsmetoderna internt. Samtidigt kan vilken utvecklare som helst inom CIAN bidra till automatisering, till exempel göra mikroautomation för att passa teamets behov eller dela en cool idé om hur man kan göra automatisering ännu bättre.

Lagerkaka av automation på CIAN

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

Alla system som är involverade i automation kan delas in i flera lager:

  1. Externa system (Jira, Bitbucket, etc.). Utvecklingsteam arbetar med dem.
  2. Integro plattform. Oftast arbetar inte utvecklare med det direkt, men det är det som håller all automation igång.
  3. Leverans-, orkestrerings- och upptäcktstjänster (till exempel Jeknins, Consul, Nomad). Med deras hjälp distribuerar vi kod på servrar och ser till att tjänster fungerar med varandra.
  4. Fysiskt lager (servrar, OS, relaterad programvara). Vår kod fungerar på denna nivå. Detta kan vara antingen en fysisk server eller en virtuell (LXC, KVM, Docker).

Utifrån detta koncept delar vi upp ansvarsområden inom DI-teamet. De två första nivåerna är inom ansvarsområdet för utvecklingsriktningen Integro, och de två sista nivåerna är redan inom ansvarsområdet för DevOps. Denna separation gör att vi kan fokusera på uppgifter och stör inte interaktionen, eftersom vi är nära varandra och ständigt utbyter kunskap och erfarenheter.

Intakt

Låt oss fokusera på Integro och börja med teknikstacken:

  • CentOS 7
  • Hamnarbetare + Nomad + Konsul + Vault
  • Java 11 (den gamla Integro-monoliten kommer att finnas kvar på Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • RabbitMQ 
  • Apache Ignite
  • Camunda (inbäddad)
  • Grafana + Grafit + Prometheus + Jaeger + ELK
  • Webbgränssnitt: React (CSR) + MobX
  • SSO: Nyckelmantel

Vi följer principen om utveckling av mikrotjänster, även om vi har arv i form av en monolit av en tidig version av Integro. Varje mikrotjänst körs i sin egen Docker-behållare och tjänsterna kommunicerar med varandra via HTTP-förfrågningar och RabbitMQ-meddelanden. Mikrotjänster hittar varandra genom Consul och gör en begäran till den och skickar auktorisering genom SSO (Keycloak, OAuth 2/OpenID Connect).

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

Som ett verkligt exempel, överväg att interagera med Jenkins, som består av följande steg:

  1. Mikrotjänsten för arbetsflödeshantering (nedan kallad Flow-mikrotjänsten) vill köra en build i Jenkins. För att göra detta använder han Consul för att hitta IP:PORT för mikrotjänsten för integration med Jenkins (hädanefter kallad Jenkins mikrotjänst) och skickar en asynkron förfrågan till den om att starta byggandet i Jenkins.
  2. Efter att ha tagit emot en förfrågan genererar Jenkins mikrotjänst och svarar med ett jobb-ID, som sedan kan användas för att identifiera resultatet av arbetet. Samtidigt triggar det inbyggnaden i Jenkins via ett REST API-anrop.
  3. Jenkins utför bygget och, efter att det är klart, skickar en webhook med exekveringsresultaten till Jenkins mikrotjänst.
  4. Jenkins mikrotjänst, efter att ha tagit emot webhook, genererar ett meddelande om slutförandet av begäranbearbetningen och bifogar exekveringsresultaten till den. Det genererade meddelandet skickas till RabbitMQ-kön.
  5. Genom RabbitMQ når det publicerade meddelandet Flow-mikrotjänsten, som får reda på resultatet av att bearbeta sin uppgift genom att matcha jobb-ID från begäran och det mottagna meddelandet.

Nu har vi ett 30-tal mikrotjänster, som kan delas in i flera grupper:

  1. Konfigurationshantering.
  2. Information och interaktion med användare (budbärare, post).
  3. Arbeta med källkod.
  4. Integration med distributionsverktyg (jenkins, nomad, konsul, etc.).
  5. Övervakning (releaser, fel, etc.).
  6. Webbverktyg (UI för att hantera testmiljöer, samla in statistik, etc.).
  7. Integration med uppgiftsspårare och liknande system.
  8. Arbetsflödeshantering för olika uppgifter.

Arbetsflödesuppgifter

Integro automatiserar aktiviteter relaterade till uppgiftens livscykel. I förenklade termer kommer livscykeln för en uppgift att förstås som arbetsflödet för en uppgift i Jira. Våra utvecklingsprocesser har flera arbetsflödesvariationer beroende på projekt, typ av uppgift och de alternativ som valts i en viss uppgift. 

Låt oss titta på arbetsflödet som vi använder oftast:

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

I diagrammet indikerar växeln att övergången anropas automatiskt av Integro, medan den mänskliga figuren anger att övergången anropas manuellt av en person. Låt oss titta på flera vägar som en uppgift kan ta i detta arbetsflöde.

Helt manuell testning på DEV+BETA utan kanariefågetest (vanligtvis är det så här vi släpper en monolit):

Från skript till vår egen plattform: hur vi automatiserade utvecklingen på CIAN

Det kan finnas andra övergångskombinationer. Ibland kan vägen som ett problem kommer att ta väljas genom alternativ i Jira.

Uppgiftsrörelse

Låt oss titta på de viktigaste stegen som utförs när en uppgift går genom arbetsflödet "DEV Testing + Canary Tests":

1. Utvecklaren eller PM skapar uppgiften.

2. Utvecklaren tar uppgiften till jobbet. Efter slutförandet växlar den till IN REVIEW-status.

3. Jira skickar en Webhook till Jira mikrotjänst (ansvarig för integration med Jira).

4. Jira-mikrotjänsten skickar en begäran till Flow-tjänsten (ansvarig för interna arbetsflöden där arbete utförs) för att starta arbetsflödet.

5. Inuti Flow-tjänsten:

  • Granskare tilldelas uppgiften (Users microservice som vet allt om användare + Jira microservice).
  • Genom Source-mikrotjänsten (den känner till repositories och filialer, men fungerar inte med själva koden) görs en sökning efter repositories som innehåller en gren av vårt nummer (för att förenkla sökningen sammanfaller filialens namn med problemet nummer i Jira). Oftast har en uppgift bara en gren i ett arkiv; detta förenklar hanteringen av distributionskön och minskar anslutningen mellan arkiven.
  • För varje hittad gren utförs följande sekvens av åtgärder:

    i) Uppdatering av mastergrenen (Git microservice för att arbeta med kod).
    ii) Grenen blockeras från ändringar av utvecklaren (Bitbucket microservice).
    iii) En Pull Request skapas för denna gren (Bitbucket microservice).
    iv) Ett meddelande om en ny Pull-begäran skickas till utvecklarchattar (Meddela mikrotjänst för att arbeta med aviseringar).
    v) Bygga, testa och distribuera uppgifter startas på DEV (Jenkins microservice för att arbeta med Jenkins).
    vi) Om alla tidigare steg har slutförts framgångsrikt, lägger Integro sitt godkännande i Pull Request (Bitbucket microservice).

  • Integro väntar på Approve in Pull Request från utsedda granskare.
  • Så snart alla nödvändiga godkännanden har mottagits (inklusive automatiserade tester har godkänts positivt), överför Integro uppgiften till statusen Test on Dev (Jira microservice).

6. Testare testar uppgiften. Om det inte finns några problem överförs uppgiften till statusen Ready For Build.

7. Integro "ser" att uppgiften är redo att släppas och startar sin distribution i kanariefågelläge (Jenkins microservice). Beredskapen för frigivning bestäms av en uppsättning regler. Till exempel är uppgiften i den status som krävs, det finns inga låsningar för andra uppgifter, det finns för närvarande inga aktiva uppladdningar av denna mikrotjänst, etc.

8. Uppgiften överförs till Canary-status (Jira microservice).

9. Jenkins startar en distributionsuppgift genom Nomad i kanariefågelläge (vanligtvis 1-3 instanser) och meddelar releaseövervakningstjänsten (DeployWatch microservice) om utplaceringen.

10. Mikrotjänsten DeployWatch samlar in felbakgrunden och reagerar på den vid behov. Om felbakgrunden överskrids (bakgrundsnormen beräknas automatiskt) meddelas utvecklarna via mikrotjänsten Notify. Om utvecklaren inte har svarat efter 5 minuter (klickat på Återställ eller Stanna), så startas en automatisk återställning av kanariefågelinstanserna. Om bakgrunden inte överskrids måste utvecklaren manuellt starta uppgiftsdistributionen till produktion (genom att klicka på en knapp i användargränssnittet). Om utvecklaren inom 60 minuter inte har lanserat distributionen till produktion, kommer kanariefågelinstanserna också att återställas av säkerhetsskäl.

11. Efter att ha lanserat distributionen till produktion:

  • Uppgiften överförs till produktionsstatus (Jira microservice).
  • Jenkins mikrotjänst startar distributionsprocessen och meddelar mikrotjänsten DeployWatch om distributionen.
  • Mikrotjänsten DeployWatch kontrollerar att alla behållare i produktionen har uppdaterats (det fanns fall då inte alla uppdaterades).
  • Genom mikrotjänsten Notify skickas ett meddelande om resultatet av driftsättningen till Produktion.

12. Utvecklare har 30 minuter på sig att börja återställa en uppgift från produktion om felaktigt mikrotjänstbeteende upptäcks. Efter denna tid kommer uppgiften automatiskt att slås samman till master (Git microservice).

13. Efter en lyckad sammanslagning till master ändras aktivitetens status till Closed (Jira microservice).

Diagrammet låtsas inte vara helt detaljerat (i verkligheten finns det ännu fler steg), men det låter dig bedöma graden av integration i processer. Vi anser inte att detta schema är idealiskt och förbättrar processerna för automatisk release och distributionsstöd.

Vad är nästa

Vi har stora planer för utveckling av automation, till exempel att eliminera manuella operationer under monolitsläpp, förbättra övervakningen under automatisk driftsättning och förbättra interaktionen med utvecklare.

Men låt oss stanna här för nu. Vi täckte många ämnen i automationsgranskningen ytligt, några berördes inte alls, så vi svarar gärna på frågor. Vi väntar på förslag på vad som ska täckas i detalj, skriv i kommentarerna.

Källa: will.com

Lägg en kommentar