Hva vet vi om mikrotjenester

Hallo! Mitt navn er Vadim Madison, jeg leder utviklingen av Avito System Platform. Det har blitt sagt mer enn en gang hvordan vi i selskapet går fra en monolittisk arkitektur til en mikrotjenester. Det er på tide å dele hvordan vi har transformert infrastrukturen vår for å få mest mulig ut av mikrotjenester og forhindre at vi går oss vill i dem. Hvordan PaaS hjelper oss her, hvordan vi forenklet distribusjon og reduserte opprettelsen av en mikrotjeneste til ett klikk – les videre. Ikke alt jeg skriver om nedenfor er fullt implementert i Avito, noe av det er hvordan vi utvikler plattformen vår.

(Og på slutten av denne artikkelen vil jeg snakke om muligheten til å delta på et tre-dagers seminar fra mikrotjenestearkitekturekspert Chris Richardson).

Hva vet vi om mikrotjenester

Hvordan vi kom til mikrotjenester

Avito er et av de største klassifiserte nettstedene i verden; mer enn 15 millioner nye annonser publiseres på det per dag. Backend vår godtar mer enn 20 tusen forespørsler per sekund. Vi har for tiden flere hundre mikrotjenester.

Vi har bygget en mikrotjenestearkitektur i flere år nå. Hvordan nøyaktig - våre kolleger i detalj fortalte på vår seksjon på RIT++ 2017. På CodeFest 2017 (se. video), Sergey Orlov og Mikhail Prokopchuk forklarte i detalj hvorfor vi trengte overgangen til mikrotjenester og hvilken rolle Kubernetes spilte her. Vel, nå gjør vi alt for å minimere skaleringskostnadene som er iboende i en slik arkitektur.

I utgangspunktet skapte vi ikke et økosystem som ville hjelpe oss med å utvikle og lansere mikrotjenester. De samlet rett og slett fornuftige open source-løsninger, lanserte dem hjemme og inviterte utvikleren til å ta seg av dem. Som et resultat dro han til et dusin steder (dashbord, interne tjenester), hvoretter han ble sterkere i ønsket om å kutte kode på den gamle måten, i en monolitt. Den grønne fargen i diagrammene nedenfor indikerer hva utvikleren gjør på en eller annen måte med egne hender, og den gule fargen indikerer automatisering.

Hva vet vi om mikrotjenester

Nå i PaaS CLI-verktøyet opprettes en ny tjeneste med én kommando, og en ny database legges til med to til og distribueres til Stage.

Hva vet vi om mikrotjenester

Hvordan overvinne epoken med "mikrotjenestefragmentering"

Med en monolitisk arkitektur ble utviklerne tvunget til å finne ut hva som foregikk med naboene for å sikre konsistensen av endringer i produktet. Når man jobber med den nye arkitekturen, er ikke tjenestekontekstene lenger avhengige av hverandre.

I tillegg, for at en mikrotjenestearkitektur skal være effektiv, må mange prosesser etableres, nemlig:

• hogst;
• be om sporing (Jaeger);
• feilaggregering (Sentry);
• statuser, meldinger, hendelser fra Kubernetes (hendelsesstrømbehandling);
• rasegrense / kretsbryter (du kan bruke Hystrix);
• kontroll over tjenestetilkobling (vi bruker Netramesh);
• overvåking (Grafana);
• montering (TeamCity);
• kommunikasjon og varsling (Slack, e-post);
• oppgavesporing; (Jira)
• utarbeidelse av dokumentasjon.

For å sikre at systemet ikke mister sin integritet og forblir effektivt mens det skaleres, har vi tenkt nytt organiseringen av mikrotjenester i Avito.

Hvordan vi administrerer mikrotjenester

Følgende hjelper til med å implementere en enhetlig "partipolitikk" blant mange Avito-mikrotjenester:

  • dele infrastruktur i lag;
  • Plattform som en tjeneste (PaaS) konsept;
  • overvåker alt som skjer med mikrotjenester.

Abstraksjonslag for infrastruktur inkluderer tre lag. La oss gå fra topp til bunn.

A. Topp - servicenett. Først prøvde vi Istio, men det viste seg at det bruker for mange ressurser, noe som er for dyrt for volumene våre. Derfor utviklet senioringeniør i arkitektteamet Alexander Lukyanchenko sin egen løsning - Netramesh (tilgjengelig i Open Source), som vi for tiden bruker i produksjon og som bruker flere ganger mindre ressurser enn Istio (men gjør ikke alt som Istio kan skryte av).
B. Middels - Kubernetes. Vi distribuerer og driver mikrotjenester på den.
C. Bunn - bart metall. Vi bruker ikke skyer eller ting som OpenStack, men stoler helt på bart metall.

