ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 59
Abonament PDF

Microservicii Cloud bazate pe containere

Stefan Blickensdörfer
Technical director @ Smartsoft/3SS



Zsolt Janosi
Software architect @ Smartsoft/3SS



Botond Palfi
Senior software engineer @ Smartsoft/3SS



PROGRAMARE


În acest articol, vom descrie un sistem backend modern de microservicii, instalate în containere Docker în cadrul unei infrastructuri Cloud. Articolul va prezenta o viziune de ansamblu asupra diferitelor componente și tehnologii utilizate. Articolul nu recapitulează și nu face un rezumat al documentaţiei pe această temă, ci oferă o viziune generală care aduce argumente pro și contra la abordările și strategiile de succes pentru 3SS. Vom explica și vom detalia aceste structuri pe baza experienţei de dezvoltare a propriilor produse pentru a putea oferi exemple reale.

Când se începe construirea arhitecturii pentru infrastructura backend și cea de sistem a produsului, am ţinut cont de următoarele aspecte legate de clienţii noștri posibili:

Am decis să ne depărtăm de arhitectura backend monolitică, dorind o soluţie care ne permite scalarea și care nu ne restricţionează la nivel de alegeri tehnologice. De asemenea, am dorit să ne asigurăm că putem menţine, actualiza și dezvolta servicii individuale în mod independent, reușind în același timp să limităm impactul potenţial asupra erorilor în timpul actualizărilor de producţie.

Deși acum toate serviciile sunt scrise în node.js, acest lucru nu presupune că nu putem folosi python sau alte limbaje mai potrivite pe viitor.

Componentele

După cum sugerează titlul, infrastructura pe care o descriem are următoarele componente:

O perspectivă simplificată ar fi:

Vom oferi detalii suplimentare în continuare.

Ce sunt microserviciile?

Nu vom oferi detalii exhaustive pe aceasta temă, deoarece aceasta este extrem de mare. Arhitectura bazată pe microservicii s-a dezvoltat în ultimii ani odată cu trecerea la aplicațiile web și la serviciile terțe. Principiul major din spatele acesteia se poate exprima astfel: "Fă un singur lucru și fă-l bine" în loc de a încerca să faci totul. Acest lucru se aplică foarte bine dezvoltării API care este o parte crucială a aplicațiilor web.

Din perspectiva programării, unul din marile beneficii ale arhitecturii bazate pe microservicii este că permite celor mai mici echipe posibile (sau programatorilor individuali) să le dețină, să le dezvolte și să le întrețină. Când se livrează noi builduri, părțile incrementale și domeniul de aplicabilitate sunt suficient de mici pentru a permite revizii la timp și evaluări care necesită efort minim, permițând roll-outuri cu risc minim.

Microserviciile sunt caracterizate de obicei ca fiind:

În dezvoltarea produselor noastre, serviciile backend comunică intens și funcționează ca mediatori între alte servicii web și backend în timp ce păstrează doar o mică capacitate a funcționalității lor inerente. Acest fapt face ca arhitectura bazată pe microservicii să fie extrem de potrivită pentru a obține performanță înaltă.

Ce sunt containerele?

O imagine container este un pachet executabil care conține tot ce este necesar pentru a rula un sistem de operare și niște aplicații. Containerele izolează aplicațiile și sistemele de operare în medii diferite, ceea ce permite să nu existe conflicte între versiuni de pachete și dependențe ale aceleiași gazde. Containerele sunt portabile și pot fi executate pe orice gazdă care rulează engine-ul respectiv.

Architectura containerelor

Infrastructura mașinilor virtuale

Cum funcţionează Docker?

Una dintre cele mai dese prejudecăți despre Docker este că acesta este un sistem complet virtualizat precum virtualbox sau vagrant. De fapt, Docker (și alte soluții de tip container) folosesc API la nivel de sistem de operare pentru a partaja și consuma resursele cu sistemul gazdă fără a replica o mașină în totalitate.

O infrastructură bazată pe Docker are patru componente:

Docker însuși folosește o arhitectură client-server. Sistemul gazdă care ar trebui să ruleze containerele trebuie să aibă docker-server/daemon (Docker-Engine).

