
Cu cât mai rapid este procesul de dezvoltare, cu atât mai repede se dezvoltă o companie tehnologică.
Din păcate, aplicațiile moderne funcționează împotriva noastră - sistemele noastre trebuie să se actualizeze în timp real, fără a deranja pe nimeni sau a provoca downtime-uri și întreruperi. Implementarea în astfel de sisteme devine o sarcină complexă și necesită pipeline-uri complicate de livrare continuă chiar și în echipe mici.
Aceste pipeline-uri au, de obicei, o utilizare restrânsă, funcționează lent și nu sunt fiabile. Dezvoltatorii trebuie să le creeze mai întâi manual și apoi să le gestioneze, iar companiile angajează adesea echipe întregi de DevOps pentru aceasta.
Viteza acestor pipeline-uri determină viteza de dezvoltare. La cele mai bune echipe, implementarea durează între 5-10 minute, dar de obicei durează mult mai mult, necesitând câteva ore pentru o singură implementare.
La Dark, acest lucru durează 50 ms. Cincizeci. Milisecunde.. Dark - , creată special pentru livrare continuă, iar toate aspectele Dark, inclusiv limbajul în sine, sunt construite având în vedere implementarea instantanee și sigură.
De ce sunt pipeline-urile de livrare continuă atât de lente?
Să spunem că avem o aplicație web Python și deja am creat un pipeline de livrare continuă minunat și modern. Pentru un dezvoltator care lucrează zilnic la acest proiect, implementarea unei singure modificări minore va arăta aproximativ astfel:
Facerea modificărilor
- Crearea unei noi ramuri în git
- Facerea modificărilor cu comutatorul de funcții
- Testare modulară pentru a verifica modificările cu și fără comutatorul de funcții
Pull request
- Comitere a modificărilor
- Încărcarea modificărilor în depozitul remote pe github
- Pull request
- Construcția CI se execută automat în fundal
- Revizuirea codului
- Încă câteva revizuiri, dacă este necesar
- Fuzionarea modificărilor cu master-ul git.
CI se execută pe master.
- Instalarea dependențelor frontend prin npm
- Construirea și optimizarea resurselor HTML+CSS+JS
- Rularea testelor modulare și funcționale în frontend
- Instalarea dependențelor Python din PyPI
- Rularea testelor modulare și funcționale în backend
- Testarea integrării la ambele capete
- Încărcarea resurselor frontend în CDN
- Construirea containerului pentru aplicația Python.
- Trimiterea containerului în registru
- Actualizarea manifestului Kubernetes
Înlocuirea vechiului cod cu unul nou
- Kubernetes lansează mai multe instanțe ale noului container
- Kubernetes așteaptă ca instanțele să devină operative
- Kubernetes adaugă instanțele în echilibratorul de încărcare HTTP
- Kubernetes așteaptă ca vechile instanțe să nu mai fie folosite
- Kubernetes oprește vechile instanțe
- Kubernetes repetă aceste operațiuni până când noile instanțe înlocuiesc toate vechile
Activarea noului comutator de funcții
- Noul cod este activat doar pentru sine, pentru a se asigura că totul este în regulă
- Noul cod este activat pentru 10% din utilizatori, urmărind metricile operaționale și de afaceri
- Noul cod este activat pentru 50% din utilizatori, urmărind metricile operaționale și de afaceri
- Noul cod este activat pentru 100% din utilizatori, urmărind metricile operaționale și de afaceri
- În final, repeți întreaga procedură pentru a elimina vechiul cod și comutatorul
Procesul depinde de instrumente, limbaj și utilizarea arhitecturilor orientate pe servicii, dar în linii mari arată astfel. Nu am menționat desfășurările cu migrarea bazelor de date, deoarece aceasta necesită o planificare atentă, dar mai jos voi explica cum se ocupă Dark cu aceasta.
Există multe componente și multe dintre ele pot încetini, eșua, provoca concurență temporară sau prăbuși sistemul de lucru.
Și cum aceste pipeline-uri sunt aproape întotdeauna create pentru cazuri speciale, este greu să te bazezi pe ele. Multe zile, codul nu poate fi desfășurat pentru că există probleme în Dockerfile, unul dintre zecile de servicii a eșuat sau specialistul necesar este în concediu.
Mai rău, multe dintre aceste etape nu fac nimic util. Au fost necesare înainte, când desfășuram codul direct pentru utilizatori, dar acum avem comutatoare pentru noul cod și aceste procese s-au separat. Ca rezultat, etapa în care se desfășoară codul (vechiul înlocuit cu noul) a devenit acum doar un risc suplimentar.
Desigur, acesta este un pipeline foarte bine gândit. Echipa care l-a creat nu a economisit timp sau bani pentru desfășurarea rapidă. De obicei, pipeline-urile de desfășurare sunt mult mai lente și mai nesigure.
Implementarea livrării continue în Dark
Livrarea continuă este atât de importantă pentru Dark, încât ne-am concentrat de la început pe un timp mai mic de o secundă. Am analizat fiecare pas din pipeline pentru a elimina tot ce era inutil, iar restul l-am perfecționat. Iată cum am eliminat pașii.
Jessie Frazelle () a venit cu un nou cuvânt, deployless (fără desfășurare), la conferința Future of Software Development din Reykjavik.
Imediat am decis că Dark se va baza pe conceptul de «deployless» (mulțumim pentru neologism). Deployless înseamnă că orice cod este desfășurat instantaneu și gata de utilizare în producție. Sigur, nu vom lăsa cod defectuos sau incomplet să treacă (principiile de securitate le voi explica mai jos).
La demonstrarea Dark, adesea am fost întrebați cum am reușit să accelerăm desfășurarea. O întrebare ciudată. Probabil că oamenii cred că am inventat o supertehnologie care compară codul, îl compilează, îl împachetează într-un container, pornește o mașină virtuală și lansează containerul rece, toate acestea în 50 ms. Este puțin probabil să fie posibil. Dar am creat un motor de desfășurare special care nu are nevoie de toate acestea.
Dark rulează interpretoare în cloud. Să presupunem că scrieți cod într-o funcție sau un handler HTTP sau de evenimente. Trimitem diff-ul în arborele de sintaxă abstract (implementarea codului pe care editorul nostru și serverele noastre îl folosesc intern) către serverele noastre, iar apoi executăm codul când vin solicitările. Așadar, desfășurarea arată pur și simplu ca o înregistrare modestă în baza de date – rapidă și elementară. Desfășurarea se face atât de repede, pentru că include minimum.
În viitor, plănuim să transformăm Dark într-un compilator de infrastructură care va crea și va rula infrastructura ideală pentru performanța ridicată și fiabilitatea aplicațiilor. Desfășurarea instantanee, desigur, nu va dispărea.
Desfășurare sigură
Editor structurat
Codul din Dark este scris în editorul Dark. Editorul structurat nu permite erori de sintaxă. Practic, în Dark nu există niciun analizzor. Atâta timp cât introduceți text, lucrăm direct cu arborele de sintaxă abstractă (AST), așa cum ar fi , , , și .
Orice cod neterminat în Dark are o semantica de execuție validă, aproximativ ca . De exemplu, dacă schimbi apelul unei funcții, păstrăm funcția veche până când cea nouă devine adecvată.
Fiecare program din Dark are sensul său, așa că codul incomplet nu împiedică funcționarea celui complet.
Moduri de editare
Scrii cod în Dark în două situații. Prima: scrii un cod nou și ești singurul utilizator. De exemplu, acesta se află în REPL și alți utilizatori nu vor avea niciodată acces la el, sau este un nou endpoint HTTP la care nu te referi nicăieri. Aici se poate lucra fără măsuri de precauție, și acum funcționezi cam așa în mediul de dezvoltare.
A doua situație: codul este deja folosit. Dacă traficul trece prin cod (funcții, gestionari de evenimente, baze de date, tipuri), trebuie să fii precaut. Pentru aceasta, blocăm tot codul utilizat și cerem utilizarea unor instrumente mai structurate pentru editare. Voi explica despre instrumentele structurale mai jos: comutatoarele de funcții pentru gestionarii HTTP și evenimente, o platformă puternică de migrare pentru baze de date și o nouă metodă de gestionare a versiunilor pentru funcții și tipuri.
Comutatoare de funcții
Una dintre modalitățile în Dark este să rezolvi mai multe probleme cu o singură soluție. Comutatoarele de funcții îndeplinesc multe sarcini diferite: înlocuirea mediului local de dezvoltare, ramurile git, desfășurarea codului și, desigur, lansarea tradițională lentă și controlată a noului cod.
Crearea și desfășurarea unui comutator de funcții se realizează în editorul nostru într-o singură operațiune. Acesta creează un spațiu gol pentru noul cod și oferă controale pentru accesul la codul vechi și nou, precum și butoane și comenzi pentru a trece treptat la noul cod sau a-l exclude.
Comutatoarele de funcții sunt integrate în limbajul Dark, iar chiar și comutatoarele incomplete își îndeplinesc sarcina — dacă condiția din comutator nu este îndeplinită, va fi executat codul vechi blocat.
Mediul de dezvoltare
Comutatoarele funcțiilor înlocuiesc mediul local de dezvoltare. Astăzi, echipelor le este greu să se asigure că toți utilizează aceleași versiuni ale instrumentelor și bibliotecilor (instrumente de formatare a codului, linte, gestionare a pachetelor, compilatoare, preprocessori, instrumente de testare etc.) Cu Dark, nu trebuie să instalăm dependențe local, să gestionăm instalarea locală Docker sau să facem alte demersuri pentru a asigura cel puțin o formă de egalitate între mediul de dezvoltare și producție. , nici măcar nu ne vom face că ne străduim să o obținem.
În loc să creăm un mediu local clonat, comutatoarele din Dark creează o nouă cutie de nisip în producție, care înlocuiește mediul de dezvoltare. În viitor, plănuim de asemenea să creăm o cutie de nisip pentru alte părți ale aplicației (de exemplu, clone instantanee ale bazei de date), deși momentan nu pare atât de important.
Ramuri și desfășurări
Există acum mai multe modalități de a introduce cod nou în sisteme: ramuri git, etape de desfășurare și comutatoare de funcții. Acestea rezolvă aceeași problemă în diferite părți ale fluxului de lucru: git – în etapele anterioare desfășurării, desfășurarea – în momentul tranziției de la vechi la noul cod, iar comutatoarele funcției – pentru lansarea controlată a noului cod.
Cea mai eficientă modalitate este comutatoarele funcțiilor (de asemenea, cea mai simplă de înțeles și utilizat). Cu acestea, putem renunța complet la celelalte două metode. Este deosebit de util să eliminăm desfășurarea – dacă oricum folosim comutatoarele funcțiilor pentru a activa codul, atunci pașii de transfer al serverelor la noul cod creează doar riscuri suplimentare.
Git este dificil de utilizat, mai ales pentru începători, și aceasta îl limitează semnificativ, dar are ramuri convenabile. Am îmbunătățit multe dintre dezavantajele git. Dark permite editarea în timp real și oferă posibilitatea colaborării în stil Google Docs, astfel încât nu este necesar să trimitem cod și putem face rebazare și unire mai rar.
Comutatoarele de funcții stau la baza desfășurării sigure. Împreună cu desfășurările instantanee, acestea permit testarea rapidă a conceptelor în fragmente mici, cu risc scăzut, în loc să se aplice o singură modificare majoră care ar putea provoca o cădere a sistemului.
Versionare
Pentru a schimba funcții și tipuri, utilizăm versiuni. Dacă doriți să modificați o funcție, Dark creează o nouă versiune a acestei funcții. Apoi, puteți apela această versiune folosind comutatorul din handler-ul HTTP sau din evenimente. (Dacă este o funcție adânc în graful de apeluri, atunci se creează o nouă versiune a fiecărei funcții pe parcurs. Poate părea excesiv, dar funcțiile nu interferează dacă nu le utilizați, astfel că nici nu o veți observa.)
Din aceleași motive, versiunea tipurilor este, de asemenea, implementată. Am discutat detaliat despre sistemul nostru de tipuri .
Datorită versiuni funcțiilor și tipurilor, puteți face modificări în aplicație treptat. Puteți verifica că fiecare handler funcționează cu noua versiune, fără a fi nevoie să faceți toate modificările în aplicație simultan (dar avem instrumente pentru a face acest lucru rapid, dacă doriți).
Este mult mai sigur decât desfășurarea a totul simultan, așa cum se întâmplă în prezent.
Versiuni noi ale pachetelor și biblioteca standard
Când actualizați un pachet în Dark, nu înlocuim imediat utilizarea fiecărei funcții sau tipuri din întreaga bază de cod. Aceasta nu este sigur. Codul continuă să folosească aceeași versiune pe care a folosit-o, iar dvs. actualizați utilizarea funcțiilor și tipurilor la noua versiune pentru fiecare caz specific folosind comutatoarele.
Captura de ecran a unei părți din procesul automatizat din Dark, arătând două versiuni ale funcției Dict::get. Dict::get_v0 returna tipul Any (de care ne desprindem), iar Dict::get_v1 returnează tipul Option.
Frecvent, oferim o nouă funcție în biblioteca standard și excludem versiunile vechi. Utilizatorii cu versiuni mai vechi în cod vor păstra accesul la acestea, dar utilizatorii noi nu le vor putea obține. Intenționăm să oferim instrumente pentru a migra utilizatorii de la versiunile vechi la cele noi într-un singur pas, din nou cu ajutorul comutatoarelor de funcții.
Dark oferă, de asemenea, o oportunitate unică: deoarece executăm codul dvs. de lucru, putem testa noi versiuni, comparând ieșirile pentru cererile noi și vechi, astfel încât să vă informăm cu privire la modificări. Prin urmare, actualizarea pachetelor, care adesea se face în orb (sau necesită teste riguroase pentru siguranță), prezintă mult mai puține riscuri și poate avea loc automat.
Versiuni noi Dark
Trecerea de la Python 2 la Python 3 s-a întins pe un deceniu și rămâne o problemă. De vreme ce construim Dark pentru livrare continuă, trebuie să luăm în considerare aceste schimbări ale limbajului.
Când facem mici modificări în limbaj, creăm o nouă versiune Dark. Codul vechi rămâne în vechea versiune Dark, iar codul nou este folosit în noua versiune. Pentru a trece la noua versiune Dark, pot fi folosite comutatoare sau versiuni de funcții.
Acest lucru este deosebit de util, având în vedere că Dark a apărut foarte recent. Multe schimbări în limbaj sau bibliotecă pot fi nereușite. Versiunea treptată a limbajului ne permite să facem actualizări minore, adică putem să nu ne grăbim și să amânăm multe decizii legate de limbaj până când avem mai mulți utilizatori, ceea ce înseamnă și mai multe informații.
Migrarea bazelor de date
Pentru migrarea sigură a bazei de date există :
- Să rescriem codul pentru a sprijini atât formatele noi, cât și cele vechi
- Să transformăm toate datele într-un nou format
- Să eliminăm accesul vechi la date
În rezultat, migrarea bazei de date se desfășoară mai lent și solicită multe resurse. Și avem scheme învechite, deoarece chiar și sarcinile simple, cum ar fi corectarea numelui unei tabele sau al unei coloane, nu merită efortul depus.
Dark are o platformă eficientă de migrare a bazelor de date, care (sperăm) va simplifica atât de mult procesul, încât nu veți mai fi teamă de el. Toate depozitele de date în Dark (depozite de perechi „cheie-valoare” sau tabele hash persistente) au un tip. Pentru a transfera un depozit de date, pur și simplu îi atribuiți un nou tip și o funcție de revenire pentru a transforma valorile între cele două tipuri.
Accesul la depozitele de date în Dark se face prin nume de variabile versionate. De exemplu, depozitul de date Users va fi inițial denumit Users-v0. Când se creează o nouă versiune cu un alt tip, numele se schimbă în Users-v1. Dacă datele sunt salvate prin Users-v0, iar tu le accesezi prin Users-v1, se aplică funcția de rollback. Dacă datele sunt salvate prin Users-v1, iar tu le accesezi prin Users-v0, se aplică funcția de rollback.
Ecranul de migrare a bazei de date cu numele câmpurilor din vechea bază de date, expresiile de rollback și rollback și instrucțiunile pentru activarea migrației.
Folosește switch-uri de funcții pentru a redirecționa apelurile de la Users-v0 la versiunea Users-v1. Acest lucru se poate face câte un handler HTTP pe rând, pentru a reduce riscurile, iar switch-urile funcționează pentru utilizatori individuali, astfel încât să poți verifica dacă totul funcționează așa cum era de așteptat. Când nu vor mai exista utilizatori pentru Users-v0, Dark va transforma în fundal toate datele rămase din vechiul format în noul format. Nici nu vei observa.
Testare
Dark este și valori imutabile, ceea ce face ca suprafața de testare să fie semnificativ mai mică în comparație cu limbajele orientate pe obiect cu tipizare dinamică. Totuși, trebuie să testăm.
În Dark, editorul rulează automat teste de unitate în fundal pentru codul editat și, în mod implicit, execută aceste teste pentru toate switch-urile de funcții. În viitor, dorim să folosim tipurile statice pentru a realiza fuzzing-ul codului automat, pentru a găsi bug-uri.
În plus, Dark rulează infrastructura ta în producție, deschizând noi oportunități. Salvăm automat cererile HTTP în infrastructura Dark (deocamdată, salvăm toate cererile, dar ulterior vrem să trecem la selecție). Testăm codul nou folosind aceste cereri și efectuăm teste de unitate, iar dacă dorești, poți transforma cu ușurință cererile interesante în teste de unitate.
De ce ne-am debarasat
Deoarece nu avem desfășurare, dar avem switch-uri de funcții, aproximativ 60% din pipeline-ul de desfășurare rămâne deoparte. Nu avem nevoie de ramuri git sau pull request-uri, de construire a resurselor backend și a containerelor, de trimiterea resurselor și containerelor în registry-uri sau de pași de desfășurare în Kubernetes.
Comparația între pipeline-ul standard de livrare continuă (stânga) și livrarea continuă Dark (dreapta). În Dark, livrarea constă din 6 pași și un ciclu, în timp ce versiunea tradițională include 35 de pași și 3 cicluri.
În Dark, implementarea are doar 6 pași și 1 ciclu (pași care se repetă de mai multe ori), în timp ce pipeline-ul modern de livrare continuă constă din 35 de pași și 3 cicluri. În Dark, testele sunt rulate automat, fără să-i vedeți; dependențele se instalează automat; tot ce ține de git sau Github nu mai este nevoie; nu este nevoie să construiți, testați și trimiteți containere Docker; implementarea în Kubernetes nu mai este necesară.
Chiar și pașii rămași în Dark au devenit mai simpli. Deoarece comutatoarele de funcții pot fi gestionate cu o singură acțiune, nu este nevoie să treci din nou prin întregul pipeline de implementare pentru a elimina codul vechi.
Am simplificat livrarea codului cât am putut, reducând timpul și riscurile asociate livrării continue. De asemenea, am simplificat semnificativ actualizările pachetelor, migrarea bazelor de date, testarea, gestionarea versiunilor, instalarea dependențelor, egalitatea între mediu de dezvoltare și producție și upgrade-uri rapide și sigure ale versiunilor limbajului.
Răspund la întrebările despre asta pe .
Pentru a afla mai multe despre dispozitivul Dark, citiți , (sau la ) sau . Dacă mergeți la StrangeLoop în septembrie, .
Sursa: habr.com