Alle lag er kombinert av PaaS. Og denne plattformen består på sin side av tre deler.

I. Generatorer, kontrollert via et CLI-verktøy. Det er hun som hjelper utvikleren med å lage en mikrotjeneste på riktig måte og med et minimum av innsats.

II. Konsolidert samler med kontroll over alle verktøy gjennom et felles dashbord.

III. Oppbevaring. Kobler til planleggere som automatisk setter utløsere for betydelige handlinger. Takket være et slikt system går man ikke glipp av en eneste oppgave bare fordi noen har glemt å sette opp en oppgave i Jira. Vi bruker et internt verktøy kalt Atlas til dette.

Hva vet vi om mikrotjenester

Implementeringen av mikrotjenester i Avito utføres også i henhold til en enkelt ordning, som forenkler kontrollen over dem på hvert trinn av utvikling og utgivelse.

Hvordan fungerer en standard mikrotjenesteutviklingspipeline?

Generelt ser opprettingskjeden for mikrotjenester slik ut:

CLI-push → Kontinuerlig integrasjon → Bake → Utplassering → Kunstige tester → Kanarietester → Klemtesting → Produksjon → Vedlikehold.

La oss gå gjennom det nøyaktig i denne rekkefølgen.

CLI-trykk

• Opprette en mikrotjeneste.
Vi slet lenge med å lære alle utviklere hvordan de kan utføre mikrotjenester. Dette inkluderte å skrive detaljerte instruksjoner i Confluence. Men ordningene endret seg og ble supplert. Resultatet er at en flaskehals dukket opp i begynnelsen av reisen: det tok mye mer tid å lansere mikrotjenester, og fortsatt oppsto det ofte problemer under opprettelsen av dem.

Til slutt bygde vi et enkelt CLI-verktøy som automatiserer de grunnleggende trinnene når du oppretter en mikrotjeneste. Faktisk erstatter den det første git-pushet. Her er nøyaktig hva hun gjør.

— Oppretter en tjeneste i henhold til en mal — trinn for trinn, i «veiviser»-modus. Vi har maler for hovedprogrammeringsspråkene i Avito-backend: PHP, Golang og Python.

- En kommando om gangen distribuerer et miljø for lokal utvikling på en spesifikk maskin - Minikube lanseres, Helm-diagrammer genereres automatisk og lanseres i lokale kubernetes.

— Kobler den nødvendige databasen. Utvikleren trenger ikke å vite IP, pålogging og passord for å få tilgang til databasen han trenger – det være seg lokalt, på scenen eller i produksjon. Dessuten distribueres databasen umiddelbart i en feiltolerant konfigurasjon og med balansering.

— Den utfører selv levende montering. La oss si at en utvikler korrigerte noe i en mikrotjeneste gjennom sin IDE. Verktøyet ser endringer i filsystemet og, basert på dem, gjenoppbygger programmet (for Golang) og starter på nytt. For PHP videresender vi ganske enkelt katalogen inne i kuben og der oppnås live-reload "automatisk".

— Genererer autotester. I form av emner, men ganske egnet for bruk.

• Implementering av mikrotjenester.

Å distribuere en mikrotjeneste pleide å være litt av et ork for oss. Følgende var påkrevd:

I. Dockerfile.

II. Konfig.
III. Rordiagram, som i seg selv er tungvint og inkluderer:

— selve diagrammene;
— maler;
— spesifikke verdier som tar hensyn til ulike miljøer.

Vi har tatt smerten av å omarbeide Kubernetes-manifester, slik at de nå genereres automatisk. Men viktigst av alt, de forenklet distribusjonen til det ytterste. Fra nå av har vi en Dockerfile, og utvikleren skriver hele konfigurasjonen i en enkelt kort app.toml-fil.

Hva vet vi om mikrotjenester

Ja, og i selve app.toml er det ingenting å gjøre på et øyeblikk. Vi spesifiserer hvor og hvor mange kopier av tjenesten som skal hentes (på utviklerserveren, på iscenesettelse, i produksjon), og angir dens avhengigheter. Legg merke til linjestørrelsen = "liten" i [motor]-blokken. Dette er grensen som vil bli tildelt tjenesten via Kubernetes.

Deretter, basert på konfigurasjonen, genereres alle nødvendige Helm-diagrammer automatisk og tilkoblinger til databasene opprettes.

