Vad vet vi om mikrotjänster

Hallå! Jag heter Vadim Madison, jag leder utvecklingen av Avito System Platform. Det har sagts mer än en gång hur vi i företaget går från en monolitisk arkitektur till en mikrotjänster. Det är dags att dela med oss ​​av hur vi har förändrat vår infrastruktur för att få ut det mesta av mikrotjänsterna och förhindra att vi går vilse i dem. Hur PaaS hjälper oss här, hur vi förenklade driftsättningen och minskade skapandet av en mikrotjänst till ett klick – läs vidare. Allt jag skriver om nedan är inte fullt implementerat i Avito, en del av det är hur vi utvecklar vår plattform.

(Och i slutet av den här artikeln kommer jag att prata om möjligheten att delta i ett tredagars seminarium från mikroservicearkitekturexperten Chris Richardson).

Vad vet vi om mikrotjänster

Hur vi kom till mikrotjänster

Avito är en av de största hemliga webbplatserna i världen, mer än 15 miljoner nya annonser publiceras på den per dag. Vår backend accepterar mer än 20 tusen förfrågningar per sekund. Vi har för närvarande flera hundra mikrotjänster.

Vi har byggt en mikrotjänstarkitektur i flera år nu. Hur exakt - våra kollegor i detalj berättade på vår sektion på RIT++ 2017. På CodeFest 2017 (se. video), Sergey Orlov och Mikhail Prokopchuk förklarade i detalj varför vi behövde övergången till mikrotjänster och vilken roll Kubernetes spelade här. Nåväl, nu gör vi allt för att minimera skalningskostnaderna som är inneboende i en sådan arkitektur.

Från början skapade vi inte ett ekosystem som heltäckande skulle hjälpa oss att utveckla och lansera mikrotjänster. De samlade helt enkelt vettiga lösningar med öppen källkod, lanserade dem hemma och bjöd in utvecklaren att ta itu med dem. Som ett resultat gick han till ett dussintal platser (dashboards, interna tjänster), varefter han blev starkare i sin önskan att klippa kod på det gamla sättet, i en monolit. Den gröna färgen i diagrammen nedan indikerar vad utvecklaren gör på ett eller annat sätt med sina egna händer, och den gula färgen indikerar automatisering.

Vad vet vi om mikrotjänster

Nu i PaaS CLI-verktyget skapas en ny tjänst med ett kommando, och en ny databas läggs till med två till och distribueras till Stage.

Vad vet vi om mikrotjänster

Hur man övervinner eran av "mikrotjänstfragmentering"

Med en monolitisk arkitektur, för konsekvensen av förändringar i produkten, tvingades utvecklare ta reda på vad som pågick med sina grannar. När man arbetar med den nya arkitekturen är tjänstesammanhang inte längre beroende av varandra.

Dessutom, för att en mikrotjänstarkitektur ska vara effektiv måste många processer etableras, nämligen:

• loggning;
• begäran om spårning (Jaeger);
• felaggregering (Sentry);
• statusar, meddelanden, händelser från Kubernetes (händelseströmbehandling);
• rasgräns/strömbrytare (du kan använda Hystrix);
• kontroll av tjänstens anslutning (vi använder Netramesh);
• övervakning (Grafana);
• montering (TeamCity);
• kommunikation och avisering (Slack, e-post);
• uppgiftsspårning; (Jira)
• upprättande av dokumentation.

För att säkerställa att systemet inte förlorar sin integritet och förblir effektivt när det skalas, tänkte vi om organisationen av mikrotjänster i Avito.

Hur vi hanterar mikrotjänster

Följande hjälper till att implementera en enhetlig "partipolicy" bland många Avitos mikrotjänster:

  • dela upp infrastrukturen i lager;
  • Plattform som en tjänst (PaaS) koncept;
  • övervakar allt som händer med mikrotjänster.

Infrastrukturabstraktionsskikt inkluderar tre lager. Låt oss gå från topp till botten.

A. Topp - servicenät. Först provade vi Istio, men det visade sig att det använder för mycket resurser, vilket är för dyrt för våra volymer. Därför utvecklade senior ingenjör i arkitekturteamet Alexander Lukyanchenko sin egen lösning - Netramesh (finns i Open Source), som vi för närvarande använder i produktionen och som förbrukar flera gånger mindre resurser än Istio (men gör inte allt som Istio kan skryta med).
B. Medium - Kubernetes. Vi distribuerar och driver mikrotjänster på den.
C. Botten - ren metall. Vi använder inte moln eller saker som OpenStack, utan förlitar oss helt på bar metall.

