Ce am învățat testând 200.000 de linii de cod de infrastructură

Ce am învățat testând 200.000 de linii de cod de infrastructură

Abordare IaC (Infrastructure as Code) nu constă doar din codul stocat în repository, ci și din persoanele și procesele care înconjoară acest cod. Este posibil să reutilizăm abordările din dezvoltarea software-ului în gestionarea și descrierea infrastructurii? Ar fi bine să păstrezi această idee în minte în timp ce citești articolul.

Versiunea în engleză

Aceasta este o prezentare a mea a discursului pe DevopsConf 2019-05-28.

Prezentări și videoclipuri

Infrastructure ca istorie bash

Ce am învățat testând 200.000 de linii de cod de infrastructură

Să presupunem că vii pe un nou proiect și ți se spune: „la noi Infrastructure as Code„. În realitate se dovedește că Infrastructure ca istorie bash sau, de exemplu, Documentație ca istorie bash. Aceasta este o situație complet reală, de exemplu, un astfel de caz a fost descris de Denis Lîsenco în prezentarea sa Cum să înlocuiești întreaga infrastructură și să începi să dormi liniștit, el a povestit cum din istoria bash au obținut o infrastructură bine structurată pentru proiect.

Cu puțină dorință, am putea spune că Infrastructure ca istorie bash este ca un cod:

  1. reproductibilitatea: poți lua istoria bash, să execuți comenzile de acolo, și, de fapt, poți obține o configurație funcțională la ieșire.
  2. versiune: știi cine a intrat și ce a făcut, din nou, nu este sigur că acest lucru te va conduce la o configurație funcțională la ieșire.
  3. este povestea: istoria a cine și ce a făcut. Doar că nu vei putea să o folosești dacă pierzi serverul.

Ce ar trebui să facem?

Infrastructure as Code

Ce am învățat testând 200.000 de linii de cod de infrastructură

Chiar și un caz așa de ciudat cum este Infrastructure ca istorie bash poate fi tras de urechi la Infrastructure as Code, dar când vrem să facem ceva mai complicat decât vechiul și bunul server LAMP, vom ajunge la concluzia că acest cod trebuie oarecum modificat, schimbat, dezvoltat. În continuare, vom considera paralele între Infrastructure as Code și dezvoltarea software-ului.

D.R.Y.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Pe un proiect de dezvoltare a unui sistem de stocare, a fost o sub-task de a configura periodic SDS: lansăm o nouă versiune — trebuie să o desfășurăm pentru teste ulterioare. Sarcina este extrem de simplă:

  • intră aici prin ssh și execută comanda.
  • copiezi fișierul acolo.
  • modifici configurația aici.
  • pornești serviciul acolo.
  • …
  • PROFIT!

Pentru logica descrisă, bash este mai mult decât suficient, mai ales în etapele incipiente ale proiectului, când acesta abia începe. Este nu este rău că folosești bash, dar, în timp, apar solicitări de a extinde ceva similar, dar ușor diferit. Primul lucru care îmi vine în minte: copierea și lipirea. Și astfel, avem deja două scripturi foarte asemănătoare care fac aproape același lucru. Cu timpul, numărul scripturilor a crescut, iar ne-am confruntat cu faptul că există o anumită logică de afaceri în desfășurarea instalării, pe care trebuie să o sincronizăm între diferitele scripturi, ceea ce este destul de complicat.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Se pare că există o astfel de practică D.R.Y. (Nu te repeta). Ideea este de a reutiliza codul existent. Pare simplu, dar nu am ajuns la asta imediat. În cazul nostru, a fost o idee banală: a separa configurațiile de scripturi. Adică, logica de afaceri despre cum se desfășoară instalarea separat, configurațiile separat.

S.O.L.I.D. pentru CFM

Ce am învățat testând 200.000 de linii de cod de infrastructură

Cu timpul, proiectul a crescut și continuarea naturală a fost apariția Ansible. Principala cauză a apariției sale este expertiza din echipă și că bash-ul nu este destinat logicii complexe. Ansible a început, de asemenea, să conțină logică complexă. Pentru ca logica complexă să nu devină haotică, în dezvoltarea software-ului există principii de organizare a codului S.O.L.I.D. De asemenea, de exemplu, Grigore Petrov în raportul său „De ce are nevoie un IT-ist de un brand personal” a abordat problema că oamenii sunt astfel structurați încât le este mai ușor să opereze cu entități sociale, iar în dezvoltarea software-ului acestea sunt obiecte. Dacă combinăm aceste două idei și continuăm să le dezvoltăm, putem observa că în descrierea infrastructurii putem folosi S.O.L.I.D. pentru ca ulterior să fie mai ușor să întreținem și să modificăm această logică.

Principiul responsabilității unice

Ce am învățat testând 200.000 de linii de cod de infrastructură

Fiecare clasă îndeplinește doar o singură sarcină.

Nu trebuie să amestecăm codul și să facem monștri divini și monoliti. Infrastructura trebuie să constea din cărămizi simple. Se pare că, dacă rupti playbook-ul Ansible în bucăți mici, citit roluri Ansible, acestea sunt mai ușor de întreținut.

Principiul deschis / închis

Ce am învățat testând 200.000 de linii de cod de infrastructură

Principiul deschiderii/închiderii.

  • Deschise pentru extindere: înseamnă că comportamentul entității poate fi extins prin crearea de noi tipuri de entități.
  • Închise pentru modificare: ca urmare a extinderii comportamentului entității, nu ar trebui aduse modificări în codul care folosește aceste entități.

Inițial, implementam infrastructura de testare pe mașini virtuale, dar datorită faptului că logica de business a desfășurării era separată de implementare, am adăugat cu ușurință desfășurarea pe bare metal.

Principiul Substituției Liskov

Ce am învățat testând 200.000 de linii de cod de infrastructură

Principiul substituției lui Barbara Liskov. Obiectele din program trebuie să fie substituibile cu instanțe ale subtipurilor lor, fără a altera corectitudinea executării programului.

Dacă privim mai amplu, nu este o specificitate a unui anumit proiect că acolo se poate aplica. S.O.L.I.D., în general se referă la CFM; de exemplu, într-un alt proiect trebuie să desfășurăm o aplicație Java cutie pe diverse servere Java, baze de date, OS etc. Pe acest exemplu voi analiza principiile ulterioare. S.O.L.I.D.

În cazul nostru, în cadrul echipei de infrastructură există un acord că, dacă am instalat rolul imbjava sau oraclejava, atunci avem un fișier executabil binar java. Acest lucru este necesar, deoarece rolurile superioare depind de acest comportament, ele așteaptă disponibilitatea java. De asemenea, acest lucru ne permite să înlocuim o implementare/versionare java cu alta fără a modifica logica desfășurării aplicației.

Problema aici constă în faptul că în Ansible nu se poate realiza așa ceva; ca urmare, în cadrul echipei apar anumite acorduri.

Principiul Segregării Interfeței

Ce am învățat testând 200.000 de linii de cod de infrastructură

Principiul segregării interfeței: „multe interfețe, special concepute pentru clienți, sunt mai bune decât o interfață de uz general.”

Inițial, am încercat să grupăm toată variabilitatea desfășurării aplicației într-un singur Ansible playbook, dar era greu de întreținut, iar abordarea, când avem o interfață specificată extern (clientul așteaptă portul 443), permite să combinăm infrastructura din cărămizi separate pentru implementarea specifică.

Principiul Inversiei Dependențelor

Ce am învățat testând 200.000 de linii de cod de infrastructură

Principiul inversării dependențelor: modulele de nivel superior nu trebuie să depindă de modulele de nivel inferior. Ambele tipuri de module trebuie să depindă de abstrații. Abstracțiile nu trebuie să depindă de detalii. Detaliile trebuie să depindă de abstrații.

Aici exemplul va fi bazat pe un antipattern.

  1. Unul dintre clienți avea un cloud privat.
  2. În interiorul cloud-ului, comandam mașini virtuale.
  3. Dar, din cauza specificităților cloud-ului, desfășurarea aplicației era legată de hypervisorul pe care a fost plasată VM.

Adică, logica de înalt nivel pentru desfășurarea aplicației, cu dependențele a avut loc la nivelurile de bază ale hypervizorului, ceea ce a însemnat probleme în reutilizarea acestei logici. Nu faceți așa.

Interacțiune

Ce am învățat testând 200.000 de linii de cod de infrastructură

Infrastructura ca și cod nu este doar despre cod, ci și despre relațiile dintre cod și oameni, despre interacțiunile dintre dezvoltatorii infrastructurii.

Bus factor

Ce am învățat testând 200.000 de linii de cod de infrastructură

Să presupunem că aveți pe proiect pe Vasya. Vasya știe tot despre infrastructura dvs., ce se va întâmpla dacă Vasya dispare brusc? Este o situație foarte reală, deoarece el poate fi lovit de un autobuz. Uneori se întâmplă așa. Dacă asta se va întâmpla iar cunoștințele despre cod, structura acestuia, modul în care funcționează, datele de acces și parolele nu sunt distribuite în echipă, s-ar putea să vă confruntați cu o serie de situații neplăcute. Pentru a minimiza aceste riscuri și a distribui cunoștințele în cadrul echipei, puteți utiliza diferite abordări.

Pair Devopsing

Ce am învățat testând 200.000 de linii de cod de infrastructură

Nu este ca în glumă, că administratorii beam bere, schimbau parolele, și analogul programării în pereche. Adică, doi ingineri se așază la un computer, la o tastatură și încep împreună să configureze infrastructura dvs.: să configureze serverul, să scrie rolul Ansible, etc. Sună frumos, dar la noi nu a funcționat. Totuși, cazuri particulare ale acestei practici au funcționat. A venit un angajat nou, mentorul său împreună cu el iau o sarcină reală, lucrează – împărtășesc cunoașterea.

Un alt caz particular este incident call. În timpul unei probleme, se adună un grup de deținători de responsabilitate și implicați, se numește un conducător care își partajează ecranul și își exprimă gândirea. Ceilalți participanți urmează gândirea conducătorului, observă trucuri din consolă, verifică ce nu a omis o linie din log, află lucruri noi despre sistem. Această abordare a funcționat mai mult decât nu.

Code Review

Ce am învățat testând 200.000 de linii de cod de infrastructură

Subiectiv, distribuția cunoștințelor despre infrastructură și modul în care este organizată s-a realizat mai eficient prin intermediul code review:

  • Infrastructura este descrisă în cod în repository.
  • Schimbările au loc într-o ramură separată.
  • La o cerere de merge se poate vedea delta modificărilor infrastructurii.

Atât de interesant a fost că revizorii erau aleși pe rând, conform unui program, ceea ce însemna că aveai o probabilitate de a accesa o nouă zonă a infrastructurii.

Cod de stil

Ce am învățat testând 200.000 de linii de cod de infrastructură

Pe măsură ce timpul a trecut, au început să apară neînțelegeri în timpul revizuirilor, deoarece recenzorii aveau propriul stil, iar schimbările frecvente ale recenzorilor le îngrămădeau cu stiluri diferite: 2 spații sau 4, camelCase sau snake_case. Implementarea acestora nu a fost imediată.

  • Prima idee a fost să recomandăm utilizarea unui linter, având în vedere că inginerii sunt, totuși, foarte inteligenți. Totuși, diferitele editoare și sisteme de operare erau incomode.
  • Aceasta a evoluat într-un bot care, pentru fiecare commit problematic, trimitea un mesaj în Slack și atașa ieșirea linterului. Dar, în cele mai multe cazuri, se găseau lucruri mai importante de făcut, iar codul rămânea necorectat.

Green Build Master

Ce am învățat testând 200.000 de linii de cod de infrastructură

Timpul trece și am ajuns la concluzia că nu putem permite angajamente în master care nu trec anumite teste. Iată-ne! Am inventat Green Build Master, care este deja o practică consacrată în dezvoltarea software-ului:

  • Dezvoltarea se desfășoară într-o ramură separată.
  • Teste sunt rulate pe această ramură.
  • Dacă testele nu trec, codul nu va ajunge în master.

Acceptarea acestei decizii a fost destul de dureroasă, deoarece a provocat multe dezbateri, dar a meritat, deoarece cererile de fuziune au început să vină fără neînțelegeri legate de stil, iar în timp, numărul locurilor problematice a început să scadă.

IaC Testing

Ce am învățat testând 200.000 de linii de cod de infrastructură

În afară de verificarea stilului, pot fi utilizate și alte lucruri, de exemplu, verificarea dacă infrastructura dumneavoastră se poate desfășura cu adevărat. Sau verificarea dacă schimbările în infrastructură nu vor duce la pierderi financiare. De ce ar fi nevoie de aceasta? Întrebarea este complicată și filosofică; cel mai bine ar fi să răspundem cu o poveste, că într-o zi a existat un auto-scaler pe PowerShell care, fără a verifica condițiile limită, a creat mai multe VM-uri decât era necesar, ceea ce a dus la cheltuieli mai mari pentru client decât se aștepta. Nu este plăcut, dar această eroare ar fi putut fi identificată cu ușurință în etape mai timpurii.

Poate te întrebi, de ce să faci infrastructura complexă și mai complicată? Testele pentru infrastructură, la fel ca și cele pentru cod, nu sunt despre simplificare, ci despre cunoașterea modului în care infrastructura ta ar trebui să funcționeze.

IaC Testing Pyramid

Ce am învățat testând 200.000 de linii de cod de infrastructură

IaC Testing: Static Analysis

Dacă desfășori întreaga infrastructură imediat și verifici dacă funcționează, ar putea să dureze o veșnicie și să necesite o mulțime de timp. Prin urmare, la bază ar trebui să fie ceva care funcționează rapid, este abundent și acoperă multe locuri primitive.

Bash este complicat

Iată un exemplu banal. Selectați toate fișierele din directorul curent și copiați-le într-o altă locație. Primul lucru care îmi vine în minte:

for i in * ; do 
    cp $i /some/path/$i.bak
done

Și ce se întâmplă dacă numele fișierului conține un spațiu? Ei bine, suntem deștepți, știm să folosim ghilimelele:

for i in * ; do cp "$i" "/some/path/$i.bak" ; done

Bravo? Nu! Ce se întâmplă dacă nu există nimic în director, adică globarea nu funcționează.

find . -type f -exec mv -v {} dst/{}.bak ;

Acum suntem deștepți? Nu... Am uitat că numele fișierului poate avea n.

touch x
mv x "$(printf "foonbar")"
find . -type f -print0 | xargs -0 mv -t /path/to/target-dir

Instrumente de analiză statică

Problema din pasul anterior ar fi putut fi depistată când am uitat ghilimelele; pentru aceasta există multe instrumente Shellcheck, în general sunt multe, și cel mai probabil veți putea găsi un linters pentru IDE-ul dumneavoastră care se potrivește cu stiva utilizată.

Limba
Instrument

bash
Shellcheck

Ruby
RuboCop

python
Pylint

ansible
Ansible Lint

IaC Testing: Unit Tests

Ce am învățat testând 200.000 de linii de cod de infrastructură

Așa cum am observat din exemplul anterior, linters nu sunt omnipotenți și nu pot identifica toate locurile problematice. Continuând pe aceeași idee cu testarea în dezvoltarea software-ului, ne vin în minte testele unității. Imediat îmi vin în minte shunit, junit, rspec, pytest. Dar ce facem cu ansible, chef, saltstack și altele asemenea?

La început, am vorbit despre S.O.L.I.D. faptul că infrastructura noastră ar trebui să fie compusă din cărămizi mici. A venit timpul lor.

  1. Infrastructura este divizată în cărămizi mici, de exemplu, roluri Ansible.
  2. Se desfășoară un anumit mediu, fie că este vorba de Docker sau VM.
  3. Pe acest mediu de testare aplicăm rolul nostru Ansible.
  4. Verificăm că totul a funcționat așa cum ne așteptam (executăm testele).
  5. Decidem dacă este ok sau nu.

IaC Testing: Unit Testing tools

Întrebarea este, ce sunt testele pentru CFM? Poate fi banal să rulezi un script, dar poți folosi soluții deja existente pentru aceasta:

CFM
Instrument

Ansible
Testinfra

Chef
Inspec

Chef
Serverspec

saltstack
Goss

Exemplu pentru testinfra, verificăm că utilizatorii test1, test2 există și fac parte din grupul sshusers:

def test_default_users(host):
    users = ['test1', 'test2' ]
    for login in users:
        assert host.user(login).exists
        assert 'sshusers' in host.user(login).groups

Ce să alegi? Întrebarea este complexă și nu are un răspuns clar; iată un exemplu de modificări în proiectele de pe github în anii 2018-2019:

Ce am învățat testând 200.000 de linii de cod de infrastructură

IaC Testing frameworks

Se ridică întrebarea cum să le adunăm pe toate și să le lansăm? Poți să iei și să faci totul tu singur dacă ai un număr suficient de ingineri. Sau poți lua soluții deja pregătite, dar nu sunt foarte multe:

CFM
Instrument

Ansible
Molecule

Chef
Test Kitchen

Terraform
Terratest

Exemplu de modificări în proiectele de pe github în anii 2018-2019:

Ce am învățat testând 200.000 de linii de cod de infrastructură

Molecule vs. Testkitchen

Ce am învățat testând 200.000 de linii de cod de infrastructură

Inițial am încercat să folosim testkitchen:

  1. Crearea unei VM în paralel.
  2. Aplică rolurile Ansible.
  3. Rulați inspecția.

Pentru 25-35 de roluri, aceasta a durat 40-70 de minute, ceea ce a fost mult.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Următorul pas a fost trecerea la jenkins / docker / ansible / molecule. Ideologic, totul este la fel.

  1. Lintuiți playbook-urile.
  2. Lintuiți rolurile.
  3. Porniți containerul.
  4. Aplică rolurile Ansible.
  5. Rulați testinfra.
  6. Verificați idempotenta.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Lintuirea pentru 40 de roluri și testele pentru zeci au început să dureze aproximativ 15 minute.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Ce să alegi depinde de mulți factori, cum ar fi stiva folosită, expertiza din echipă etc. Aici fiecare decide cum să abordeze problema testării unităților.

Testarea IaC: Teste de Integrare

Ce am învățat testând 200.000 de linii de cod de infrastructură

La următorul nivel al piramidei testării infrastructurii apar testele de integrare. Acestea sunt similare cu testele unității:

  1. Infrastructura este fragmentată în bucăți mici, de exemplu, roluri Ansible.
  2. Se desfășoară un anumit mediu, fie că este vorba de Docker sau VM.
  3. Acest mediu de testare este aplicat o mulțime rolurilor Ansible.
  4. Verificăm că totul a funcționat așa cum ne așteptăm (rulăm testele).
  5. Decidem dacă este ok sau nu.

Pe scurt, nu verificăm funcționalitatea unui singur element al sistemului ca în testele unității, ci verificăm cum este configurat serverul în ansamblu.

Testarea IaC: Teste End to End

Ce am învățat testând 200.000 de linii de cod de infrastructură

În vârful piramidei ne întâmpină testele End to End. Adică, nu verificăm funcționalitatea unui server individual, a unui script individual, a unei bucăți individuale a infrastructurii noastre. Verificăm dacă un număr de servere, unite, infrastructura noastră funcționează așa cum ne așteptăm. Din păcate, nu am văzut soluții standard gata făcute, probabil pentru că infrastructura este adesea unică și este greu de șablonat și de realizat un cadru pentru testarea ei. Ca rezultat, toată lumea creează propriile soluții. Există cerere, dar răspunsul lipsește. Așa că voi povesti ce există pentru a inspira pe alții sau pentru a-mi arăta că totul a fost deja inventat înaintea noastră.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Un proiect cu o istorie bogată. Este folosit în organizații mari și probabil că fiecare dintre voi a interacționat indirect cu el. Aplicația suportă numeroase baze de date, integrații etc. Cunoașterea modului în care poate arăta infrastructura este o mulțime de fișiere docker-compose, iar cunoașterea testelor care trebuie rulate în ce mediu - aceasta este jenkins.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Această schemă a funcționat suficient de bine, până când cercetări am încercat să o transferăm în Openshift. Containerele au rămas aceleași, dar mediul de rulare s-a schimbat ( salut D.R.Y. din nou).

Ce am învățat testând 200.000 de linii de cod de infrastructură

Cercetarea a evoluat, iar în openshift a fost descoperit un lucru numit APB (Ansible Playbook Bundle), care permite împachetarea cunoștințelor despre modul de desfășurare a infrastructurii într-un container. Adică, există un punct de cunoștință reproducibil și testabil despre cum să desfășurăm infrastructura.

Ce am învățat testând 200.000 de linii de cod de infrastructură

Toate acestea sunau bine, până când ne-am lovit de infrastructura heterogenă: pentru teste aveam nevoie de Windows. În cele din urmă, cunoștințele despre ce, unde și cum să desfășurăm și să testăm sunt stocate în jenkins.

Concluzie

Ce am învățat testând 200.000 de linii de cod de infrastructură

Infrastructure as Code este

  • Cod în repository.
  • Interacțiunea între oameni.
  • Testarea infrastructurii.

links

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster