Ce este Docker: o scurtă introducere în istorie și principalele sale abstractizări

Pe 10 august a început un curs video despre Docker, în care analizăm totul — de la principalele abstractizări până la parametrii de rețea.

În acest articol, vom discuta despre istoricul apariției Docker și despre principalele sale abstractizări: Image, Cli, Dockerfile. Lecția este destinată începătorilor, așa că, cu siguranță, nu va fi interesantă pentru utilizatorii experimentați. Nu vor fi detalii tehnice complexe, doar cele mai de bază informații.

Ce este Docker: o scurtă introducere în istorie și principalele sale abstractizări

Ce este Docker

Să aruncăm o privire asupra definiției Docker de pe Wikipedia.

Docker este un software pentru automatizarea desfășurării și gestionării aplicațiilor în medii care suportă containerizarea.

Din această definiție nu reiese mare lucru. În special, nu este clar ce înseamnă „în medii care suportă containerizarea”. Pentru a înțelege, să ne întoarcem în timp. Să începem cu epoca pe care o numesc „Era Monolitică”.

Era Monolitică

Era Monolitică este începutul anilor 2000, când toate aplicațiile erau monolitice, cu multe dependențe. Dezvoltarea dura mult timp. În același timp, serverele nu erau foarte multe, le cunoșteam pe nume și le monitorizam. Există o comparație amuzantă:

Redați video

Pets — acestea sunt animale de companie. În era monolitică, ne raportam la serverele noastre ca la animale de companie, le îngrijeam și le protejam, îndepărtând praful de pe ele. Iar pentru o gestionare mai bună a resurselor, foloseam virtualizarea: luam un server și îl împărțeam în mai multe mașini virtuale, asigurând astfel izolarea mediului.

Sisteme de virtualizare bazate pe hypervisor

Despre sistemele de virtualizare cu siguranță ați auzit: VMware, VirtualBox, Hyper-V, Qemu KVM etc. Acestea oferă izolare pentru aplicații și gestionarea resurselor, dar au și dezavantaje. Pentru a face virtualizare, este necesar un hypervisor. Iar hypervisorul adaugă un overhead de resurse. Mașina virtuală în sine este, de obicei, destul de voluminoasă — o imagine grea, pe care se află sistemul de operare, Nginx, Apache și posibil și MySQL. Imaginea este mare, iar operarea cu mașinile virtuale poate fi incomodă. Ca urmare, lucrul cu virtualizările poate fi lent. Pentru a rezolva această problemă, au fost create sisteme de virtualizare la nivel de kernel.

Sisteme de virtualizare la nivel de kernel

Virtualizarea la nivel de kernel este susținută de sisteme precum OpenVZ, Systemd-nspawn, LXC. Un exemplu reprezentativ de astfel de virtualizare este LXC (Containere Linux).

LXC este un sistem de virtualizare la nivel de sistem de operare pentru a rula mai multe instanțe izolate ale sistemului de operare Linux pe un singur nod. LXC nu folosește mașini virtuale, ci creează un mediu virtual cu propriul său spațiu de procese și stiva de rețea.

Practic, LXC creează containere. Care este diferența între mașinile virtuale și containere?

Ce este Docker: o scurtă introducere în istorie și principalele sale abstractizări

Un container nu este potrivit pentru a izola procese: în sistemele de virtualizare la nivel de nucleu se găsesc vulnerabilități care permit deschiderea containerului către gazdă. Prin urmare, dacă aveți nevoie să izolați ceva, este mai bine să folosiți o mașină virtuală.

Diferențele dintre virtualizare și containerizare pot fi observate în diagramă.
Există hypervizori hardware, hypervizori deasupra sistemului de operare și containere.

Ce este Docker: o scurtă introducere în istorie și principalele sale abstractizări

Hypervizorii "de fier" sunt o soluție excelentă, dacă doriți să izolați ceva cu adevărat. Deoarece există posibilitatea de a izola la nivel de pagini de memorie și procesoare.

Există hypervizori ca programe și există containere, despre care vom vorbi mai departe. În sistemele de containerizare, nu există hypervizor, ci există un Container Engine, care creează și gestionează containere. Această soluție este mai ușoară, de aceea, datorită lucrului cu nucleul, overhead-ul este mai mic sau chiar inexistent.

Ce este utilizat pentru containerizarea la nivel de nucleu

Tehnologiile principale care permit crearea unui container izolat de alte procese sunt Namespaces și Control Groups.

Namespaces: PID, Networking, Mount și User. Există și altele, dar pentru simplificarea înțelegerii ne vom opri asupra acestora.

PID Namespace limitează procesele. Când, de exemplu, creăm un PID Namespace și plasăm un proces acolo, acesta devine cu PID 1. De obicei, în sistemele PID 1 este systemd sau init. Astfel, când plasăm un proces într-un nou namespace, acesta primește și el PID 1.

Networking Namespace permite limitarea/izolarea rețelei și plasarea interfețelor proprii în interior. Mount reprezintă o limitare a sistemului de fișiere. User reprezintă limitări pe baza utilizatorilor.