Alla lager kombineras av PaaS. Och denna plattform består i sin tur av tre delar.

I. Generatorer, kontrolleras via ett CLI-verktyg. Det är hon som hjälper utvecklaren att skapa en mikrotjänst på rätt sätt och med ett minimum av ansträngning.

II. Konsoliderad samlare med kontroll över alla verktyg via en gemensam instrumentpanel.

III. Lagring. Ansluter till schemaläggare som automatiskt ställer in utlösare för viktiga åtgärder. Tack vare ett sådant system missas inte en enda uppgift bara för att någon glömt att ställa in en uppgift i Jira. Vi använder ett internt verktyg som heter Atlas för detta.

Vad vet vi om mikrotjänster

Implementeringen av mikrotjänster i Avito utförs också enligt ett enda schema, vilket förenklar kontrollen över dem i varje utvecklings- och releasestadium.

Hur fungerar en standardutvecklingspipeline för mikrotjänster?

Generellt ser kedjan för att skapa mikrotjänster ut så här:

CLI-push → Kontinuerlig integration → Baka → Utplacera → Konstgjorda tester → Kanarie-tester → Squeeze Testing → Produktion → Underhåll.

Låt oss gå igenom det exakt i denna ordning.

CLI-tryck

• Skapa en mikrotjänst.
Vi kämpade länge för att lära alla utvecklare hur man gör mikrotjänster. Detta inkluderade att skriva detaljerade instruktioner i Confluence. Men uppläggen förändrades och kompletterades. Resultatet är att en flaskhals dök upp i början av resan: det tog mycket längre tid att lansera mikrotjänster, och fortfarande uppstod problem ofta under deras skapande.

Till slut byggde vi ett enkelt CLI-verktyg som automatiserar de grundläggande stegen när du skapar en mikrotjänst. Faktum är att den ersätter den första git-pushen. Här är exakt vad hon gör.

— Skapar en tjänst enligt en mall — steg för steg, i "guide"-läge. Vi har mallar för de viktigaste programmeringsspråken i Avito-backend: PHP, Golang och Python.

- Ett kommando i taget distribuerar en miljö för lokal utveckling på en specifik maskin - Minikube lanseras, Helm-diagram genereras automatiskt och lanseras i lokala kubernetes.

— Ansluter den nödvändiga databasen. Utvecklaren behöver inte känna till IP, inloggning och lösenord för att få tillgång till databasen han behöver - vare sig det är lokalt, på scenen eller i produktion. Dessutom distribueras databasen omedelbart i en feltolerant konfiguration och med balansering.

— Den utför själva monteringen live. Låt oss säga att en utvecklare korrigerade något i en mikrotjänst genom sin IDE. Verktyget ser ändringar i filsystemet och, baserat på dem, bygger om programmet (för Golang) och startar om. För PHP vidarebefordrar vi helt enkelt katalogen inuti kuben och där erhålls live-reload "automatiskt".

— Genererar autotester. I form av ämnen, men ganska lämplig för användning.

• Implementering av mikrotjänster.

Att distribuera en mikrotjänst brukade vara lite jobbigt för oss. Följande krävdes:

I. Dockerfile.

II. Konfig.
III. Styrdiagram, som i sig är besvärligt och inkluderar:

— själva sjökorten.
— mallar;
— specifika värden som tar hänsyn till olika miljöer.

Vi har tagit bort smärtan av att omarbeta Kubernetes-manifest så att de nu genereras automatiskt. Men viktigast av allt, de förenklade driftsättningen till det yttersta. Från och med nu har vi en Dockerfil, och utvecklaren skriver hela konfigurationen i en enda kort app.toml-fil.

Vad vet vi om mikrotjänster

Ja, och i själva app.toml finns det inget att göra på en minut. Vi anger var och hur många kopior av tjänsten som ska samlas in (på dev-servern, på iscensättning, i produktion) och anger dess beroenden. Lägg märke till linjestorleken = "liten" i blocket [motor]. Detta är gränsen som kommer att tilldelas tjänsten via Kubernetes.

Sedan, baserat på konfigurationen, genereras alla nödvändiga Helm-diagram automatiskt och anslutningar till databaserna skapas.

