O abordare non-standard privind microserviciile în AWS

În căutarea celei mai bune soluții

În cadrul unui proiect găzduit pe Amazon Web Services, se dorea trecerea către folosirea de microservicii cu un impact minim pentru întreaga echipă de DevOPS. Cum nu întotdeauna se poate implementa „ca la carte” din diverse constrângeri impuse, totuși soluția oferită trebuie să fie cea mai bună ținând cont de aceste constrângeri, existând astfel și câteva provocări.

Context

Cerințele inițiale venite din partea clientului erau următoarele:

  • Configurarea serviciilor și a infrastructurii să fie identică între diverse medii (Test, Pre-producție, Producție)
  • Doar un singur microserviciu trebuie să fie accesibil din exterior
  • Disponibilitate ridicată (>99.9%) a serviciilor
  • Scalabilitate automată atât pe verticală cât și orizontală
  • Costuri minime pentru infrastructură
  • Timp maxim de implementare: 7 zile
  • Soluția propusă nu trebuie să necesite o perioadă de învățare a unor noi tehnologii din partea echipei de DevOPS
    Pe lângă aceste cerințe inițiale, ne-am adăugat propriile cerințe, crescând astfel provocarea și complexitatea:
  • Vrem să folosim mai multe metrici în vederea scalării aplicației
  • Soluția propusă trebuie să poată evolua într-una mai complexă pe viitor, prin care să putem adăuga ușor alte componente
  • Trebuie ținut cont de faptul că există deja o infrastructură pentru celelalte aplicații, astfel încât noua soluție trebuie să înglobeze aplicațiile existente

