TSM - 450 de miliarde de requesturi HTTP lunar disponibilitate globală 99.99%

Ionuț Lupșan - Software Architect @ Connatix


Călătoria Connatix înspre un nivel de disponibilitate a serviciilor de 99.99% la nivel global este presărată de multe lecții învățate pe parcurs.

Înainte de a explora în detaliu provocările fiecărei etape, este necesară menționarea unor aspecte specifice companiei care au avut o influență majoră asupra deciziilor luate de-a lungul timpului:

Secțiunile următoare vor prezenta evoluția în timp a infrastructurii care suportă deservirea celor 450 de miliarde de HTTP requests la nivel global împreună cu factorii interni/externi care au determinat tranziția către etapa următoare. Fiecare etapă adaugă atât complexitate la nivel de aplicație și de infrastructură, cât și costuri adiționale.

Etapa 1: Kubernetes + AWS

Unul din tiparele frecvente de rulare în AWS Cloud a serviciilor web care au nevoie de un nivel mare de disponibilitate este reprezentat de combinația Kubernetes Deployment/Service + Kubernetes HAProxy Ingress Controller într-un cluster AWS EKS + AWS Elastic Load Balancer.

Elasticitatea instanțelor serviciilor web este asigurată automat de Kubernetes HPA (auto-scalarea pe orizontală a podurilor) în funcție de gradul de utilizare al procesorului și/sau al memoriei.

În momentul de față, numărul total de instanțe variază între 70 și 150, privind ciclurile zilnice ale traficului web. În acest fel este prevenită supraîncărcarea serviciilor.

Pentru a asigura disponibilitatea serviciilor și pe parcursul actualizărilor, o altă funcționalitate din Kubernetes vine în ajutor: Rolling Upgrade (actualizări graduale). Astfel, se adaugă treptat instanțe de aplicație cu noua versiune, iar, pe măsură ce acestea încep să deservească HTTP requests, instanțele vechi sunt retrase.

Acest proces este posibil datorită unor reguli stricte din procesul de dezvoltare care necesită ca oricare două versiuni consecutive (care ar putea rula în paralel la un moment dat) să fie compatibile pentru aplicațiile client care efectuează HTTP requests.

Etapa 2: AWS Multi-AZ (zone multiple de disponibilitate)

Un cluster Kubernetes este asociat cu o regiune geografică în AWS Cloud. Fiecare regiune beneficiază de trei zone de disponibilitate care pot găzdui servere, fiecare dintre ele având propria sursă de energie, propriul sistem de răcire, respectiv propriile legături la nivel de rețea. Ele pot asigura astfel redundanță în caz de avarii localizate.

Pentru a putea beneficia la nivel de aplicație de acest aspect, este necesară configurarea rețelei asociate cu clusterul Kubernetes a mai multor subneturi. Fiecare subnet este asociat cu o zonă de disponibilitate diferită, ceea ce permite ca instanțele EC2 din cluster să fie inițializate în zone diferite, păstrând, în același timp, interconectivitatea.

Efectele secundare se regăsesc, de obicei, și în factura serviciilor Cloud, deoarece transferul de date între zonele de disponibilitate generează costuri adiționale, pe când transferul de date în cadrul unei singure zone este gratuit.

Etapa 3: Multi-Cluster + CDN

Dacă în etapa anterioară am îmbunătățit disponibilitatea serviciilor, în cazul avariilor cauzate de către furnizorul serviciilor de Cloud (AWS), există în continuare scenarii în care erorile umane cu impact catastrofal pot fi cu greu evitate. Atât schimbări la nivel de aplicație, cât și la nivel de infrastructură pot afecta imediat toate instanțele care deservesc HTTP requests, fără a avea soluții de rezervă.

Abordarea noastră pentru diminuarea rapidă și cu complexitate cât mai redusă a acestor riscuri a fost crearea unui cluster Kubernetes adițional care să preia o parte din HTTP requests. Am ales o configurare Active-Active (ambele seturi de instanțe ale serviciilor web gestionează un număr relativ similar de HTTP requests), care aduce următoarele beneficii:

Pe lângă modificările necesare în procesul de dezvoltare și de livrare etapizată a funcționalităților, apare un nou element de complexitate: unde decidem în ce cluster ar trebui să ajungă un HTTP request?

Un răspuns parțial la prima întrebare, "Unde?", este "Nu într-un singur loc", tocmai pentru a evita un potențial punct unic de eșec pentru toate requesturile.

Implementarea finală a constat în a redirecționa primul request HTTP de la începutul fiecărei sesiuni către serviciul de Global Load Balancing. Astfel, decizia de alegere a unui anumit clusters-a luat în peste 200 de locații din întreaga lume, cât mai aproape de utilizator, cu o latență minimă.

Motivul pentru care doar primul request HTTP din fiecare sesiune fost gestionat de către Global Load Balancer ține de costurile suplimentare semnificative care ar fi putut fi generate de luarea acestei decizii pentru fiecare din cele 450 de miliarde de HTTP requests.

Alte aspecte care necesită clarificare sunt raționamentul pentru care nu am optat pentru o configurare de tip Active-Passive sau de ce am ales să distribuim o proporție relativ egală a traficului web între cele două clustere.

Explicația este aceeași pentru ambele: în caz că unul din aceste clustere nu mai funcționează corect, celălalt trebuie să aibă deja suficiente instanțe de aplicație pentru a putea prelua instant restul de trafic redirecționat.

Această redirecționare se efectuează de la nivel de Global Load Balancer, fie automat, în funcție de indicatori de integritate ai serviciilor, fie manual, în cazul perioadelor de mentenanță.

Etapa 4: Multi-Region

Rularea instanțelor de aplicație în mai multe regiuni geografice aduce, la rândul ei, atât avantaje, cât și extra complexitate și costuri.

Printre avantaje regăsim:

În ce privește elementele de complexitate și costuri adiționale, putem enumera:

O alternativă la redirecționarea de HTTP requests prin intermediul Global Load Balancer este folosirea serviciului de DNS, care poate decide în funcție de geolocație și indicatorii de integritate ai serviciilor către cel mai apropiat cluster.

Dezavantajul major al acestei soluții este cache-ul înregistrărilor de tip DNS realizat de diferitele aplicații sau browsere care nu țin cont întotdeauna de durata de viață prestabilită (TTL). Astfel se reduce semnificativ controlul asupra destinației fiecărui HTTP request, uneori fiind necesare minute sau chiar ore pentru redirecționarea întregului trafic web comparativ cu redirecționarea care se produce instantaneu în cazul soluției alese de noi.

Concluzii

În 2023 am reușit să păstrăm disponibilitatea serviciilor Connatix la un nivel peste 99.99%. Soluțiile alese de noi nu reprezintă o rețetă universală, însă, cu siguranță, sunt o poveste de succes care nu se oprește aici. Etapa următoare va explora redundanță la nivel de Global Load Balancer.

Impactul întreruperii temporare a serviciilor este diferit pentru fiecare companie, ceea ce automat duce la o multitudine de decizii și implementări alternative, în funcție de complexitatea adăugată și costurile suplimentare generate.