• Grundläggande validering. Sådana kontroller är också automatiserade.
Behöver spåra:
— finns det en Dockerfile;
— finns det app.toml;
— finns det dokumentation tillgänglig?
— är beroendet i sin ordning?
— om varningsregler har fastställts.
Till den sista punkten: ägaren av tjänsten bestämmer själv vilka produktmått som ska övervakas.

• Upprättande av dokumentation.
Fortfarande ett problemområde. Det verkar vara det mest uppenbara, men samtidigt är det också ett rekord ”ofta bortglömt”, och därför en sårbar länk i kedjan.
Det är nödvändigt att det finns dokumentation för varje mikrotjänst. Den innehåller följande block.

I. Kort beskrivning av tjänsten. Bokstavligen några meningar om vad den gör och varför den behövs.

II. Arkitektur diagram länk. Det är viktigt att med en snabb blick på det är det lätt att till exempel förstå om du använder Redis för cachning eller som huvuddatalager i beständigt läge. I Avito för nu är detta en länk till Confluence.

III. Runbook. En kort guide om att starta tjänsten och krångligheterna med att hantera den.

IV. FAQ, där det skulle vara bra att förutse de problem som dina kollegor kan stöta på när de arbetar med tjänsten.

V. Beskrivning av slutpunkter för API. Om du plötsligt inte angav destinationerna kommer kollegor vars mikrotjänster är relaterade till din nästan säkert att betala för det. Nu använder vi Swagger och vår lösning som heter brief för detta.

VI. Etiketter. Eller markörer som visar vilken produkt, funktion eller strukturell indelning av företaget tjänsten tillhör. De hjälper dig att snabbt förstå till exempel om du skär ner funktionalitet som dina kollegor rullade ut för samma affärsenhet för en vecka sedan.

VII. Ägare eller ägare till tjänsten. I de flesta fall kan det – eller dem – bestämmas automatiskt med PaaS, men för att vara på den säkra sidan kräver vi att utvecklaren specificerar dem manuellt.

Slutligen är det en bra praxis att granska dokumentation, liknande kodgranskning.

Kontinuerlig integration

  • Förbereder förråd.
  • Skapa en pipeline i TeamCity.
  • Inställningsrättigheter.
  • Sök efter tjänsteägare. Det finns ett hybridschema här - manuell märkning och minimal automatisering från PaaS. Ett helautomatiskt schema misslyckas när tjänster överförs för support till ett annat utvecklingsteam eller till exempel om tjänsteutvecklaren slutar.
  • Registrera en tjänst i Atlas (se ovan). Med alla dess ägare och beroenden.
  • Kontrollerar migrationer. Vi kontrollerar om någon av dem är potentiellt farlig. I en av dem dyker till exempel upp en ändringstabell eller något annat som kan bryta dataschemats kompatibilitet mellan olika versioner av tjänsten. Då utförs inte migreringen, utan placeras i ett abonnemang – PaaS:en måste signalera tjänsteägaren när det är säkert att använda det.

Baka

Nästa steg är paketeringstjänster före driftsättning.

  • Bygger applikationen. Enligt klassikerna - i en Docker-bild.
  • Generering av Helm-diagram för själva tjänsten och relaterade resurser. Inklusive för databaser och cache. De skapas automatiskt i enlighet med app.toml-konfigurationen som genererades vid CLI-push-steget.
  • Skapa biljetter för administratörer att öppna portar (vid behov).
  • Köra enhetstester och beräkna kodtäckning. Om kodtäckningen är under den angivna tröskeln, kommer tjänsten troligen inte att gå längre - till driftsättning. Om det är på gränsen till acceptabelt kommer tjänsten att tilldelas en "pessimerande" koefficient: sedan, om det inte finns någon förbättring av indikatorn över tiden, kommer utvecklaren att få ett meddelande om att det inte finns några framsteg när det gäller tester ( och något måste göras åt det).
  • Redogör för minnes- och CPU-begränsningar. Vi skriver främst mikrotjänster i Golang och kör dem i Kubernetes. Därav en subtilitet förknippad med det speciella med Golang-språket: som standard, när du startar, används alla kärnor på maskinen, om du inte uttryckligen ställer in GOMAXPROCS-variabeln, och när flera sådana tjänster startas på samma maskin, börjar de att konkurrera om resurser, störa varandra. Graferna nedan visar hur exekveringstiden ändras om du kör applikationen utan tvekan och i kapplöpningsläget för resurser. (Källor till grafer är här).

Vad vet vi om mikrotjänster

