Aplicațiile „cloud-native” sau pur și simplu „în cloud” sunt create special pentru a funcționa în infrastructuri cloud. De obicei, acestea sunt construite ca un set de microservicii slabe legate, ambalate în containere, care sunt gestionate de o platformă cloud. Aceste aplicații sunt pregătite în mod implicit pentru a face față defecțiunilor, ceea ce înseamnă că funcționează în mod fiabil și se scalează chiar și în cazul unor defecțiuni grave la nivelul infrastructurii. Pe de altă parte, există un set de restricții (contracte) pe care platforma cloud le impune aplicațiilor în containere pentru a le putea gestiona automat.

Fiind conștienți de necesitatea și importanța migrării către aplicațiile cloud, multe organizații încă nu știu cu ce să înceapă. În acest articol, vom explora o serie de principii, respectarea cărora în dezvoltarea aplicațiilor în containere va permite valorificarea potențialului platformelor cloud și asigurarea unei funcționări fiabile și a scalabilității aplicațiilor chiar și în fața unor defecțiuni grave în infrastructura IT. Scopul final al acestor principii este să învățăm cum să creăm aplicații care pot fi gestionate automat de platformele cloud, cum ar fi Kubernetes.
Principiile de proiectare a software-ului
În lumea programării, prin principii se înțeleg reguli destul de generale care trebuie respectate în dezvoltarea software-ului. Acestea pot fi aplicate în lucrul cu orice limbaj de programare. Fiecare principiu are scopuri proprii, iar instrumentul prin care se ating acestea sunt de obicei șabloanele și practicile. Există, de asemenea, o serie de principii fundamentale pentru crearea unui software de calitate, din care derivă toate celelalte. Iată câteva exemple de principii fundamentale:
- (Keep it simple, stupid) – a nu complica;
- (Don’t repeat yourself) – a nu te repeta;
- (You aren’t gonna need it) – a nu crea ceea ce nu este necesar imediat;
- (Separation of concerns) – a separa responsabilitățile.
După cum se poate observa, aceste principii nu impun reguli specifice, ci fac parte din așa-numitele considerații de bun simț bazate pe experiență practică, pe care le împărtășesc mulți dezvoltatori și la care aceștia se referă frecvent.
În plus, există – un set de primele cinci principii de programare și proiectare orientată pe obiect, formulate de Robert Martin. SOLID include principii complementare, generalizate și deschise interpretărilor, care – aplicate în ansamblu – ajută la crearea unor sisteme software de calitate superioară și la menținerea acestora pe termen lung.
Principiile SOLID se referă la domeniul OOP și sunt formulate în termeni precum clase, interfețe și moștenire. În mod similar, pentru aplicațiile de cloud, se pot formula principii de dezvoltare, cu elementul de bază fiind containerul, nu clasa. Urmând aceste principii, se pot crea aplicații bazate pe containere care răspund mai bine nevoilor și obiectivelor platformelor de cloud, precum Kubernetes.
Containere orientate pe cloud: abordarea Red Hat
Astăzi, pentru majoritatea aplicațiilor se poate realiza relativ ușor împachetarea în containere. Însă, pentru ca aplicațiile să fie automate și orchestrate eficient într-o platformă de cloud precum Kubernetes, este necesară o muncă suplimentară.
Fundamentul ideilor prezentate mai jos este metodologia și numeroase alte lucrări care acoperă diferite aspecte ale creării aplicațiilor web, de la gestionarea codului sursă la modele de scalare. Principiile descrise se referă exclusiv la dezvoltarea aplicațiilor bazate pe containere, construite pe baza microserviciilor și destinate platformelor de cloud, cum ar fi Kubernetes. Elementul de bază în analizele noastre este imaginea containerului, iar prin mediu țintă de execuție a containerelor înțelegem platforma de orchestrare a containerelor. Scopul principiilor propuse este de a crea containere pentru care majoritatea platformelor de orchestrare pot automatiza sarcini de programare (scheduling – alegerea gazdei pentru lansarea instanței containerului), scalare și monitorizare. Principiile sunt prezentate în ordine aleatorie.
Principiul unei singure sarcini (Single Concern Principle, SCP)
Acest principiu este în mare măsură similar cu principiul unei singure responsabilități (Single Responsibility Principle, ), care face parte din setul SOLID și stipulează că fiecare obiect ar trebui să aibă o singură responsabilitate, iar această responsabilitate ar trebui să fie complet încapsulată în clasă. Esența SRP este că fiecare responsabilitate reprezintă un motiv de schimbare, iar clasa ar trebui să aibă un singur motiv pentru a se schimba.
În SCP, în loc de cuvântul „responsabilitate” (responsibility), folosim cuvântul „îndatorire” (concern), pentru a indica un nivel mai înalt de abstractizare și o utilizare mai largă a containerului, comparativ cu clasa OOP. Și dacă scopul SRP este de a avea doar un singur motiv pentru schimbare, SCP are ca obiectiv creșterea capacității de reutilizare și înlocuire a containerelor. Urmând SRP și creând un container care rezolvă o singură îndatorie și face acest lucru într-un mod funcțional complet, îți îmbunătățești șansele de reutilizare a acestui container în diferite contexte de aplicație.
Principiul SCP stipulează că fiecare container ar trebui să rezolve o singură îndatorie și să o facă bine. În plus, SCP în lumea containerelor este mai ușor de realizat decât SRP în lumea OOP, deoarece containerele în general execută un singur proces, iar cea mai mare parte a timpului acest proces rezolvă o singură îndatorie.
Dacă un microserviciu containerizat trebuie să rezolve simultan mai multe îndatorii, acesta poate fi împărțit în containere cu o singură îndatorie și reunite în cadrul aceluiași pod (unitate de desfășurare a platformei containerizate) prin intermediul șabloanelor sidecar și init-container. În plus, SCP facilitează înlocuirea unui container vechi (de exemplu, un server web sau un broker de mesaje) cu unul nou, care rezolvă aceeași îndatorie, dar are funcționalitate extinsă sau se scalează mai bine.