• Grunnleggende validering. Slike kontroller er også automatiserte.
Trenger å spore:
— er det en Dockerfile;
— er det app.toml;
– finnes det dokumentasjon?
— er avhengigheten i orden?
— om det er satt varslingsregler.
Til siste punkt: eieren av tjenesten bestemmer selv hvilke produktmålinger som skal overvåkes.

• Utarbeidelse av dokumentasjon.
Fortsatt et problemområde. Det ser ut til å være det mest åpenbare, men samtidig er det også en rekord "ofte glemt", og derfor et sårbart ledd i kjeden.
Det er nødvendig at det finnes dokumentasjon for hver mikrotjeneste. Den inkluderer følgende blokker.

I. Kort beskrivelse av tjenesten. Bokstavelig talt noen setninger om hva den gjør og hvorfor den er nødvendig.

II. Link til arkitekturdiagram. Det er viktig at det med et raskt blikk er lett å forstå, for eksempel om du bruker Redis for caching eller som hoveddatalager i vedvarende modus. I Avito foreløpig er dette en lenke til Confluence.

III. Runbook. En kort veiledning om hvordan du starter tjenesten og vanskelighetene ved å håndtere den.

IV. FAQ, der det vil være greit å forutse problemene som kollegene dine kan støte på når de jobber med tjenesten.

V. Beskrivelse av endepunkter for API. Hvis du plutselig ikke spesifiserte destinasjonene, vil kolleger hvis mikrotjenester er relatert til dine, nesten helt sikkert betale for det. Nå bruker vi Swagger og løsningen vår som heter brief for dette.

VI. Etiketter. Eller markører som viser hvilket produkt, funksjonalitet eller strukturell inndeling av selskapet tjenesten tilhører. De hjelper deg raskt å forstå, for eksempel om du kutter funksjonalitet som kollegene dine rullet ut for samme forretningsenhet for en uke siden.

VII. Eier eller eiere av tjenesten. I de fleste tilfeller kan det – eller dem – bestemmes automatisk ved hjelp av PaaS, men for å være på den sikre siden krever vi at utvikleren spesifiserer dem manuelt.

Til slutt er det en god praksis å gjennomgå dokumentasjon, på samme måte som kodegjennomgang.

Kontinuerlig integrasjon

  • Klargjøring av depoter.
  • Opprette en pipeline i TeamCity.
  • Sette rettigheter.
  • Søk etter tjenesteeiere. Her er det en hybridordning - manuell merking og minimal automatisering fra PaaS. Et helautomatisk opplegg mislykkes når tjenester overføres for støtte til et annet utviklingsteam eller for eksempel hvis tjenesteutvikleren slutter.
  • Registrere en tjeneste i Atlas (se ovenfor). Med alle sine eiere og avhengigheter.
  • Sjekker migreringer. Vi sjekker om noen av dem er potensielt farlige. For eksempel, i en av dem dukker det opp en endringstabell eller noe annet som kan bryte kompatibiliteten til dataskjemaet mellom forskjellige versjoner av tjenesten. Da utføres ikke migreringen, men legges i et abonnement – ​​PaaS-en må signalisere tjenesteeier når det er trygt å bruke det.

Varsel

Neste trinn er pakketjenester før distribusjon.

  • Bygger applikasjonen. I følge klassikerne - i et Docker-bilde.
  • Generering av Helm-diagrammer for selve tjenesten og relaterte ressurser. Inkludert for databaser og cache. De opprettes automatisk i samsvar med app.toml-konfigurasjonen som ble generert på CLI-push-stadiet.
  • Opprette billetter for administratorer til åpne porter (når det trengs).
  • Kjøre enhetstester og beregne kodedekning. Hvis kodedekningen er under den angitte terskelen, vil tjenesten mest sannsynlig ikke gå videre - til distribusjon. Hvis det er på grensen til akseptabelt, vil tjenesten bli tildelt en "pessimerende" koeffisient: så, hvis det ikke er noen forbedring i indikatoren over tid, vil utvikleren motta et varsel om at det ikke er noen fremgang når det gjelder tester ( og noe må gjøres med det).
  • Ta hensyn til minne- og CPU-begrensninger. Vi skriver hovedsakelig mikrotjenester i Golang og kjører dem i Kubernetes. Derav en subtilitet knyttet til det særegne ved Golang-språket: som standard, når du starter, brukes alle kjerner på maskinen, hvis du ikke eksplisitt angir GOMAXPROCS-variabelen, og når flere slike tjenester startes på samme maskin, begynner de å konkurrere om ressurser, forstyrre hverandre. Grafene nedenfor viser hvordan utførelsestiden endres hvis du kjører applikasjonen uten strid og i kappløpet om ressurser-modus. (Kilder til grafer er her).