Utförandetid, mindre är bättre. Max: 643 ms, minimum: 42 ms. Bilden är klickbar.

Vad vet vi om mikrotjänster

Dags för operation, mindre är bättre. Max: 14091 ns, minimum: 151 ns. Bilden är klickbar.

Vid monteringsförberedelsen kan du ställa in denna variabel explicit eller så kan du använda biblioteket automaxprocs från killarna från Uber.

Distribuera

• Kontrollera konventioner. Innan du börjar leverera serviceenheter till dina avsedda miljöer måste du kontrollera följande:
- API-slutpunkter.
— Överensstämmelse med API-slutpunkters svar med schemat.
— Loggformat.
— Ställa in rubriker för förfrågningar till tjänsten (för närvarande görs detta av netramesh)
— Ställa in ägartoken när meddelanden skickas till händelsebussen. Detta behövs för att spåra uppkopplingen av tjänster över bussen. Du kan skicka både idempotent data till bussen, vilket inte ökar uppkopplingen av tjänster (vilket är bra), och affärsdata som stärker uppkopplingen av tjänster (vilket är mycket dåligt!). Och när den här anslutningen blir ett problem, hjälper förståelsen vem som skriver och läser bussen att separera tjänsterna ordentligt.

Det finns inte särskilt många kongresser i Avito ännu, men deras pool expanderar. Ju fler sådana avtal är tillgängliga i en form som teamet kan förstå och förstå, desto lättare är det att upprätthålla överensstämmelse mellan mikrotjänster.

Syntetiska tester

• Testning med sluten slinga. För detta använder vi nu öppen källkod Hoverfly.io. Först registrerar den den verkliga belastningen på tjänsten, sedan - bara i en sluten slinga - emulerar den den.

• Stresstestning. Vi försöker få alla tjänster till optimal prestanda. Och alla versioner av varje tjänst måste vara föremål för belastningstester - på så sätt kan vi förstå tjänstens nuvarande prestanda och skillnaden med tidigare versioner av samma tjänst. Om dess prestanda sjunkit med en och en halv gång efter en tjänstuppdatering, är detta en tydlig signal för dess ägare: du måste gräva i koden och rätta till situationen.
Vi använder till exempel den insamlade informationen för att korrekt implementera automatisk skalning och i slutändan förstår vi generellt hur skalbar tjänsten är.

Vid belastningstestning kontrollerar vi om resursåtgången når uppsatta gränser. Och vi fokuserar främst på ytterligheter.

a) Vi tittar på den totala belastningen.
– För liten – troligtvis fungerar något inte alls om belastningen plötsligt sjunker flera gånger.
- För stor - optimering krävs.

b) Vi tittar på cutoff enligt RPS.
Här tittar vi på skillnaden mellan den nuvarande versionen och den tidigare och den totala kvantiteten. Till exempel, om en tjänst producerar 100 rps, är den antingen dåligt skriven, eller så är det dess specificitet, men i alla fall är detta en anledning att titta på tjänsten mycket noggrant.
Om det tvärtom finns för många RPS, så kanske det finns någon typ av bugg och några av slutpunkterna har slutat exekvera nyttolasten, och någon annan utlöses helt enkelt return true;

Kanarieprov

Efter att vi klarat de syntetiska testerna testar vi mikrotjänsten på ett litet antal användare. Vi börjar försiktigt, med en liten andel av tjänstens avsedda målgrupp – mindre än 0,1 %. I detta skede är det mycket viktigt att rätt tekniska och produktmässiga mätvärden ingår i övervakningen så att de visar problemet i tjänsten så snabbt som möjligt. Minsta tid för ett kanariefågetest är 5 minuter, den huvudsakliga är 2 timmar. För komplexa tjänster ställer vi in ​​tiden manuellt.
Vi analyserar:
— Språkspecifika mätvärden, särskilt php-fpm-arbetare.
— fel i Sentry;
— Svarsstatus.
— Svarstid, exakt och genomsnittlig.
— latens;
— Undantag, bearbetade och obehandlade.
— produktmått.

Squeeze Testing

Squeeze-testning kallas också "squeeze"-testning. Namnet på tekniken introducerades i Netflix. Dess essens är att vi först fyller en instans med verklig trafik tills vi misslyckas och sätter därmed dess gräns. Sedan lägger vi till ytterligare en instans och laddar detta par - igen till max; vi ser deras tak och delta med den första "squeeze". Och så kopplar vi en instans i taget och beräknar mönstret för förändringar.
Testdata genom "squeezing" flödar också in i en gemensam metrikdatabas, där vi antingen berikar de artificiella belastningsresultaten med dem, eller till och med ersätter "syntetik" med dem.