Principiul utilității monitorizării (High Observability Principle, HOP)
Atunci când se utilizează containere ca o modalitate unificată de ambalare și desfășurare a aplicațiilor, aplicațiile în sine sunt considerate ca un „black box”. Totuși, dacă acestea sunt containere cloud, ele trebuie să ofere mediu de execuție API-uri speciale pentru a controla integritatea containerelor și, dacă este necesar, să ia măsuri corespunzătoare. Fără aceste API-uri, nu va fi posibilă unificarea automatizării actualizării containerelor și gestionarea ciclului lor de viață, ceea ce, la rândul său, va afecta reziliența și ușurința de utilizare a sistemului software.
În practică, aplicația de tip container trebuie, cel puțin, să aibă un API pentru diferite tipuri de teste de sănătate: teste de activitate (liveness) și teste de pregătire (readiness). Dacă aplicația pretinde mai mult, atunci trebuie să ofere și alte mijloace de control al stării. De exemplu, înregistrarea evenimentelor importante prin STDERR și STDOUT pentru agregarea jurnalelor folosind Fluentd, Logstash și alte instrumente similare. De asemenea, integrarea cu biblioteci de trasare și colectare de metrici, precum OpenTracing, Prometheus etc.
În general, aplicația poate fi în continuare considerată un „black box”, dar trebuie echipată cu toate API-urile necesare platformei pentru a-i monitoriza și gestiona cel mai bine funcționarea.
Principiul conformității cu ciclul de viață (Life-cycle Conformance Principle, LCP)
LCP este o antiteză a HOP. Dacă HOP afirmă că un container trebuie să ofere platformei API-uri pentru citire, LCP impune aplicației capacitatea de a percepe informații de la platformă. În plus, containerul trebuie nu doar să primească evenimente, ci și să reacționeze la ele. De aici provine și denumirea principiului, care poate fi considerat o cerință de a oferi platformei API-uri pentru scriere.

Platformele au diferite tipuri de evenimente care ajută la gestionarea ciclului de viață al containerului. Însă decizia despre care dintre acestea trebuie percepute și cum să reacționeze trebuie să revină aplicației în sine.
Este clar că unele evenimente sunt mai importante decât altele. De exemplu, dacă o aplicație gestionează prost închiderea bruscă, aceasta trebuie să accepte mesajele signal: terminate (SIGTERM) și să inițieze cât mai repede procedura de închidere pentru a se asigura că finalizează înainte de a primi signal: kill (SIGKILL), care urmează după SIGTERM.
În plus, pentru ciclul de viață al aplicației, evenimentele precum PostStart și PreStop pot fi importante. De exemplu, după lansare, aplicația poate necesita un timp de „încălzire” înainte de a putea răspunde la cereri. Sau aplicația ar trebui să elibereze resurse într-un mod specific la închiderea sa.
Principiul imutabilității imaginii containerului (Image Immutability Principle, IIP)
Se consideră în mod obișnuit că aplicațiile containerizate ar trebui să rămână neschimbate după construirea lor, chiar și atunci când sunt lansate în medii diferite. Din aceasta derivă necesitatea de a externaliza stocarea datelor în timpul execuției (cu alte cuvinte, de a utiliza mijloace externe pentru aceasta) și de a se baza pe configurații externe, adaptate la mediul specific de execuție, în loc să se modifice sau să se creeze containere unice pentru fiecare mediu. După orice modificare a aplicației, imaginea containerului trebuie reconstruită și desfășurată în toate mediile utilizate. Apropo, un principiu similar se folosește în managementul sistemelor IT, cunoscut sub numele de principiul imutabilității serverelor și infrastructurii.
Scopul IIP este de a preveni crearea unor imagini containerizate separate pentru diferite medii de execuție și de a utiliza aceeași imagine în toate medii, împreună cu configurația corespunzătoare pentru fiecare mediu. Respectarea acestui principiu permite implementarea unor practici importante pentru automatizarea sistemelor cloud, cum ar fi roll-back-ul (întoarcerea) și roll-forward-ul (avansarea) actualizărilor aplicației.

Principiul dispoziției proceselor (Process Disposability Principle, PDP)
Una dintre cele mai importante caracteristici ale unui container este efemeritatea sa: un exemplu de container poate fi creat cu ușurință și distrus cu ușurință, astfel că poate fi înlocuit cu un alt exemplu oricând. Motivele pentru o astfel de înlocuire pot fi multiple: eșecul unui test de funcționalitate, scalarea aplicației, migrarea pe un alt host, epuizarea resurselor platformei sau alte situații.
Prin urmare, aplicațiile containerizate trebuie să își păstreze starea cu ajutorul unor resurse externe sau să folosească scheme distribuite interne cu redundanță. De asemenea, aplicația trebuie să se pornească rapid și să se încheie rapid, fiind pregătită pentru un posibil eșec fatal neașteptat al hardware-ului.
Una dintre practicile care ajută la implementarea acestui principiu este crearea containerelor de dimensiuni mici. Mediile cloud pot alege automat gazda pentru a rula instanța unui container, astfel că cu cât containerul este mai mic, cu atât se va lansa mai repede – se va copia pur și simplu mai rapid pe gazda țintă prin rețea.
Principiul autosuficienței (Self-containment Principle, S-CP)
Conform acestui principiu, în etapa de construire, containerul include toate componentele necesare. Containerul trebuie să fie construit având în vedere că în sistem există doar un nucleu Linux curat, astfel încât toate bibliotecile suplimentare necesare trebuie să fie incluse în containerul însuși. De asemenea, trebuie să fie incluse și asemenea lucruri precum mediul de execuție pentru limbajul de programare corespunzător, platforma aplicației (dacă este nevoie) și alte dependențe care vor fi necesare în timpul funcționării aplicației containerizate.

Excepțiile se fac doar pentru configurațiile care variază de la un mediu la altul și care trebuie furnizate în timpul execuției, de exemplu, prin Kubernetes ConfigMap.
Aplicația poate include mai multe componente containerizate, cum ar fi un container separat de SGBD în cadrul unei aplicații web containerizate. Conform principiului S-CP, aceste containere nu trebuie să fie combinate într-unul singur, ci trebuie să fie astfel concepute încât containerul SGBD să conțină tot ce este necesar pentru funcționarea bazei de date, iar containerul aplicației web să conțină tot ce este necesar pentru funcționarea aplicației web, inclusiv serverul web. Ca rezultat, în timpul execuției, containerul aplicației web va depinde de containerul SGBD și va apela la acesta după necesitate.
Principiul restricționării în timpul execuției (Runtime Confinement Principle, RCP)
Principiul S-CP definește modul în care ar trebui să fie construit un container și ce ar trebui să conțină fișierul binar al imaginii. Însă, containerul nu este doar o „cutie neagră” ale cărei singură caracteristică este dimensiunea fișierului. În timpul execuției, containerul obține și alte dimensiuni: volumul de memorie utilizată, timpul procesorului și alte resurse sistemice.

Aici intervine principiul RCP, conform căruia containerul trebuie să decapaciteze cerințele sale de resurse sistemice și să le transmită platformei. Având profiluri de resurse pentru fiecare container (câtă resursă de CPU, memorie, rețea și sistem de stocare are nevoie), platforma poate efectua optimizarea transportului și a scalării automate, gestionând capacitățile IT și menținând nivelurile SLA pentru containere.
În plus față de satisfacerea cerințelor de resurse ale containerului, aplicația este de asemenea important să nu depășească limitele stabilite de ea însăși. Altfel, în cazul unei penurii de resurse, platforma va avea mai multe șanse să includă aplicația pe lista celor care trebuie întrerupte sau migrate.
Când vorbim despre orientarea spre cloud, avem în vedere în primul rând modul de lucru.
Mai sus, am formulat o serie de principii generale care stabilesc fundamentul metodologic pentru construirea unor aplicații containerizate de calitate pentru medii cloud.
Trebuie menționat că, pe lângă aceste principii generale, veți avea nevoie de metode și tehnici avansate suplimentare pentru lucrul cu containere. De asemenea, avem câteva recomandări scurte care sunt mai specifice și ar trebui aplicate (sau nu) în funcție de situație:
- Încercați să reduceți dimensiunea imaginilor: eliminați fișierele temporare și nu instalați pachete inutile – cu cât dimensiunea containerului este mai mică, cu atât se compilează și se copiază mai repede pe gazda țintă prin rețea.
- Focalizați-vă pe User-ID-uri arbitrare: nu utilizați comanda sudo sau user-id-uri speciale pentru a rula containerele dvs.
- Etichetați porturile importante: numerele de porturi pot fi specificate și în timpul execuției, dar este mai bine să le indicați folosind comanda EXPOSE – altor persoane și programe le va fi mai ușor să utilizeze imaginile dvs.
- Păstrați datele persistente pe volume: datele care trebuie să rămână după distrugerea containerului ar trebui să fie scrise pe volume.
- Specificați metadatele imaginii: etichetele, notele și anotațiile facilitează utilizarea imaginilor - alți dezvoltatori vă vor fi recunoscători.
- Sincronizați gazda și imaginile: pentru unele aplicații de container, este necesară sincronizarea containerului cu gazda pe anumite atribute, cum ar fi timpul sau identificatorul mașinii.
- În concluzie, vă împărtășim șabloane și cele mai bune practici care vor ajuta la implementarea mai eficientă a principiilor menționate anterior:
11 iunie la 11.00
Ce veți învăța:
- Red Hat Enterprise Linux CoreOS Imutabil
- Rețea de servicii OpenShift
- Cadru Operator
- Cadru Knative
Sursa: habr.com