De ce ar trebui să optăm pentru această abordare?

Utilizarea containerelor Docker în defavoarea altor soluții (e.g. mașini virtuale, gazde virtuale etc.) aduce o serie de avantaje:

  1. Folosind containere, toate mediile de la dezvoltare la producție sunt identice pentru aplicație. Una din marile probleme ce există în dezvoltarea aplicațiilor backend este că, în mod normal, mediile nu sunt identice când se instalează builduri și apar probleme de dependențe ("Oh, aceasta este versiunea 10.2.1.2 - la staging am folosit 10.2.1.1 și a funcționat"). Mediile de dezvoltare/staging/producție nu sunt identice, sunt configurate diferit, iar un test QA pe oricare din aceste medii nu garantează că nu vor fi probleme după instalare. În trecut, replicarea unei instalări complete era o provocare care necesita eforturi mari. Folosind containere, putem rula același mediu peste tot.

  2. Comparate cu mașinile virtuale, gazdele noastre gestionează mai bine surplusul și livrează performanță mai bună. În acest punct, am experimentat vagrant ca soluție la problema noastră pe medii diferite. Deși problema se poate rezolva așa, utilizarea unui mediu virtual complet necesită mai multe resurse din partea gazdei pentru a le putea rula.

  3. Nu lansăm cod sursă sau builduri, ci medii întregi. Când se folosesc containere, instalarea nu mai presupune actualizarea buildurilor pe mediile țintă ci și instalarea mediului. Acest fapt reduce posibilitatea ca buildurile să se strice, deoarece un pas din procesul de build eșuează sau nu funcționează conform așteptărilor. Deoarece instalăm un container care este gata de rulare, știm că ceea ce se află pe mediul țintă rulează corect și conține un build corect funcțional. Acesta este parte a CI-pipeline.

  4. Putem întreține containere ușor. Containerele Docker au un sistem de management de versiune pe "nivele", ceea ce se poate compara cu un sistem clasic de management al surselor. Când se fac schimbări la nivel de configurație sau la nivel de container, se păstrează doar diferențele dintre ultima versiune și noua versiune. Astfel, actualizările pe containerele Docker vor fi rapide și se va reduce volumul de date ce trebuie transferat. Containerele Docker sunt create pe baza imaginilor care pot fi doar sisteme de operare și pachete de bază sau un application-stack complet care poate fi lansat împreună cu containerele. Fiecare acțiune luată la nivel de container Docker adaugă un nou nivel nivelului predecesor. Acești pași sunt definiți într-un "Dockerfile":

Exemplu de dockerfile:

#
# This is a simple Dockerfile
#
FROM ubuntu:latest
MAINTAINER John doe "john.doe@3ss.tv"

RUN apt-get update
RUN apt-get install -y python python-pip wget
RUN pip install Flask

ADD hello.py /home/hello.py

WORKDIR /home

Acest Dockerfile preia ultima imagine ubuntu disponibilă (la dockerhub) și rulează 3 comenzi pentru a instala python și actualiza pachetele. Apoi, creează un fișier și stabilește directorul de lucru. Ca în exemplul de mai sus, se pot executa orice comenzi și acțiuni prin intermediul unui Dockerfile. Acest lucru este util, spre exemplu, pentru a crea imagini bazate pe o pre-configurare, iar apoi pentru a adăuga pași suplimentari ce trebuie adaptați sau extinși în timp, e.g. instalarea de pachete suplimentare sau realizarea de pași suplimentari pentru a configura și rula un serviciu

5. Putem scala mediile noastre fără a influența aplicația în vreun fel. Folosind un cluster și orchestrare putem rula oricâte instanțe dorim de pe orice container pe baza unor reguli foarte simple pentru load-balancing. Deoarece folosim containere individuale pentru fiecare serviciu, putem scala doar un punct de intrare special care se dovedește a fi puternic utilizat.

Provocări în realizarea infrastructurii

Alegerea furnizorului Cloud

Arhitectura bazată pe Cloud este o temă vastă. Utilizarea containerelor o face mai comprehensibilă în anumite aspecte, deoarece în procesul de implementare este nevoie doar de puterea computațională a serviciului Cloud prin alocarea de gazde care rulează docker-engines. Când ne îndepărtăm de partea experimentală și ajungem la o structură mai stabilă și mai apropiată de producție, mai putem întâlni probleme ce trebuie luate în calcul când alegem furnizorul și detaliile instalării:

Monitorizare și logare

Deoarece folosirea unei astfel de infrastructuri presupune că vor fi multe mașini care rulează, este crucial să se monitorizeze performanța instanțelor și a aplicațiilor. A fost o provocare să se găsească un setup corect datorită volumului și varietății surselor de date. Colectarea datelor este complexă deoarece a avea un număr atât de mare de containere multiple mărește volumul pentru sys-logs, service-logs și application logs, toate acestea având nevoie de colectare și structurare.

Am decis să folosim ELK stack pentru a realiza acest lucru. ELK este acronimul pentru Elastic Logstash and Kibana, un set de 3 tooluri utilizate împreună pentru a prelua (logstash), stoca și indexa (Elastic), dar și pentru a afișa (Kibana) datele. Pentru citirea și livrarea de log-files pentru mașini utilizăm filebeat, care poate citi loguri din stdin și din fișiere. Acesta păstrează o evidență a stărilor fiecărui log și se asigură că fiecare log-entry este livrat cu succes măcar o dată.

Pentru a strânge și organiza logurile am efectuat o serie de operații. Fiecare din serviciile noastre scrie loguri în format JSON ce conțin atribute bazate pe parametri de mediu ai containerelor care identifică serviciul printr-un service_name. Aceste loguri se scriu în fișierele de log stocate în foldere diferite bazate pe mediu/client/serviciu. În fiecare serviciu gazdă există deamonsets pentru filebeat - O capsulă rulează în fiecare gazdă care are aceeași etichetă ca nod selector al capsulei- ce rulează. Filebeat colectează logurile și le trimite către logstash. Logstash realizează filtrări în funcție de tipul de document (care este adăugat de filebeat pentru fiecare service log) și le salvează în indecși service-logs pentru Elastic. Pe baza service-names și service-types putem filtra și căuta în service-logs pentru a vedea loguri pentru servicii individuale și/sau medii etc. .

Deoarece folosim nginx ca punct de intrare pentru toate serviciile noastre, colectăm și nginx logs cu request time, upstream time și xforward pentru intrările din loguri, îmbogățindu-le cu informație de tip geolocație. Astfel, nu doar că înțelegem cât de bine lucrează serviciul, ci și cum se monitorizează performanța și cum se corelează informația.

Alegerea sistemelor de operare potrivite

Deși containerele Docker nu sunt mașini virtuale depline, acestea tot funcționează ca entități independente ce au nevoie de un sistem de operare și o configurare completă. De exemplu, pentru a rula un simplu container cu gazde și un nginx webserver, este nevoie de un sistem de operare care rulează nginx pentru a-l configura apoi nevoilor voastre.

Studii de caz tipice pentru containerele Docker presupun rularea de servicii web, în multe cazuri microservicii izolate. Mai mult, cum containerele Docker nu sunt mașini virtuale complete, se permite utilizarea unor sisteme de operare de capacitate redusă. Acest lucru este cu atât mai important cu cât unul dintre factorii care influențează performanța și managementul costului ține de dimensiunea imaginilor Docker. Cu cât imaginea este mai mică, cu atât mai puțin spațiu trebuie alocat și cu atât mai repede va porni containerul. Rămânând la cazul nginx-webserver, nu este nevoie să realizați o instalare completă de sistem de operare Debian cu toate librăriile și dependențele. Din acest motiv, arhitectura bazată pe containere a dus la popularitatea crescândă a distribuțiilor minimale de tip Linux, similare cu busybox. Aceste distribuții sunt facile, conținând un minimum de pachete care să ruleze aplicațiile necesare. Distribuții precum CoreOS sau RancherOS sunt construite pentru a rula containere (sau, mai specific, containere Docker) și a avea configurări și modificări specifice.

Alegerea orchestrării

