Containere, microservicii și rețele de servicii

Pe internet o grămadă articole о plasă de serviciu (plasă de serviciu) și iată încă una. Ura! Dar de ce? Apoi, vreau să-mi exprim părerea că ar fi fost mai bine dacă rețelele de service să apară acum 10 ani, înainte de apariția platformelor de containere precum Docker și Kubernetes. Nu spun că punctul meu de vedere este mai bun sau mai rău decât alții, dar din moment ce ochiurile de serviciu sunt animale destul de complexe, punctele de vedere multiple vor ajuta la înțelegerea lor mai bună.

Voi vorbi despre platforma dotCloud, care a fost construită pe peste o sută de microservicii și a suportat mii de aplicații containerizate. Voi explica provocările cu care ne-am confruntat în dezvoltarea și lansarea acestuia și cum ar putea (sau nu) să ajute rețelele de servicii.

Istoria dotCloud

Am scris despre istoria dotCloud și despre alegerile de arhitectură pentru această platformă, dar nu am vorbit prea mult despre stratul de rețea. Dacă nu vrei să te scufunzi în lectură ultimul articol despre dotCloud, iată esenta pe scurt: este o platformă PaaS-as-a-service care permite clienților să ruleze o gamă largă de aplicații (Java, PHP, Python...), cu suport pentru o gamă largă de date servicii (MongoDB, MySQL, Redis...) și un flux de lucru precum Heroku: Îți încarci codul pe platformă, acesta creează imagini container și le implementează.

Vă voi spune cum a fost direcționat traficul către platforma dotCloud. Nu pentru că a fost deosebit de mișto (deși sistemul a funcționat bine pentru vremea lui!), ci în primul rând pentru că, cu instrumente moderne, un astfel de design poate fi implementat cu ușurință într-un timp scurt de o echipă modestă dacă are nevoie de o modalitate de a direcționa traficul între o grămadă. de microservicii sau o grămadă de aplicații. În acest fel, puteți compara opțiunile: ce se întâmplă dacă dezvoltați totul singur sau utilizați o rețea de servicii existentă. Alegerea standard este să o faci singur sau să o cumperi.

Dirijarea traficului pentru aplicațiile găzduite

Aplicațiile de pe dotCloud pot expune punctele finale HTTP și TCP.

Puncte finale HTTP adăugat dinamic la configurația clusterului de echilibrare a încărcăturii Hipache. Acest lucru este similar cu ceea ce fac resursele astăzi Pătrundere în Kubernetes și un echilibrator de încărcare ca Traefik.

Clienții se conectează la punctele terminale HTTP prin domeniile adecvate, cu condiția ca numele domeniului să trimită către echilibratori de încărcare dotCloud. Nimic special.

Puncte finale TCP asociat cu un număr de port, care este apoi transmis tuturor containerelor din acel stivă prin variabilele de mediu.

Clienții se pot conecta la punctele finale TCP folosind numele de gazdă adecvat (ceva precum gateway-X.dotcloud.com) și numărul de port.

Acest nume de gazdă se rezolvă în clusterul de server „nats” (nu are legătură cu NATS), care va direcționa conexiunile TCP de intrare către containerul corect (sau, în cazul serviciilor echilibrate, către containerele corecte).

Dacă sunteți familiarizat cu Kubernetes, acest lucru vă va aminti probabil de Servicii Portul nodului.

Nu existau servicii echivalente pe platforma dotCloud ClusterIP: Pentru simplitate, serviciile au fost accesate în același mod atât din interiorul, cât și din exteriorul platformei.

Totul a fost organizat destul de simplu: implementările inițiale ale rețelelor de rutare HTTP și TCP au fost probabil doar câteva sute de linii Python fiecare. Algoritmi simpli (aș spune naivi) care s-au perfecționat pe măsură ce platforma a crescut și au apărut cerințe suplimentare.

Nu a fost necesară refactorizarea extensivă a codului existent. În special, Aplicații cu 12 factori poate folosi direct adresa obţinută prin variabilele de mediu.

Cum este aceasta diferită de o plasă de servicii modernă?

Limitat vizibilitate. Nu aveam deloc valori pentru rețeaua de rutare TCP. Când vine vorba de rutarea HTTP, versiunile ulterioare au introdus metrici HTTP detaliate cu coduri de eroare și timpi de răspuns, dar rețelele de servicii moderne merg chiar mai departe, oferind integrare cu sisteme de colectare de valori precum Prometheus, de exemplu.

Vizibilitatea este importantă nu numai dintr-o perspectivă operațională (pentru a ajuta la depanarea problemelor), ci și la lansarea de noi funcții. Este vorba despre siguranța desfășurare albastru-verde и desfăşurare canar.

Eficiență de rutare este de asemenea limitat. În rețeaua de rutare dotCloud, tot traficul trebuia să treacă printr-un cluster de noduri de rutare dedicate. Acest lucru a însemnat potențial depășirea mai multor granițe AZ (zonă de disponibilitate) și creșterea semnificativă a latenței. Îmi amintesc de depanarea codului care făcea peste o sută de interogări SQL pe pagină și deschidea o nouă conexiune la serverul SQL pentru fiecare interogare. Când rulează local, pagina se încarcă instantaneu, dar în dotCloud durează câteva secunde pentru a se încărca, deoarece fiecare conexiune TCP (și interogarea SQL ulterioară) durează zeci de milisecunde. În acest caz particular, conexiunile persistente au rezolvat problema.

Rețelele de serviciu moderne sunt mai bune pentru a face față unor astfel de probleme. În primul rând, ei verifică dacă conexiunile sunt direcționate în sursă. Fluxul logic este același: клиент → меш → сервис, dar acum rețeaua funcționează local și nu pe noduri la distanță, deci conexiunea клиент → меш este locală și foarte rapidă (microsecunde în loc de milisecunde).

Rețelele de servicii moderne implementează, de asemenea, algoritmi mai inteligenți de echilibrare a sarcinii. Prin monitorizarea stării de sănătate a backend-urilor, acestea pot trimite mai mult trafic către backend-uri mai rapide, rezultând o performanță generală îmbunătățită.

Безопасность mai bine. Mesh-ul de rutare dotCloud a rulat în întregime pe EC2 Classic și nu a criptat traficul (bazat pe presupunerea că, dacă cineva a reușit să pună un sniffer pe traficul de rețea EC2, ai avut deja mari probleme). Rețelele de servicii moderne ne protejează în mod transparent tot traficul, de exemplu, cu autentificare TLS reciprocă și criptare ulterioară.

Dirijarea traficului pentru serviciile platformei

Bine, am discutat despre traficul dintre aplicații, dar cum rămâne cu platforma dotCloud în sine?

Platforma în sine a constat din aproximativ o sută de microservicii responsabile de diverse funcții. Unii au acceptat solicitări de la alții, iar unii au fost lucrători de fond care se conectau la alte servicii, dar nu acceptau conexiuni ei înșiși. În orice caz, fiecare serviciu trebuie să cunoască punctele finale ale adreselor la care trebuie să se conecteze.

Multe servicii de nivel înalt pot utiliza rețeaua de rutare descrisă mai sus. De fapt, multe dintre cele peste o sută de microservicii ale dotCloud au fost implementate ca aplicații obișnuite pe platforma dotCloud în sine. Dar un număr mic de servicii de nivel scăzut (în special cele care implementează această rețea de rutare) aveau nevoie de ceva mai simplu, cu mai puține dependențe (din moment ce nu puteau depinde de ei înșiși pentru a funcționa - problema bătrânului pui și ouă).

Aceste servicii de nivel scăzut, critice pentru misiune au fost implementate prin rularea containerelor direct pe câteva noduri cheie. În acest caz, nu au fost utilizate serviciile standard ale platformei: linker, scheduler și runner. Dacă doriți să comparați cu platformele moderne de containere, este ca și cum ați rula un avion de control cu docker run direct pe noduri, în loc să delegeți sarcina lui Kubernetes. Este destul de asemănător ca concept module statice (pod-uri), pe care îl folosește kubeadm sau bootkube la pornirea unui cluster autonom.

Aceste servicii au fost expuse într-o manieră simplă și brută: un fișier YAML a enumerat numele și adresele lor; și fiecare client a trebuit să ia o copie a acestui fișier YAML pentru implementare.

Pe de o parte, este extrem de fiabil deoarece nu necesită suportul unui magazin extern de chei/valoare, cum ar fi Zookeeper (rețineți, etcd sau Consul nu existau la acel moment). Pe de altă parte, a îngreunat mutarea serviciilor. De fiecare dată când se face o mutare, toți clienții primeau un fișier YAML actualizat (și eventual reporneau). Nu foarte confortabil!

Ulterior, am început să implementăm o nouă schemă, în care fiecare client se conectează la un server proxy local. În loc de o adresă și un port, trebuie doar să cunoască numărul portului serviciului și să se conecteze prin intermediul localhost. Proxy-ul local se ocupă de această conexiune și o trimite către serverul actual. Acum, când mutați backend-ul pe o altă mașină sau scalați, în loc să actualizați toți clienții, trebuie doar să actualizați toate aceste proxy-uri locale; iar o repornire nu mai este necesară.