Hva vet vi om mikrotjenester

Utførelsestid, mindre er bedre. Maksimum: 643 ms, minimum: 42 ms. Bildet er klikkbart.

Hva vet vi om mikrotjenester

Tid for operasjon, mindre er bedre. Maksimum: 14091 ns, minimum: 151 ns. Bildet er klikkbart.

På monteringsforberedelsesstadiet kan du angi denne variabelen eksplisitt, eller du kan bruke biblioteket automaxprocs fra gutta fra Uber.

Utplassere

• Sjekke konvensjoner. Før du begynner å levere serviceenheter til de tiltenkte miljøene, må du sjekke følgende:
- API-endepunkter.
— Overholdelse av API-endepunktsvar med skjemaet.
— Loggformat.
— Sette overskrifter for forespørsler til tjenesten (for øyeblikket gjøres dette av netramesh)
— Angi eiertoken når du sender meldinger til hendelsesbussen. Dette er nødvendig for å spore tilkoblingen til tjenester på tvers av bussen. Du kan sende både idempotente data til bussen, som ikke øker tilkoblingen til tjenester (som er bra), og forretningsdata som styrker tilkoblingen til tjenester (som er veldig dårlig!). Og når denne tilkoblingen blir et problem, hjelper det å forstå hvem som skriver og leser bussen til å skille tjenester på riktig måte.

Det er ikke så mange stevner i Avito ennå, men bassenget deres utvides. Jo flere slike avtaler er tilgjengelige i en form som teamet kan forstå og forstå, jo lettere er det å opprettholde konsistens mellom mikrotjenester.

Syntetiske tester

• Closed loop testing. Til dette bruker vi nå åpen kildekode Hoverfly.io. Først registrerer den den virkelige belastningen på tjenesten, deretter - bare i en lukket sløyfe - emulerer den den.

• Stresstesting. Vi prøver å bringe alle tjenester til optimal ytelse. Og alle versjoner av hver tjeneste må være gjenstand for belastningstesting - på denne måten kan vi forstå den nåværende ytelsen til tjenesten og forskjellen med tidligere versjoner av samme tjeneste. Hvis ytelsen falt med en og en halv gang etter en tjenesteoppdatering, er dette et klart signal for eierne: du må grave i koden og rette opp situasjonen.
Vi bruker for eksempel de innsamlede dataene til å implementere automatisk skalering på riktig måte, og til slutt forstår vi generelt hvor skalerbar tjenesten er.

Under lasttesting sjekker vi om ressursforbruket holder de fastsatte grensene. Og vi fokuserer først og fremst på ytterpunkter.

a) Vi ser på den totale belastningen.
- For lite - mest sannsynlig er det noe som ikke fungerer i det hele tatt hvis belastningen plutselig synker flere ganger.
- For stor - optimalisering kreves.

b) Vi ser på cutoff i henhold til RPS.
Her ser vi på forskjellen mellom den nåværende versjonen og den forrige og det totale antallet. For eksempel, hvis en tjeneste produserer 100 rps, er den enten dårlig skrevet, eller dette er dens spesifisitet, men i alle fall er dette en grunn til å se på tjenesten veldig nøye.
Hvis det tvert imot er for mange RPS, så er det kanskje en slags feil og noen av endepunktene har sluttet å utføre nyttelasten, og noen andre blir rett og slett trigget return true;

Kanariprøver

Etter at vi har bestått de syntetiske testene, tester vi mikrotjenesten på et lite antall brukere. Vi starter forsiktig, med en liten andel av tjenestens tiltenkte publikum – mindre enn 0,1 %. På dette stadiet er det svært viktig at korrekte tekniske og produktmessige beregninger er inkludert i overvåkingen slik at de viser problemet i tjenesten så raskt som mulig. Minimumstiden for en kanariprøve er 5 minutter, den viktigste er 2 timer. For komplekse tjenester setter vi tiden manuelt.
La oss analysere:
— språkspesifikke beregninger, spesielt php-fpm-arbeidere;
— feil i Sentry;
— svarstatuser;
— responstid, eksakt og gjennomsnittlig;
- ventetid;
— unntak, behandlet og ubehandlet;
— produktberegninger.

Klemtesting

Klemtesting kalles også "klemmetesting". Navnet på teknikken ble introdusert i Netflix. Essensen er at vi først fyller en forekomst med ekte trafikk til feilen og dermed setter grensen. Så legger vi til en ny instans og laster inn dette paret - igjen til det maksimale; vi ser taket og deltaet deres med den første "klemmen". Så vi kobler sammen én instans om gangen og beregner mønsteret av endringer.
Testdata gjennom "klemming" strømmer også inn i en vanlig metrikkdatabase, hvor vi enten beriker de kunstige belastningsresultatene med dem, eller til og med erstatter "syntetiske stoffer" med dem.

Produksjon

• Skalering. Når vi ruller ut en tjeneste til produksjon, overvåker vi hvordan den skaleres. Vår erfaring er at kun overvåking av CPU-indikatorer er ineffektivt. Automatisk skalering med RPS-benchmarking i sin rene form fungerer, men bare for visse tjenester, for eksempel nettstrømming. Så vi ser først på applikasjonsspesifikke produktberegninger.

Som et resultat, når vi skalering analyserer vi:
- CPU- og RAM-indikatorer,
– antall forespørsler i køen,
- responstid,
— prognose basert på akkumulerte historiske data.

Når du skalerer en tjeneste, er det også viktig å overvåke dens avhengigheter slik at vi ikke skalerer den første tjenesten i kjeden, og de den får tilgang til mislykkes under belastning. For å etablere en akseptabel belastning for hele utvalget av tjenester, ser vi på de historiske dataene til den "nærmeste" avhengige tjenesten (basert på en kombinasjon av CPU- og RAM-indikatorer, kombinert med appspesifikke beregninger) og sammenligner dem med de historiske dataene av initialiseringstjenesten, og så videre gjennom hele "avhengighetskjeden", fra topp til bunn.

tjeneste

Etter at mikrotjenesten er satt i drift, kan vi feste triggere til den.

Her er typiske situasjoner der triggere oppstår.
— Potensielt farlige migrasjoner oppdaget.
— Sikkerhetsoppdateringer er utgitt.
— Selve tjenesten har ikke vært oppdatert på lenge.
— Belastningen på tjenesten har blitt merkbart redusert eller noen av produktberegningene er utenfor normalområdet.
— Tjenesten oppfyller ikke lenger de nye plattformkravene.

Noen av utløserne er ansvarlige for driftsstabilitet, noen - som en funksjon av systemvedlikehold - for eksempel har noen tjenester ikke vært utplassert på lenge og basisbildet har sluttet å bestå sikkerhetskontroller.

Dashbord

Kort fortalt er dashbordet kontrollpanelet til hele vår PaaS.

  • Et enkelt informasjonspunkt om tjenesten, med data om testdekningen, antall bilder, antall produksjonseksemplarer, versjoner osv.
  • Et verktøy for å filtrere data etter tjenester og etiketter (markører for tilhørighet til forretningsenheter, produktfunksjonalitet, etc.)
  • Et verktøy for integrasjon med infrastrukturverktøy for sporing, logging og overvåking.
  • Dokumentasjon for ett enkelt servicepunkt.
  • Et enkelt synspunkt på alle hendelser på tvers av tjenester.

Hva vet vi om mikrotjenester
Hva vet vi om mikrotjenester
Hva vet vi om mikrotjenester
Hva vet vi om mikrotjenester

Totalt

Før vi introduserer PaaS, kan en ny utvikler bruke flere uker på å forstå alle verktøyene som er nødvendige for å lansere en mikrotjeneste i produksjon: Kubernetes, Helm, våre interne TeamCity-funksjoner, sette opp tilkoblinger til databaser og cacher på en feiltolerant måte, osv. Nå tar et par timer å lese hurtigstarten og lage selve tjenesten.

Jeg ga en rapport om dette emnet for HighLoad++ 2018, du kan se den video и presentasjon.

Bonusspor for de som leser til slutt

Vi i Avito arrangerer en intern tredagers opplæring for utviklere fra Chris Richardson, en ekspert på mikrotjenestearkitektur. Vi vil gjerne gi muligheten til å delta i det til en av leserne av dette innlegget. Her Treningsprogrammet er lagt ut.

Treningen vil finne sted fra 5. til 7. august i Moskva. Dette er arbeidsdager som vil være fullt opptatt. Lunsj og trening vil være på vårt kontor, og den utvalgte deltakeren betaler reise og opphold selv.

Du kan søke om deltakelse i dette google-skjemaet. Fra deg - svaret på spørsmålet om hvorfor du trenger å delta på opplæringen og informasjon om hvordan du kan kontakte deg. Svar på engelsk, for Chris velger selv deltakeren som skal delta på treningen.
Vi vil kunngjøre navnet på treningsdeltakeren i en oppdatering av dette innlegget og på sosiale nettverk Avito for utviklere (AvitoTech i Фейсбуке, VKontakte, Twitter) senest 19. juli.

Kilde: www.habr.com

Legg til en kommentar