Cei de la Amazon au studiat problema microserviciilor și au pus la dispoziție publicului un ghid (http://bit.ly/amznmicros) privind cele mai bune soluții pentru implementarea acestor tipuri de servicii. Astfel, recomandările lor sunt următoarele:

1. Auto Scaling: serviciu al AWS prin care se permite scalarea automată folosind instanțe EC2
2. Elastic Beanstalk: echipa poate încărca în AWS codul dezvoltat, iar acest serviciu are grijă să contruiască infrastructura pe care va rula aplicația
3. ECS (EC2 Container Service): la bază stau containere de tip Docker, iar serviciul ECS se ocupă managementul lor
4. Lambda: rularea aplicației dezvoltate fără a folosi instanțe EC2 (cunoscută sub numele de arhitectură „serverless”)

Problema

Analizând soluțiile descrise în studiul celor de la Amazon, am identificat și problemele care ne împiedică în a aborda oricare din aceste servicii AWS (vezi tabelul)

Soluție și implementare

Întrucât nicio soluție propusă în documentul privind abordarea microserviciilor al Amazon nu se adapta nevoilor noastre, în cele din urmă am ales o combinație a următorelor aplicații și servicii AWS: CloudFormation, Auto Scaling, Lambda, Ansible.

CloudFormation permite crearea automată a infrastructurii în AWS, folosind un document în care sunt descrise toate serviciile AWS sub formă de JSON.

Astfel descriem întreaga infrastructură sub formă de cod și ne asigurăm că indiferent de mediu totul va fi identic. Câteva exemple de șabloane CloudFormation pot fi găsite aici: http://bit.ly/cldformtemplate

În Figura 1 sunt reprezentate toate serviciile AWS folosite pentru a descrie un microserviciu. Așadar, avem descrisă o configurație inițială (LaunchConfiguration) pe baza căreia se vor crea instanțele EC2 pe care va rula microserviciul dezvoltat. În această configurație inițială este specificat tipul de instanță (memorie, CPU, disc), sistemul de operare ce trebuie folosit, iar odată ce acesta a fost instalat, pașii ce trebuie urmăriți pentru ca aplicația să fie instalată. Unii din acești pași folosesc Ansible, permițându-ne astfel o mai bună automatizare a întregului proces.

Figura 1 – Reprezentare grafică a serviciilor AWS din CloudFormation

Figura 2 – Pași de executat după instalarea sistemului de operare

Figura 3 – Comportamentul Auto Scaling în cazul unei actualizări

Pe baza acestui LaunchConfiguration se vor crea mașinile necesare aplicației, dar și partea de scalabilitate prin Auto Scaling, aceste două servicii fiind legate între ele după cum se observă și în Figura 1. Un alt aspect important îl reprezintă comportamentul avut în momentul în care trebuie pusă o nouă versiune fie a aplicației fie a întregii infrastructuri.

Astfel, în cazul unei actualizări vom aștepta până la maximum 17 minute până când noua versiune este instalată, iar această actualizare trebuie făcută astfel încât să se asigure continuitatea serviciului oferit.

Dacă aplicația rulează pe o singură mașină, atunci Auto Scaling va crea una nouă, va urma toți pașii automați de instalare a noii versiuni, se va asigura că noua versiune trece testele, iar la final va inter-schimba vechea mașină cu cea nouă. În cazul in care aplicația rulează deja pe două servere diferite, actualizarea se va efectua pe rând, pe câte un server în parte și nu pe toate deodată, având astfel grijă ca microserviciul să fie mereu funcțional.

Pentru că nu mereu lucrurile merg perfect, este posibil ca prezența unor probleme în aplicație sau a unor factori exteriori să determine oprirea funcționalității aplicației pe unul dintre servere.

Întrucât intervenția umană nu trebuie implicată, prevenirea acestui lucru este realizat prin specificarea faptului că mereu vrem să avem minim o instanță pe care să ruleze aplicația (parametrul „MinSize” din Figura 3).

CloudFormation permite totodată adăugarea sau ștergerea unor componente de infrastructură în mod dinamic, fără a recrea de la zero toate resursele.

Spre exemplu, dacă pe viitor este necesară adăugarea unor alarme noi, în fișierul JSON se vor adăuga aceste alarme, iar CloudFormation le va adăuga doar pe acestea, fără a afecta celelalte resursele existente.

Același comportament este și în cazul ștergerii unor resurse. Astfel infrastructura poate evolua oricând, indiferent de gradul de complexitate.

Până în acest moment am acoperit majoritatea cerințelor inițiale ale clientului, dar și cele ulterioare impuse de echipă, un lucru neabordat fiind scalabilitatea pe baza unor metrici multiple.

În Figura 1 se pot observa câteva metrici atașate serviciilor AWS. Acestea stau la baza deciziei de a scala microserviciul. În acest moment Auto Scaling nu permite combinarea mai multor metrici pentru astfel de decizii, iar o soluție la această problemă este AWS Lambda.

Așa cum menționam anterior, Lambda nu are nevoie de instanțe EC2 pe care să ruleze. Totodată, Lambda rulează doar atunci când este nevoie, ca răspuns la un eveniment primit, serviciul însuși fiind disponibil mereu. Acest aspect este important întrucât nu e necesar să avem cel puțin două servere care vor rula continuu, economisind astfel atât bani cât și efort. Prin intermediul Lambda ne asigurăm că avem o funcție ce se ocupă de scalabilitatea aplicației în funcție de anumiți factori.

Figura 5 – Arhitectura procesului de decizie a scalabilității

Conform arhitecturii din Figura 5, metricile atașate declanșează anumite alarme în cazul încălcării lor. Spre exemplu, dacă timpul de răspuns al microserviciului depășește 0.5 secunde, se va declanșa o alarmă ce are ca destinație un topic de notificare AWS SNS. Funcția Lambda este abonată la notificările primite de către acest topic și va fi declanșată de către orice notificare primită.

Figura 4 – Structura logică de decizie asupra scalabilității aplicației

Odată primită notificarea, aceasta este analizată de către funcție, unde se verifică ce prag a fost depășit (pentru procesor sau pentru timpul de răspuns) și, conform structurii de decizie din Figura 4, va adăuga sau scoate o instanță ce rulează microserviciul dorit. Singura cerință care mai trebuie satisfăcută este cea privind costurile. AWS oferă două modele de preț pentru mașinile EC2: on-demand și spot. Modelul „on-demand” are costuri standard, prețul fiind fix și stabilit de către Amazon conform politicilor sale. Modelul „spot” oferă costuri variabile, de obicei mai mici decât cele standard. Practic prin acest ultim model utilizatorul își stabilește un preț maxim per oră pe care este dispus să îl plătească și participă în mod automat la o licitație împreună cu toți ceilalți clienți ai AWS.

În funcție de resursele AWS disponibile și de prețul pieței, utilizatorul poate obține sau nu o instanță pe care să ruleze aplicațiile. Dezavantajul „spot” îl reprezintă faptul că există posibilitatea ca prețul maxim pe care l-a stabilit utilizatorul să fie prea mic față de piață și atunci AWS nu-i mai alocă instanța cerută. Întrucât e vorba de un mediu dinamic, este destul de dificil de stabilit cea mai bună valoare spre a fi oferită, dar Amazon are totuși un instrument prin care îți poți face o idee despre această valoare – http://bit.ly/bidadvise.

Figura 6 – Instrument de alegere a prețului pentru instanțele Spot

Prin intermediul acestei aplicații se poate selecta regiunea în care să ruleze serverul, numărul de procesoare, memoria, prețul de licitație comparativ cu cel standard, iar ca răspuns se va obține probabilitatea ca prețul ales să fie depășit de către ceilalți participanți. Cu alte cuvinte, gradul de încredere în obținerea instanței dorite pentru prețul selectat.

Practic fiecare microserviciu va rula pe cel puțin două tipuri de instanțe: una cu preț standard on-demand și alta cu preț mai mic, spot. În acest fel, în cazul în care nu vom putea obține o instanță spot din cauza prețului ales, microserviciul va fi în continuare disponibil prin instanța on-demand. Pentru a eficientiza și mai mult costurile, în cadrul scalabilității făcută de către funcția Lambda, vom prefera să adăugam mai multe instanțe cu preț mic în comparație cu cele standard, iar în cazul în care încărcarea pe fiecare microserviciu în parte scade, vom opri mai degrabă instanțele on-demand. Într-un final, arhitectura microserviciilor arată ca în Figura 7.

Figura 7 – Arhitectura finală

Așadar am reușit să acoperim absolut toate cerințele impuse, oferind o soluție dinamică, având costuri minime și o disponibilitate ridicată.

De | 2017-07-03T13:36:38+02:00 3 iulie 2017|Categorii: Numarul 5, Oameni din IT|Taguri: |0 Comentarii

Despre autor:

Lasa un comentariu