(S-a planificat, de asemenea, să se încapsuleze traficul în conexiunile TLS și să se pună un alt server proxy pe partea de recepție, precum și să se verifice certificatele TLS fără participarea serviciului de primire, care este configurat să accepte conexiuni numai pe localhost. Mai multe despre asta mai târziu).

Acesta este foarte asemănător cu SmartStack de la Airbnb, dar diferența semnificativă este că SmartStack este implementat și implementat în producție, în timp ce sistemul de rutare intern al dotCloud a fost abandonat când dotCloud a devenit Docker.

Personal, consider că SmartStack este unul dintre predecesorii sistemelor precum Istio, Linkerd și Consul Connect, deoarece toate urmează același model:

  • Rulați un proxy pe fiecare nod.
  • Clienții se conectează la proxy.
  • Planul de control actualizează configurația proxy atunci când backend-urile se modifică.
  • ... Profit!

Implementarea modernă a unei rețele de servicii

Dacă ar fi nevoie să implementăm o grilă similară astăzi, am putea folosi principii similare. De exemplu, configurați o zonă DNS internă prin maparea numelor de servicii cu adresele din spațiu 127.0.0.0/8. Apoi rulați HAProxy pe fiecare nod din cluster, acceptând conexiuni la fiecare adresă de serviciu (în acea subrețea 127.0.0.0/8) și redirecționarea/echilibrarea sarcinii către backend-urile corespunzătoare. Configurația HAProxy poate fi controlată confd, permițându-vă să stocați informațiile backend în etcd sau Consul și să împingeți automat configurația actualizată la HAProxy atunci când este necesar.

Cam așa funcționează Istio! Dar cu unele diferențe:

  • Utilizări Envoy Proxy în loc de HAProxy.
  • Stochează configurația backend prin API-ul Kubernetes în loc de etcd sau Consul.
  • Serviciile sunt alocate adrese pe subrețeaua internă (adrese Kubernetes ClusterIP) în loc de 127.0.0.0/8.
  • Are o componentă suplimentară (Citadel) pentru a adăuga autentificare TLS reciprocă între client și servere.
  • Acceptă funcții noi, cum ar fi întreruperea circuitului, urmărirea distribuită, implementarea Canary etc.

Să aruncăm o privire rapidă la unele dintre diferențe.

Envoy Proxy

Envoy Proxy a fost scris de Lyft [concurentul Uber pe piața taxiurilor - aprox. BANDĂ]. Este similar în multe privințe cu alți proxy (de exemplu, HAProxy, Nginx, Traefik...), dar Lyft le-a scris pe ale lor pentru că aveau nevoie de caracteristici care le lipseau altor proxy și părea mai inteligent să creeze unul nou decât să îl extindă pe cel existent.

Envoy poate fi folosit singur. Dacă am un anumit serviciu care trebuie să se conecteze la alte servicii, îl pot configura să se conecteze la Envoy și apoi să configurez și să reconfigurez dinamic Envoy cu locația altor servicii, obținând în același timp o mulțime de funcționalități suplimentare excelente, cum ar fi vizibilitatea. În loc de o bibliotecă client personalizată sau de a injecta urme de apel în cod, trimitem trafic către Envoy, iar acesta colectează valori pentru noi.

Dar Envoy este, de asemenea, capabil să lucreze ca planul de date (planul de date) pentru rețeaua de serviciu. Aceasta înseamnă că Envoy este acum configurat pentru această rețea de servicii planul de control (planul de control).

Planul de control

Pentru planul de control, Istio se bazează pe API-ul Kubernetes. Acest lucru nu este foarte diferit de utilizarea confd, care se bazează pe etcd sau Consul pentru a vizualiza setul de chei din depozitul de date. Istio utilizează API-ul Kubernetes pentru a vedea un set de resurse Kubernetes.

Între asta și atunci: Am găsit personal acest lucru util Descrierea API-ului Kubernetescare scrie:

Serverul API Kubernetes este un „server prost” care oferă stocare, versiune, validare, actualizare și semantică pentru resursele API.

Istio este proiectat să funcționeze cu Kubernetes; iar dacă doriți să îl utilizați în afara Kubernetes, atunci trebuie să rulați o instanță a serverului API Kubernetes (și serviciul de ajutor etcd).

Adrese de service

Istio se bazează pe adresele ClusterIP pe care Kubernetes le alocă, astfel încât serviciile Istio primesc o adresă internă (nu în intervalul 127.0.0.0/8).