Produktion

• Skalning. När vi rullar ut en tjänst till produktionen övervakar vi hur den skalas. Enligt vår erfarenhet är det ineffektivt att övervaka endast CPU-indikatorer. Automatisk skalning med RPS-benchmarking i sin rena form fungerar, men bara för vissa tjänster, som onlinestreaming. Så vi tittar först på applikationsspecifika produktmått.

Som ett resultat analyserar vi vid skalning:
- CPU och RAM-indikatorer,
— antalet förfrågningar i kön,
- respons tid,
— Prognos baserad på ackumulerade historiska data.

När du skalar en tjänst är det också viktigt att övervaka dess beroenden så att vi inte skalar den första tjänsten i kedjan, och de som den har tillgång till misslyckas under belastning. För att fastställa en acceptabel belastning för hela tjänstepoolen tittar vi på historiska data för den "närmaste" beroende tjänsten (baserat på en kombination av CPU- och RAM-indikatorer, tillsammans med appspecifika mätvärden) och jämför dem med historiska data av initieringstjänsten, och så vidare genom hela "beroendekedjan", från topp till botten.

tjänsten

Efter att mikrotjänsten har tagits i drift kan vi koppla triggers till den.

Här är typiska situationer där triggers uppstår.
— Potentiellt farliga migrationer upptäckts.
— Säkerhetsuppdateringar har släppts.
— Själva tjänsten har inte uppdaterats på länge.
— Belastningen på tjänsten har minskat märkbart eller en del av dess produktmått ligger utanför det normala intervallet.
— Tjänsten uppfyller inte längre de nya plattformskraven.

Vissa av triggarna är ansvariga för driftstabilitet, vissa - som en funktion av systemunderhåll - till exempel har en del tjänst inte distribuerats på länge och dess basbild har upphört att klara säkerhetskontroller.

instrumentbräda

Kort sagt, instrumentpanelen är kontrollpanelen för hela vår PaaS.

  • En enda informationspunkt om tjänsten, med uppgifter om dess testtäckning, antalet bilder, antalet produktionsexemplar, versioner etc.
  • Ett verktyg för att filtrera data efter tjänster och etiketter (markörer för att tillhöra affärsenheter, produktfunktionalitet, etc.)
  • Ett verktyg för integration med infrastrukturverktyg för spårning, loggning och övervakning.
  • En enda servicedokumentation.
  • En enda synvinkel på alla händelser över tjänster.

Vad vet vi om mikrotjänster
Vad vet vi om mikrotjänster
Vad vet vi om mikrotjänster
Vad vet vi om mikrotjänster

Totalt

Innan en ny utvecklare introducerade PaaS kunde en ny utvecklare ägna flera veckor åt att förstå alla verktyg som behövs för att lansera en mikrotjänst i produktion: Kubernetes, Helm, våra interna TeamCity-funktioner, sätta upp anslutningar till databaser och cachar på ett feltolerant sätt, etc. Nu är det tar ett par timmar att läsa snabbstarten och skapa själva tjänsten.

Jag gav en rapport om detta ämne för HighLoad++ 2018, du kan se den video и presentation.

Bonusspår för den som läser till slutet

Vi på Avito anordnar en intern tredagarsutbildning för utvecklare från Chris Richardson, expert på mikrotjänstarkitektur. Vi vill ge möjligheten att delta i det till en av läsarna av detta inlägg. Här Utbildningsprogrammet har lagts ut.

Utbildningen kommer att äga rum den 5-7 augusti i Moskva. Det är arbetsdagar som kommer att vara fullt upptagna. Lunch och utbildning kommer att finnas på vårt kontor och den utvalda deltagaren betalar själv för resa och boende.

Du kan ansöka om deltagande i detta googleformulär. Från dig – svaret på frågan varför du behöver gå utbildningen och information om hur du kontaktar dig. Svara på engelska, eftersom Chris väljer den deltagare som ska gå utbildningen själv.
Vi kommer att meddela namnet på utbildningsdeltagaren i en uppdatering av detta inlägg och på sociala nätverk Avito för utvecklare (AvitoTech i Facebook, Vkontakte, Twitter) senast den 19 juli.

Källa: will.com

Lägg en kommentar