Control Groups: Memorie, CPU, IOPS, Rețea — în total aproximativ 12 setări. Acestea mai sunt cunoscute și sub numele de Cgroups ("C grupuri").

Control Groups gestionează resursele pentru container. Prin Control Groups putem specifica că un container nu trebuie să consume mai mult decât o anumită cantitate de resurse.

Pentru ca containerizarea să funcționeze corespunzător, sunt utilizate tehnologii suplimentare: Capabilities, Copy-on-write și altele.

Capabilities se referă la faptul că spunem procesului ce poate face și ce nu poate. La nivelul nucleului, acestea sunt pur și simplu hărți de biți cu numeroase parametrii. De exemplu, utilizatorul root are privilegii complete, poate face tot. Serverul de timp poate schimba timpul de sistem: are capabilități pentru Time Capsule, și atât. Prin privilegii, putem configura flexibil restricțiile pentru procese și astfel ne putem proteja.

Sistemul Copy-on-write ne permite să lucrăm cu imaginile Docker, folosindu-le mai eficient.

În prezent, Docker are probleme cu compatibilitatea Cgroups v2, de aceea articolul se concentrează asupra Cgroups v1.

Dar să ne întoarcem la poveste.

Când au apărut sistemele de virtualizare la nivelul nucleului, acestea au început să fie utilizate intensiv. Overhead-ul pe hypervizor a dispărut, dar au rămas unele probleme:

  • imaginile mari: în OpenVZ sunt împinse sistemul de operare, biblioteci, o mulțime de software divers, și, în cele din urmă, imaginea tot devine destul de mare;
  • nu există un standard normal pentru ambalare și livrare, de aceea rămâne problema dependențelor. Există situații în care două bucăți de cod folosesc aceeași bibliotecă, dar cu versiuni diferite. Între ele poate apărea un conflict.

Pentru a rezolva toate aceste probleme, a venit următoarea eră.

Era containerelor

Când a început Era containerelor, filosofia de lucru cu acestea s-a schimbat:

  • Un proces — un container.
  • Toate dependențele necesare procesului sunt livrate în containerul său. Aceasta necesită împărțirea monolitului în microservicii.
  • Cu cât imaginea este mai mică, cu atât mai bine — mai puține vulnerabilități posibile, se desfășoară mai repede și așa mai departe.
  • Instanțele devin efemere.

Țineți minte, am vorbit despre pets vs cattle? În trecut, instanțele erau asemănătoare animalelor de companie, iar acum sunt ca și bovinele — vite. În trecut, era un monolit — o aplicație. Acum sunt 100 de microservicii, 100 de containere. Unele containere pot avea 2-3 replici. Nu mai este atât de important să controlăm fiecare container. Ce este cu adevărat important este disponibilitatea serviciului în sine: ceea ce face acest set de containere. Acest lucru schimbă abordările în monitorizare.

Între 2014-2015, a avut loc apogeul Docker — tehnologia despre care vom vorbi acum.

Docker a schimbat filosofia și a standardizat ambalarea aplicațiilor. Cu ajutorul Docker, putem ambala o aplicație, o trimitem într-un repository, o descărcăm de acolo și o desfășurăm.

Într-un container Docker, includem tot ce este necesar, astfel încât problema dependențelor este rezolvată. Docker garantează reproducibilitatea. Cred că mulți s-au confruntat cu probleme de reproducibilitate: la tine totul funcționează, dar când faci push pe producție, acolo nu mai funcționează. Cu Docker, această problemă dispare. Dacă containerul tău Docker pornește și face ceea ce trebuie să facă, atunci cu o probabilitate mare va porni pe producție și acolo va face același lucru.

O notă despre overhead

Există întotdeauna dispute pe tema overhead-ului. Unii consideră că Docker nu aduce o încărcătură suplimentară, deoarece folosește nucleul Linux și toate procesele sale necesare pentru containerizare. Adică, „dacă spui că Docker este overhead, atunci și nucleul Linux este overhead”.

Pe de altă parte, dacă ne aprofundăm, există într-adevăr câteva aspecte în Docker despre care putem spune, cu o anumită întindere, că sunt overhead.

Primul este PID namespace. Când plasăm un proces în namespace, i se alocă PID 1. În același timp, acestui proces îi este atribuit și un alt PID, care se află în namespace-ul gazdei, dincolo de container. De exemplu, am pornit Nginx într-un container, acesta a devenit PID 1 (procesul principal). Iar pe gazdă are PID 12623. Și este greu de spus cât de mult reprezintă asta un overhead.

Al doilea aspect este Cgroups. Să luăm Cgroups pentru memorie, adică capacitatea de a limita memoria unui container. Atunci când este activat, se activează contorii, memory accounting: nucleul trebuie să înțeleagă câte pagini au fost alocate și câte sunt încă libere pentru acest container. Acesta ar putea fi un overhead, dar nu am întâlnit studii precise despre cum influențează performanța. Și eu personal nu am observat că aplicația, rulată în Docker, ar fi pierdut brusc din performanță.

Și încă un comentariu despre performanță. Anumite parametru ai nucleului sunt trecute de la gazdă în container. În special, anumiți parametru de rețea. Prin urmare, dacă dorești să rulezi ceva de mare performanță în Docker, de exemplu ceva care va utiliza intens rețeaua, atunci, cel puțin, va trebui să ajustezi acești parametru. De exemplu, nf_conntrack.

Despre conceptul Docker

Docker este compus din mai multe componente:

  1. Docker Daemon — motorul containerelor; acesta lansează containere.
  2. Docker CII — un utilitar pentru gestionarea Docker.
  3. Dockerfile — instrucțiuni pentru construirea unei imagini.
  4. Image — imaginea din care se desfășoară containerele.
  5. Container.
  6. Docker registry — un depozit de imagini.

Din punct de vedere schematic, arată cam așa:

Ce este Docker: o scurtă introducere în istorie și principalele sale abstractizări

Pe Docker_host funcționează Docker daemon, care lansează containere. Există un Client care trimite comenzi: construiește imaginea, descarcă imaginea, lansează containerul. Docker daemon se conectează la registry și le execută. Docker client poate să se adreseze și local (prin unix socket), și prin TCP de pe un host la distanță.

Să parcurgem fiecare componentă.

Docker daemon (demonul) — partea serverului, acesta funcționează pe mașina gazdă: descarcă imagini și lansează containere din ele, creează rețea între containere, colecționează loguri. Când spunem „creează imagine”, demonul se ocupă de aceasta.

Docker CLI — partea client a Docker, un utilitar de consolă pentru lucru cu demonul. O repet, poate funcționa nu doar local, ci și prin rețea.

Comenzile de bază:

docker ps — arată containerele care sunt acum lansate pe Docker host.
docker images — arată imaginile descărcate local.
docker search — căutare imagine în registry.
docker pull — descarcă imaginea din registry pe mașină.
docker build <> — construiește imaginea.
docker run — lansează containerul.
docker rm — șterge containerul.
docker logs — logurile containerului
docker start/stop/restart — lucrul cu containerul

Dacă stăpâniți aceste comenzi și le folosiți cu încredere, atunci considerați că ați stăpânit Docker la nivel de utilizator în proporție de 70%.

Dockerfile — instrucțiuni pentru crearea imaginii. Practic fiecare comandă din instrucțiune este un nou strat. Să vedem exemplul.

Ce este Docker: o scurtă introducere în istorie și principalele sale abstractizări

Așa arată un Dockerfile: pe stânga sunt comenzile, pe dreapta — argumentele. Fiecare comandă de aici (și în general scrisă în Dockerfile) creează un nou strat în Image.

Chiar și uitându-ne la partea stângă, putem înțelege ce se întâmplă. Spunem: „creează-ne un folder” — acesta este un strat. „Fă folderul de lucru” — acesta este un alt strat, și așa mai departe. Tortul stratificat simplifică viața. Dacă voi crea un alt Dockerfile și în ultima linie voi schimba ceva — voi lansa nu "python" "main.py", ci altceva, sau voi instala dependențe dintr-un alt fișier — atunci straturile anterioare vor fi reutilizate, ca un cache.

Imagine — este un pachet de containere, din imagine pornesc containere. Dacă privim Docker din perspectiva unui manager de pachete (de parcă am lucra cu pachete deb sau rpm), imaginea este practic un pachet rpm. Prin yum install putem instala aplicații, le putem șterge, le putem căuta în depozit, descărca. Aici este cam la fel: din imagine pornesc containere, acestea sunt stocate în Docker registry (analog cu yum, în depozit), iar fiecare imagine are un hash SHA-256, un nume și un tag.

Imaginea este construită conform instrucțiunii din Dockerfile. Fiecare instrucțiune din Dockerfile creează un nou strat. Straturile pot fi reutilizate.

Docker registry — este un depozit de imagini Docker. Analog cu sistemele de operare, Docker are un registru standard public — dockerhub. Dar se poate construi propriul depozit, propriul Docker registry.

Container — este ceea ce se lansează din imagine. Conform instrucțiunii din Dockerfile am construit imaginea, apoi o lansăm din această imagine. Acest container este izolat de celelalte containere, ar trebui să conțină tot ce este necesar pentru funcționarea aplicației. Totodată, un container este un singur proces. Se întâmplă să fie necesar să facem două procese, dar aceasta contravine oarecum ideologiei Docker.

Cerinta „un container — un proces” este legată de PID Namespace. Când în Namespace se lansează un proces cu PID 1, dacă acesta moare, atunci întregul container moare și el. Dacă acolo sunt lansate două procese: unul trăiește, iar al doilea a murit, containerul tot continuă să trăiască. Dar acest lucru este legat de cele mai bune practici, despre care vom discuta în alte materiale.

Pentru a studia mai detaliat caracteristicile și programul complet al cursului, puteți accesa linkul: „Curs video despre Docker».

Autor: Marcel Ibraev, administrator Kubernetes certificat, inginer practicant la Southbridge, speaker și dezvoltator de cursuri Sloerm.

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