De obicei, un singur container Docker nu va asigura configurația pe care o căutați. Distracția, în ceea ce privește containerele Docker, începe când poți produce și îndepărta containere Docker oricând, din moment ce nevoia de resurse se modifică - în cel mai bun caz fără efort manual. Pentru a realiza acest lucru este nevoie de un tool de orchestrare. Docker-engine oferă această facilitate din moment ce puteți porni, opri sau instala containere Docker din simple instrucțiuni în linii de comandă. Totuși, aceste capabilități sunt prea limitate pentru o configurare automată, pentru care veți avea nevoie de un alt engine care monitorizează consumul de resurse, utilizarea și acuratețea cu care funcționează containerele atât în privința gestionării instanțelor, cât și a traficului.

Există o serie de soluții disponibile.

Swarm

Acesta este un sistem de clustering nativ realizat de Docker. Este o parte componentă din Docker și este disponibilă din oficiu. Utilizează API-uri Docker standard și este cea mai apropiată de modul în care funcționează Docker. Este cel mai ușor de înțeles.

Kubernetes

Kubernetes este un sistem de orchestrare și clustering, întreținut de Google, care are particularități raportat la operațiile Docker native. Totuși, Kubernetes este cel mai popular sistem când vine vorba de medii de producere scalate care trebuie să se adapteze la diferite cerințe în ceea ce privește resursele pe baza încărcării și a traficului, cu grad mare de încredere.

Kubernetes și Google Cloud sunt o combinație bună pentru infrastructura noastră, cu o serie de beneficii:

Cel mai mare avantaj în utilizarea Kubernetes este instalarea automată de microservicii având la bază scripturi.

Configurarea unui load-balancer nu necesită multă informație sau experiență. Cu o singură comandă, Google Cloud rezervă IP-ul static și configurează load-balancer. Configurarea sistemului de filtrare și firewall este ușoară.

Din doar câteva comenzi pentru a selecta tipul de mașină și numărul de noduri pentru un grup, se poate configura un întreg cluster, împreună cu un dashboard și monitorizare prin Graphana și Heapster.

Actualizarea serviciilor sau schimbarea de proprietăți precum e cazul parametrilor ENV ai unei instalări sunt lucruri ușoare ce pot fi realizate automat prin Bash scripts, ceea ce ajută la mentenanța serviciilor.

Configurarea Kubernetes pe mașini Linux

Utilizând kubeadm se poate configura un cluster pe servere Linux. Ultima versiune pare a fi stabilă, ceea ce face scalarea clusterului ușoară prin adăugarea de gazde noi ca serviciu.Când acest lucru se întâmplă, capsulele sunt migrate și scalate automat către gazda nouă.

În timpul actualizării noii versiuni de Kubernetes am avut probleme, deci nu vom recomanda configurarea kubeadm pentru producție.

Rancher

Rancher este tentant deoarece are un UI prietenos ce permite gestionarea facilă a containerelor și a clusterelor. Ca orice UI prietenos, vine cu prețul "ascunderii" elementelor interne Docker, făcând foarte grea monitorizarea și rezolvarea problemelor.

Rancher este foarte util în dezvoltare. Este ușor de configurat și instalat servicii, care mai apoi să fie configurate cu Rancher UI, pe care îl recomandăm pentru dezvoltarea rapidă a proiectelor de mici dimensiuni care utilizează un număr mic de containere. Este de asemenea logic să folosiți toată configurația de micro-servicii din aplicație atât din punctul de vedere al dezvoltării, cât și al structurării. Orice programator devops abil poate face acest lucru, iar sistemul final de producție se poate baza pe Kubernetes sau pe orice altă configurație.

În medii de producție, Rancher ar trebui utilizat cu grijă. Uneori, serviciile și capsulele nu au mai răspuns, nu au primit adrese IP. Aceste probleme se pot rezolva ușor pe mediul de lucru zilnic, apoi aplicat în producție, dar este nevoie de timp și atenție pentru a le rezolva.

Concluzie: Pentru configurarea mediului de producție care răspunde expectanțelor de scalabilitate, automatizare și încredere (cel puțin în arii non-enterprise, open-source) Kubernetes este cea mai bună soluție, deși are cea mai abruptă curbă de învățare.

Scalarea și echilibrarea consumului de resurse (load balancing)

Deoarece infrastructura noastră se bazează pe Google Cloud și Kubernetes, am decis să folosim soluția de the load-balancing oferită de Google Cloud.

Noi servicii (în acest caz, nginx) se publică utilizând "Ingress deployment" ce folosește numele serviciului backend, Secret (creat înainte de cheia și certificatul ssl), și portul public. La instalarea acestui ingress se creează automat un LB pentru GC, se alocă un IP static, se aplică regula de redirecționare și se adaugă serverele backend.

Toate serviciile comunică prin portul 80, SSL-termination este gestionat de Google-cloud load-balancers.

În Google Cloud, se poate activa auto scale pentru grupuri de noduri. Când loadul o solicită, Google Cloud adaugă încă o gazdă în cluster, distribuie capsule către acel server și configurează load-balancer automat pentru a face referire la acest server de asemenea.

CI pipeline folosind Docker

Unul dintre cele mai mari avantaje ale existenței containerelor, din punctul de vedere al dezvoltării, este faptul că dezvoltarea, compilarea, testarea și instalarea pot fi separate foarte clar prin procese bine integrate. De asemenea, după configurarea inițială, programatorii devops pot gestiona singuri toate aceste componente sau pot automatiza procesul. Utilizarea containerelor permite și o gestionare corectă și mai coerentă a interdependențelor, deschide multe alte posibilități la nivel de tehnologie, inclusiv posibilitatea de a ne îndrepta spre un mod de programare mai modular, bazat pe componente. Cum compilarea unei imagini Docker presupune doar rularea unui set de comenzi, se realizează un pas mare spre automatizarea verificării, compilării, testării și instalării de microservicii, ceea ce economisește din efortul depus atunci când se realizează proiecte de dimensiuni medii sau mari. Nu trebuie să uităm de modularizare și de dezvoltarea bazată pe CI, ceea ce aduce eforturi suplimentare de gestionare a proiectului și a sarcinilor de lucru. Decizia de a opta pentru această soluție trebuie bine gândită.

Așa cum s-a discutat anterior, infrastructura Docker de bază conține un docker-engine, un container, o imagine și o bază de date. Când se integrează un CI-pipeline pentru Docker, taskul principal după o compilare cu succes este publicarea imaginilor în baza de date de unde pot fi luate de mediile țintă și executate. Aceasta este CI-pipeline pe scurt, deși e nevoie de automatizarea mai multor pași pentru o configurare completă:

Concluzie

Nu este atât de ușor precum pare să alcătuiţi un sistem structurat și să faceţi arhitectura să funcţioneze. Când am început dezvoltarea, a trebuit să ţinem cont de faptul că software-ul utilizat era la începuturile dezvoltării sale. Noile releaseuri au introdus adesea schimbări nocive, în timp ce altele nu au funcţionat conform așteptărilor. Această situaţie s-a îmbunătăţit, dar încă ne confruntăm cu provocări.

Mai mult, construirea unui CI-pipeline s-a dovedit complicată. Construirea containerelor a eșuat des, iar identificarea cauzei a necesitat multe încercări și greșeli.

Ultimul pas, orchestrarea, a avut nevoie de multă testare și cercetare, deoarece nu au existat multe implementări de referinţă și nici documentaţie adecvată despre cum să se realizeze corect.

Când aceste provocări sunt depășite, beneficiile sunt enorme:

  1. Instalarea este ușoară și rapidă. Aceasta se realizează automat pornind de la procesul vostru CI și puteţi fi siguri că ceea ce instalaţi funcţionează.

  2. Iniţierea noilor programatori, începerea de servicii noi, testarea și build-ul au devenit mult mai ușoare și mai eficiente.

  3. Nu trebuie să vă îngrijoreze scalarea aplicaţiei (cu excepţia plăţii facturii voastre de putere computaţională).

Ca orice soluţie, aceasta are teme ce necesită atenţie sporită. Cele care se aplică în cazul de faţă ar fi:

Peisajul containerelor se dezvoltă foarte rapid în acest moment. Sisteme de operare optimizate relativ la containere, instrumente de orchestrare și management, toate sunt dezvoltate și publicate, iar unele din abordările noastre ar trebui să fie revizuite pentru a ţine pasul cu dezvoltarea.

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects