Ce știm despre microservicii

Buna ziua! Numele meu este Vadim Madison, conduc dezvoltarea platformei de sistem Avito. S-a spus de mai multe ori cum noi în companie trecem de la o arhitectură monolitică la una de microservicii. Este timpul să împărtășim cum ne-am transformat infrastructura pentru a profita la maximum de microservicii și a preveni să ne pierdem în ele. Cum ne ajută PaaS aici, cum am simplificat implementarea și am redus crearea unui microserviciu la un singur clic - citiți mai departe. Nu tot ce scriu mai jos este implementat pe deplin în Avito, o parte este modul în care ne dezvoltăm platforma.

(Și la sfârșitul acestui articol, voi vorbi despre oportunitatea de a participa la un seminar de trei zile de la expertul în arhitectură de microservicii Chris Richardson).

Ce știm despre microservicii

Cum am ajuns la microservicii

Avito este unul dintre cele mai mari site-uri clasificate din lume; peste 15 milioane de reclame noi sunt publicate pe el pe zi. Backend-ul nostru acceptă mai mult de 20 de mii de solicitări pe secundă. În prezent avem câteva sute de microservicii.

Construim o arhitectură de microservicii de câțiva ani. Cum anume - colegii noștri în detaliu a spus la secțiunea noastră de la RIT++ 2017. La CodeFest 2017 (vezi. video), Sergey Orlov și Mikhail Prokopchuk au explicat în detaliu de ce aveam nevoie de tranziția la microservicii și ce rol a jucat Kubernetes aici. Ei bine, acum facem totul pentru a minimiza costurile de scalare care sunt inerente unei astfel de arhitecturi.

Inițial, nu am creat un ecosistem care să ne ajute în mod cuprinzător să dezvoltăm și să lansăm microservicii. Pur și simplu au colectat soluții open source sensibile, le-au lansat acasă și au invitat dezvoltatorul să se ocupe de ele. Drept urmare, a mers într-o duzină de locuri (tablete de bord, servicii interne), după care a devenit mai puternic în dorința de a tăia codul în mod vechi, într-un monolit. Culoarea verde din diagramele de mai jos indică ceea ce dezvoltatorul face într-un fel sau altul cu propriile mâini, iar culoarea galbenă indică automatizarea.

Ce știm despre microservicii

Acum, în utilitarul PaaS CLI, un nou serviciu este creat cu o singură comandă și o nouă bază de date este adăugată cu încă două și implementată în Stage.

Ce știm despre microservicii

Cum să depășiți era „fragmentării microserviciilor”

Cu o arhitectură monolitică, de dragul coerenței schimbărilor în produs, dezvoltatorii au fost forțați să-și dea seama ce se întâmplă cu vecinii lor. Când lucrați la noua arhitectură, contextele de servicii nu mai depind unele de altele.

În plus, pentru ca o arhitectură de microservicii să fie eficientă, trebuie stabilite multe procese și anume:

• Logare;
• urmărirea cererii (Jaeger);
• agregarea erorilor (Sentry);
• stări, mesaje, evenimente din Kubernetes (Event Stream Processing);
• limită de cursă / întrerupător (puteți folosi Hystrix);
• controlul conectivității serviciului (folosim Netramesh);
• monitorizare (Grafana);
• asamblare (TeamCity);
• comunicare si notificare (Slack, email);
• urmărirea sarcinilor; (Jira)
• pregătirea documentaţiei.

Pentru a ne asigura că sistemul nu își pierde integritatea și rămâne eficient pe măsură ce se extinde, am regândit organizarea microserviciilor în Avito.

Cum gestionăm microservicii

Următoarele ajută la implementarea unei „politici de partid” unificate printre multe microservicii Avito:

  • împărțirea infrastructurii în straturi;
  • Concept Platform as a Service (PaaS);
  • monitorizarea a tot ceea ce se întâmplă cu microservicii.

Straturile de abstractizare a infrastructurii includ trei straturi. Să mergem de sus în jos.

A. Top - plasă de serviciu. La început am încercat Istio, dar s-a dovedit că folosește prea multe resurse, ceea ce este prea scump pentru volumele noastre. Prin urmare, inginer senior din echipa de arhitectură Alexander Lukyanchenko și-a dezvoltat propria soluție - Netramesh (disponibil în Open Source), pe care îl folosim în prezent în producție și care consumă de câteva ori mai puține resurse decât Istio (dar nu face tot ce se poate lăuda Istio).
B. Mediu - Kubernetes. Implementăm și operăm microservicii pe acesta.
C. Fund - metal gol. Nu folosim nori sau lucruri precum OpenStack, ci ne bazăm în întregime pe bare metal.

Toate straturile sunt combinate prin PaaS. Și această platformă, la rândul său, constă din trei părți.

I. Generatoare, controlat printr-un utilitar CLI. Ea este cea care ajută dezvoltatorul să creeze un microserviciu în mod corect și cu un minim de efort.

II. Colector consolidat cu controlul tuturor instrumentelor printr-un tablou de bord comun.

III. Depozitare. Se conectează cu programatori care setează automat declanșatoare pentru acțiuni semnificative. Datorită unui astfel de sistem, nici o sarcină nu este ratată doar pentru că cineva a uitat să configureze o sarcină în Jira. Pentru aceasta folosim un instrument intern numit Atlas.

Ce știm despre microservicii

Implementarea microserviciilor în Avito se realizează, de asemenea, conform unei scheme unice, care simplifică controlul asupra acestora la fiecare etapă de dezvoltare și lansare.

Cum funcționează o conductă standard de dezvoltare de microservicii?

În general, lanțul de creare a microserviciilor arată astfel:

CLI-push → Integrare continuă → Coace → Implementare → Teste artificiale → Teste Canary → Testare Squeeze → Producție → Întreținere.

Să o parcurgem exact în această ordine.

CLI-push

• Crearea unui microserviciu.
Ne-am luptat mult timp să învățăm fiecare dezvoltator cum să facă microservicii. Aceasta a inclus scrierea de instrucțiuni detaliate în Confluence. Dar schemele s-au schimbat și au fost completate. Rezultatul este că la începutul călătoriei a apărut un blocaj: a durat mult mai mult timp pentru a lansa microservicii și totuși au apărut adesea probleme în timpul creării lor.

În cele din urmă, am construit un utilitar CLI simplu care automatizează pașii de bază la crearea unui microserviciu. De fapt, înlocuiește primul git push. Iată ce face ea exact.

— Creează un serviciu după un șablon — pas cu pas, în modul „vrăjitor”. Avem șabloane pentru principalele limbaje de programare din backend-ul Avito: PHP, Golang și Python.

- O comandă la un moment dat implementează un mediu pentru dezvoltarea locală pe o anumită mașină - Minikube este lansat, diagramele Helm sunt generate automat și lansate în kubernetes locale.

— Conectează baza de date necesară. Dezvoltatorul nu trebuie să cunoască IP-ul, autentificarea și parola pentru a obține acces la baza de date de care are nevoie - fie local, la Stage sau în producție. Mai mult, baza de date este implementată imediat într-o configurație tolerantă la erori și cu echilibrare.

— Realizează singur asamblarea sub tensiune. Să presupunem că un dezvoltator a corectat ceva într-un microserviciu prin intermediul IDE-ului său. Utilitarul vede modificări în sistemul de fișiere și, pe baza acestora, reconstruiește aplicația (pentru Golang) și repornește. Pentru PHP, pur și simplu trimitem directorul în interiorul cubului și acolo reîncărcarea live se obține „automat”.

— generează autotestări. Sub formă de semifabricate, dar destul de potrivite pentru utilizare.

• Implementarea microserviciilor.

Implementarea unui microserviciu era un pic o corvoadă pentru noi. Au fost necesare următoarele:

I. Dockerfile.

II. Config.
III. Diagrama de cârmă, care în sine este greoaie și include:

— diagramele în sine;
— șabloane;
— valori specifice ținând cont de diferite medii.

Am eliminat durerea din reelaborarea manifestelor Kubernetes, astfel încât acestea sunt acum generate automat. Dar cel mai important, au simplificat implementarea la limită. De acum încolo avem un Dockerfile, iar dezvoltatorul scrie întreaga configurație într-un singur fișier scurt app.toml.

Ce știm despre microservicii

Da, iar în app.toml în sine nu este nimic de făcut pentru un minut. Specificăm unde și câte copii ale serviciului să ridicăm (pe serverul de dezvoltare, pe staging, în producție) și indicăm dependențele acestuia. Observați dimensiunea liniei = „mic” în blocul [motor]. Aceasta este limita care va fi alocată serviciului prin Kubernetes.

Apoi, pe baza configurației, toate diagramele Helm necesare sunt generate automat și sunt create conexiuni la bazele de date.

• Validare de bază. Astfel de verificări sunt, de asemenea, automatizate.
Trebuie să urmăriți:
— există un Dockerfile;
— există app.toml;
- există documentație disponibilă?
— este în regulă dependența?
— dacă au fost stabilite reguli de alertă.
Până la ultimul punct: proprietarul serviciului determină însuși ce măsurători de produs să monitorizeze.

• Întocmirea documentaţiei.
Încă o zonă cu probleme. Pare a fi cel mai evident, dar în același timp este și un record „deseori uitat”, și deci o verigă vulnerabilă a lanțului.
Este necesar să existe documentație pentru fiecare microserviciu. Include următoarele blocuri.

I. Scurtă descriere a serviciului. Literal, câteva propoziții despre ceea ce face și de ce este nevoie.

II. Link diagramă arhitectură. Este important ca, printr-o privire rapidă asupra acestuia, să fie ușor de înțeles, de exemplu, dacă utilizați Redis pentru stocarea în cache sau ca depozit de date principal în modul persistent. În Avito, deocamdată, aceasta este o legătură către Confluence.

III. Runbook. Un scurt ghid despre pornirea serviciului și despre complexitățile gestionării acestuia.

IV. FAQ, unde ar fi bine să anticipați problemele pe care le pot întâmpina colegii atunci când lucrează cu serviciul.

V. Descrierea punctelor finale pentru API. Dacă dintr-o dată nu ai specificat destinațiile, aproape sigur că vor plăti pentru asta colegii ale căror microservicii sunt legate de ale tale. Acum folosim Swagger și soluția noastră numită brief pentru asta.

VI. Etichete. Sau marcatori care arată căreia produs, funcționalitate sau diviziune structurală a companiei îi aparține serviciul. Ele vă ajută să înțelegeți rapid, de exemplu, dacă reduceți funcționalitatea pe care colegii dvs. le-au lansat pentru aceeași unitate de afaceri în urmă cu o săptămână.

VII. Proprietarul sau proprietarii serviciului. În cele mai multe cazuri, acesta - sau ei - poate fi determinat automat utilizând PaaS, dar pentru a fi sigur, solicităm dezvoltatorului să le specifice manual.

În cele din urmă, este o practică bună să revizuiți documentația, similar cu revizuirea codului.

Integrare continuă

  • Pregătirea depozitelor.
  • Crearea unei conducte în TeamCity.
  • Stabilirea drepturilor.
  • Căutați proprietari de servicii. Există o schemă hibridă aici - marcare manuală și automatizare minimă de la PaaS. O schemă complet automată eșuează atunci când serviciile sunt transferate pentru asistență către o altă echipă de dezvoltare sau, de exemplu, dacă dezvoltatorul de servicii renunță.
  • Înregistrarea unui serviciu în Atlas (Vezi deasupra). Cu toți proprietarii și dependențele lui.
  • Verificarea migrațiilor. Verificăm dacă vreuna dintre ele este potențial periculoasă. De exemplu, într-una dintre ele apare un tabel de modificare sau altceva care poate rupe compatibilitatea schemei de date între diferite versiuni ale serviciului. Apoi migrarea nu este efectuată, ci plasată într-un abonament - PaaS trebuie să semnaleze proprietarul serviciului când este sigur să îl folosească.

Coace

Următoarea etapă este ambalarea serviciilor înainte de implementare.

  • Construirea aplicației. Potrivit clasicilor - într-o imagine Docker.
  • Generarea de diagrame Helm pentru serviciul în sine și resursele aferente. Inclusiv pentru baze de date și cache. Acestea sunt create automat în conformitate cu configurația app.toml care a fost generată în etapa CLI-push.
  • Crearea de bilete pentru administratori pentru a deschide porturi (când este necesar).
  • Executarea testelor unitare și calcularea acoperirii codului. Dacă acoperirea codului este sub pragul specificat, atunci cel mai probabil serviciul nu va merge mai departe - până la implementare. Dacă este pe punctul de a fi acceptabil, atunci serviciului i se va atribui un coeficient „pesimizant”: apoi, dacă nu există nicio îmbunătățire a indicatorului în timp, dezvoltatorul va primi o notificare că nu există progrese în ceea ce privește testele ( și trebuie făcut ceva în privința asta).
  • Luarea în considerare a limitărilor de memorie și CPU. Scriem în principal microservicii în Golang și le rulăm în Kubernetes. Prin urmare, o subtilitate asociată cu particularitățile limbajului Golang: în mod implicit, la pornire, sunt folosite toate nucleele de pe mașină, dacă nu setați în mod explicit variabila GOMAXPROCS și când mai multe astfel de servicii sunt lansate pe aceeași mașină, acestea încep să concureze pentru resurse, interferând unul cu celălalt. Graficele de mai jos arată cum se schimbă timpul de execuție dacă rulați aplicația fără dispute și în modul cursă pentru resurse. (Sursele graficelor sunt aici).

Ce știm despre microservicii

Timp de execuție, mai puțin este mai bine. Maxim: 643 ms, minim: 42 ms. Poza se poate face clic.

Ce știm despre microservicii

E timpul pentru operație, mai puțin este mai bine. Maxim: 14091 ns, minim: 151 ns. Poza se poate face clic.

În etapa de pregătire a asamblarii, puteți seta această variabilă în mod explicit sau puteți utiliza biblioteca automaxprocs de la băieții de la Uber.

Implementează

• Verificarea convenţiilor. Înainte de a începe să livrați ansambluri de service în mediile dorite, trebuie să verificați următoarele:
- Puncte finale API.
— Conformitatea răspunsurilor punctelor finale API cu schema.
— Format jurnal.
— Setarea antetelor pentru solicitările către serviciu (în prezent acest lucru se face de netramesh)
— Setarea simbolului proprietarului atunci când trimiteți mesaje către magistrala de evenimente. Acest lucru este necesar pentru a urmări conectivitatea serviciilor de-a lungul autobuzului. Puteți trimite atât date idempotente către autobuz, care nu măresc conectivitatea serviciilor (ceea ce este bine), cât și date de afaceri care întăresc conectivitatea serviciilor (ceea ce este foarte rău!). Și în momentul în care această conectivitate devine o problemă, înțelegerea cine scrie și citește autobuzul ajută la separarea corectă a serviciilor.

În Avito nu sunt încă foarte multe convenții, dar piscina lor se extinde. Cu cât sunt disponibile mai multe astfel de acorduri într-o formă pe care echipa o poate înțelege și înțelege, cu atât este mai ușor să mențineți coerența între microservicii.

Teste sintetice

• Testare în buclă închisă. Pentru aceasta, acum folosim open source Hoverfly.io. Mai întâi, înregistrează sarcina reală a serviciului, apoi - doar într-o buclă închisă - o emulează.

• Testare stresanta. Încercăm să aducem toate serviciile la performanțe optime. Și toate versiunile fiecărui serviciu trebuie să fie supuse testării de încărcare - astfel putem înțelege performanța actuală a serviciului și diferența cu versiunile anterioare ale aceluiași serviciu. Dacă, după o actualizare a serviciului, performanța sa a scăzut de o dată și jumătate, acesta este un semnal clar pentru proprietarii săi: trebuie să cercetați codul și să corectați situația.
Utilizăm datele colectate, de exemplu, pentru a implementa corect scalarea automată și, în cele din urmă, pentru a înțelege în general cât de scalabil este serviciul.

În timpul testării de încărcare, verificăm dacă consumul de resurse îndeplinește limitele stabilite. Și ne concentrăm în primul rând pe extreme.

a) Ne uităm la sarcina totală.
- Prea mic - cel mai probabil ceva nu funcționează deloc dacă sarcina scade brusc de mai multe ori.
- Prea mare - este necesară optimizarea.

b) Ne uităm la cutoff conform RPS.
Aici ne uităm la diferența dintre versiunea actuală și cea anterioară și cantitatea totală. De exemplu, dacă un serviciu produce 100 rps, atunci este fie prost scris, fie acesta este specificul său, dar în orice caz, acesta este un motiv pentru a privi serviciul foarte atent.
Dacă, dimpotrivă, există prea multe RPS, atunci poate că există un fel de eroare și unele dintre punctele finale au încetat să execute sarcina utilă, iar altul este pur și simplu declanșat return true;

Teste canare

După ce trecem testele sintetice, testăm microserviciul pe un număr mic de utilizatori. Începem cu atenție, cu o mică parte din publicul vizat al serviciului - mai puțin de 0,1%. În această etapă, este foarte important ca în monitorizare să fie incluse valorile tehnice și de produs corecte, astfel încât să arate cât mai repede problema în serviciu. Timpul minim pentru un test canar este de 5 minute, cel principal este de 2 ore. Pentru servicii complexe, setăm ora manual.
Analizăm:
— metrici specifice limbii, în special lucrătorii php-fpm;
— erori în Sentry;
— stări de răspuns;
— timp de răspuns, exact și mediu;
— latența;
— excepții, procesate și netratate;
— valorile produsului.

Testarea de strângere

Testarea de strângere se mai numește și testarea de „strângere”. Numele tehnicii a fost introdus în Netflix. Esența sa este că mai întâi umplem o instanță cu trafic real până la punctul de eșec și astfel îi stabilim limita. Apoi adăugăm o altă instanță și încărcăm această pereche - din nou la maximum; le vedem plafonul și delta cu prima „strângere”. Și astfel conectăm o instanță la un moment dat și calculăm modelul modificărilor.
Datele de testare prin „strângere” curg, de asemenea, într-o bază de date de valori comună, unde fie îmbogățim rezultatele încărcării artificiale cu ele, fie chiar înlocuim „sinteticele” cu ele.

Productie

• Scalare. Când lansăm un serviciu în producție, monitorizăm modul în care acesta se extinde. Din experiența noastră, monitorizarea numai a indicatorilor CPU este ineficientă. Scalare automată cu benchmarking RPS în forma sa pură funcționează, dar numai pentru anumite servicii, cum ar fi streaming online. Așa că ne uităm mai întâi la valorile produsului specifice aplicației.

Ca urmare, la scalare analizăm:
- indicatori CPU și RAM,
— numărul de cereri din coadă,
- timp de raspuns,
— prognoză bazată pe datele istorice acumulate.

Când scalați un serviciu, este, de asemenea, important să monitorizăm dependențele acestuia, astfel încât să nu scalam primul serviciu din lanț, iar cei pe care îi accesează eșuează sub sarcină. Pentru a stabili o sarcină acceptabilă pentru întregul pool de servicii, analizăm datele istorice ale serviciului dependent „cel mai apropiat” (pe baza unei combinații de indicatori CPU și RAM, cuplate cu metrici specifice aplicației) și le comparăm cu datele istorice a serviciului de inițializare și așa mai departe de-a lungul „lanțului de dependență”, de sus în jos.

serviciu

După ce microserviciul este pus în funcțiune, îi putem atașa declanșatoare.

Iată situațiile tipice în care apar declanșatorii.
— Migrații potențial periculoase detectate.
— Au fost lansate actualizări de securitate.
— Serviciul în sine nu a fost actualizat de mult timp.
— Încărcarea serviciului a scăzut considerabil sau unele dintre valorile produsului se află în afara intervalului normal.
— Serviciul nu mai îndeplinește cerințele noii platforme.

Unii dintre declanșatori sunt responsabili pentru stabilitatea funcționării, unii - în funcție de întreținerea sistemului - de exemplu, un serviciu nu a fost implementat de mult timp și imaginea sa de bază a încetat să treacă controalele de securitate.

Bord

Pe scurt, tabloul de bord este panoul de control al întregului nostru PaaS.

  • Un singur punct de informare despre serviciu, cu date despre acoperirea sa de testare, numărul de imagini ale acestuia, numărul de copii de producție, versiuni etc.
  • Un instrument de filtrare a datelor după servicii și etichete (marcatori de apartenență la unitățile de afaceri, funcționalitatea produsului etc.)
  • Un instrument de integrare cu instrumente de infrastructură pentru urmărire, înregistrare și monitorizare.
  • Un singur punct de documentare de service.
  • Un punct de vedere unic al tuturor evenimentelor din cadrul serviciilor.

Ce știm despre microservicii
Ce știm despre microservicii
Ce știm despre microservicii
Ce știm despre microservicii

În total

Înainte de a introduce PaaS, un nou dezvoltator ar putea petrece câteva săptămâni înțelegând toate instrumentele necesare pentru a lansa un microserviciu în producție: Kubernetes, Helm, funcțiile noastre interne TeamCity, configurarea conexiunilor la baze de date și cache într-o manieră tolerantă la erori etc. durează câteva ore pentru a citi programul de pornire rapidă și pentru a crea serviciul în sine.

Am dat un raport pe acest subiect pentru HighLoad++ 2018, îl puteți urmări video и prezentare.

Bonus track pentru cei care citesc până la capăt

Noi, cei de la Avito, organizăm un training intern de trei zile pentru dezvoltatori din Chris Richardson, un expert în arhitectura de microservicii. Am dori să oferim ocazia de a participa la el unuia dintre cititorii acestei postări. Aici Programul de instruire a fost postat.

Antrenamentul va avea loc în perioada 5-7 august la Moscova. Acestea sunt zile lucrătoare care vor fi ocupate integral. Prânzul și antrenamentul vor avea loc în biroul nostru, iar participantul selectat va plăti el însuși călătoria și cazarea.

Puteți aplica pentru participare în acest formular google. De la tine - răspunsul la întrebarea de ce trebuie să participi la training și informații despre cum să te contactezi. Răspundeți în engleză, deoarece Chris va alege singur participantul care va participa la training.
Vom anunța numele participantului la training într-o actualizare a acestei postări și pe rețelele sociale Avito pentru dezvoltatori (AvitoTech în Фейсбуке, Vkontakte, stare de nervozitate) până la 19 iulie.

Sursa: www.habr.com

Adauga un comentariu