Traficul către adresa ClusterIP pentru un anumit serviciu dintr-un cluster Kubernetes fără Istio este interceptat de kube-proxy și trimis către backend-ul respectivului proxy. Dacă sunteți interesat de detaliile tehnice, kube-proxy setează reguli iptables (sau echilibrare de încărcare IPVS, în funcție de modul în care este configurat) pentru a rescrie adresele IP de destinație ale conexiunilor care merg la adresa ClusterIP.

Odată ce Istio este instalat pe un cluster Kubernetes, nimic nu se schimbă până când este activat în mod explicit pentru un anumit consumator, sau chiar întregul spațiu de nume, prin introducerea unui container sidecar în poduri personalizate. Acest container va crea o instanță a lui Envoy și va configura un set de reguli iptables pentru a intercepta traficul care merge către alte servicii și a redirecționa acel trafic către Envoy.

Atunci când este integrat cu Kubernetes DNS, aceasta înseamnă că codul nostru se poate conecta după numele serviciului și totul „funcționează”. Cu alte cuvinte, codul nostru emite interogări precum http://api/v1/users/4242atunci api rezolva cererea pentru 10.97.105.48, regulile iptables vor intercepta conexiunile de la 10.97.105.48 și le vor redirecționa către proxy-ul Envoy local, iar acel proxy local va transmite cererea către API-ul backend real. Pf!

Volante suplimentare

Istio oferă, de asemenea, criptare și autentificare end-to-end prin mTLS (TLS mutual). O componentă numită Cetate.

Există și o componentă mixer, pe care Envoyul îl poate solicita fiecare solicitarea de a lua o decizie specială cu privire la acea solicitare, în funcție de diverși factori, cum ar fi antete, încărcare backend etc... (nu vă faceți griji: există multe modalități de a menține Mixer să funcționeze și, chiar dacă se blochează, Envoy va continua să funcționeze bine ca proxy).

Și, bineînțeles, am menționat vizibilitatea: Envoy colectează o cantitate imensă de valori în timp ce oferă urmărire distribuită. Într-o arhitectură de microservicii, dacă o singură solicitare API trebuie să treacă prin microservicii A, B, C și D, atunci la conectare, urmărirea distribuită va adăuga un identificator unic la cerere și va stoca acest identificator prin subcereri la toate aceste microservicii, permițând toate apelurile aferente să fie captate, întârzieri etc.

Dezvoltați sau cumpărați

Istio are reputația de a fi complex. În schimb, construirea rețelei de rutare pe care am descris-o la începutul acestei postări este relativ simplă folosind instrumentele existente. Deci, are sens să vă creați propria rețea de servicii?

Dacă avem nevoi modeste (nu avem nevoie de vizibilitate, un întrerupător de circuit și alte subtilități), atunci se gândesc să ne dezvoltăm propriul instrument. Dar dacă folosim Kubernetes, s-ar putea să nu fie necesar, deoarece Kubernetes oferă deja instrumente de bază pentru descoperirea serviciilor și echilibrarea sarcinii.

Dar dacă avem cerințe avansate, atunci „achiziționarea” unei rețele de servicii pare a fi o opțiune mult mai bună. (Acesta nu este întotdeauna o „cumpărare”, deoarece Istio este open source, dar trebuie totuși să investim timp de inginerie pentru a-l înțelege, implementa și gestiona.)

Ar trebui să aleg Istio, Linkerd sau Consul Connect?

Până acum am vorbit doar despre Istio, dar aceasta nu este singura rețea de service. Alternativa populara - Linkerd, și mai sunt Consul Connect.

Ce să aleg?

Sincer, nu știu. Momentan nu mă consider suficient de competent pentru a răspunde la această întrebare. Sunt cateva interesant articole cu o comparaţie a acestor instrumente şi chiar repere.

O abordare promițătoare este utilizarea unui instrument precum SuperGloo. Implementează un strat de abstractizare pentru a simplifica și unifica API-urile expuse de rețelele de servicii. În loc să învățăm API-urile specifice (și, după părerea mea, relativ complexe) ale diferitelor rețele de servicii, putem folosi constructele mai simple ale SuperGloo - și putem trece cu ușurință de la una la alta, ca și cum am avea un format de configurare intermediar care descrie interfețele HTTP și backend-uri capabile de generare a configurației actuale pentru Nginx, HAProxy, Traefik, Apache...

M-am ocupat puțin cu Istio și SuperGloo, iar în următorul articol vreau să arăt cum să adăugați Istio sau Linkerd la un cluster existent folosind SuperGloo și cum acesta din urmă își face treaba, adică vă permite să treceți de la o plasă de servicii la alta fără a suprascrie configurațiile.

Sursa: www.habr.com

Adauga un comentariu