Modulul 9 · 9.1
Înțelegerea containerelor
Modulul 9: Docker și docker compose · Timp de citire: 10 minute
Obiectivul lecției: După această lecție veți putea explica ce este un container, cum diferă de o mașină virtuală, unde rămân datele unui container și când Docker are sens pe serverul dumneavoastră.
Noțiuni cheie: image, container, strat, registry (Docker Hub), tag, digest, namespaces, cgroups, kernel partajat, sistem de fișiere volatil, volum, bind mount, publicarea porturilor, image oficial
De ce containere?
Să presupunem că doriți să rulați pe VPS-ul dumneavoastră o pagină de stare care arată dacă site-ul funcționează. Programul este scris în Node.js, are nevoie de o anumită versiune a acestuia, plus vreo două sute de biblioteci. Fără containere, instalați toate acestea pe server, alături de serverul web și de baza de date. Un an mai târziu doriți un al doilea program, care are nevoie de altă versiune de Node.js. Atunci începe bătaia de cap.
Un container rezolvă această problemă. Autorul programului livrează programul împreună cu tot ce îi trebuie, într-un singur pachet: un image. Pe server porniți acel image ca un container. Programul își vede propriile fișiere, propriul Node.js, propriile biblioteci și nu intră în conflict cu restul serverului. Nu mai aveți nevoie de el? Ștergeți containerul și image-ul, iar în /usr sau /etc nu rămâne nimic.
Docker este cel mai cunoscut instrument pentru descărcarea image-urilor și rularea containerelor. Există alternative (Podman, containerd însuși), dar noțiunile sunt aceleași peste tot.
Image, container și registry
Trei cuvinte pe care le veți întâlni mereu:
- Image: un șablon numai pentru citire, cu un sistem de fișiere (program, biblioteci, setări) și comanda care trebuie pornită. Un image este alcătuit din straturi: un strat de bază (de exemplu un Debian minimal), peste acesta un strat cu Node.js, iar peste acesta programul. Două image-uri cu același strat de bază împart acel strat pe disc.
- Container: o instanță în rulare (sau oprită) a unui image. Peste straturile numai pentru citire, containerul primește un strat de scriere propriu, subțire. Dintr-un singur image puteți porni zece containere.
- Registry: locul în care se află image-urile, cum ar fi Docker Hub (
docker.io) sau registry-ul GitHub (ghcr.io). Docker descarcă un image cudocker pull.
Numele unui image arată astfel:
louislam/uptime-kuma:2
caddy:2
vaultwarden/server:latestPartea de după cele două puncte este tag-ul: o etichetă pentru o versiune. Un tag se poate schimba: caddy:2 indică astăzi alt build decât peste trei luni, de îndată ce apare o nouă versiune 2.x. Dacă doriți să fixați exact ce image rulați, folosiți digest-ul: o amprentă a conținutului, cum ar fi caddy@sha256:.... Aceasta nu se schimbă niciodată. Tag-ul latest este doar un nume ales de autor; nu înseamnă automat "cea mai nouă versiune stabilă" și de aceea este o alegere proastă dacă doriți să știți ce rulează.
Nu este o mașină virtuală
Un container seamănă cu o mașină virtuală mică, dar nu este una. Această diferență contează pentru securitate.
| Mașină virtuală (cum este VPS-ul dumneavoastră) | Container | |
|---|---|---|
| Kernel | Kernel propriu | Folosește kernelul gazdei |
| Pornire | De la câteva secunde la câteva minute, un întreg sistem de operare | De obicei într-o secundă, doar programul |
| Separare | Prin hypervisor, la nivel hardware | Prin kernel: namespaces și cgroups |
| Dimensiune | Gigabytes | Adesea zeci până la sute de megabytes |
Un container este de fapt un proces obișnuit pe server, pe care kernelul îl plasează într-un mediu izolat:
- namespaces fac ca procesul să aibă propria perspectivă asupra fișierelor, proceselor, rețelei și utilizatorilor. În container, programul pare să fie procesul numărul 1 și nu vede restul serverului;
- cgroups limitează cât procesor și câtă memorie poate folosi.
Deoarece toate containerele folosesc același kernel, separarea este mai puțin strictă decât între două VPS-uri. O vulnerabilitate în kernel sau un container care primește prea multe drepturi poate afecta gazda. De aici regulile din acest modul: rulați doar image-uri în care aveți încredere, nu dați containerelor mai multe drepturi decât este nevoie și nu montați niciodată /var/run/docker.sock sau / într-un container (lecția 9.3 și 10.2).
Unde rămân datele dumneavoastră?
Aceasta este capcana clasică pentru începători. Stratul de scriere al unui container este volatil: dacă ștergeți containerul (de exemplu la o actualizare, lecția 9.4), tot ce a scris programul acolo dispare. De aceea, datele care trebuie păstrate se pun în afara containerului:
- Volum: un spațiu de stocare administrat de Docker însuși (în
/var/lib/docker/volumes/). Îi dați un nume, de exempluuptime-kuma, și îl legați de un director din container:-v uptime-kuma:/app/data. - Bind mount: un director obișnuit de pe server pe care îl montați în container:
-v /srv/vaultwarden:/data. Este practic, deoarece vedeți fișierele direct și le includeți ușor în backup (modulul 8).
Un container cu volume bine configurate poate fi șters și recreat fără să pierdeți nimic. Exact așa funcționează actualizarea: descărcați noul image, ștergeți containerul vechi, porniți un container nou pe aceleași volume. De aceea, includeți volumele în planul dumneavoastră de backup (lecția 8.1).
Publicarea porturilor
Un container are propria rețea. Un program web care ascultă în container pe portul 3001 nu este accesibil din afara serverului până când nu publicați acel port: -p 3001:3001 (port gazdă:port container). Atenție: fără adres, Docker publică portul pe toate adresele serverului, adică 0.0.0.0 și [::]. Asta înseamnă: accesibil din tot internetul, iar Docker face acest lucru ocolind ufw (lecția 9.3). Obiceiul sigur este publicarea pe loopback, -p 127.0.0.1:3001:3001, cu un reverse proxy cu https în față (lecția 9.4).
Cine creează image-ul?
Un image este software al altcuiva, care poate rula ca root pe serverul dumneavoastră. De aceea, verificați cine este autorul:
- Docker Official Images (cum ar fi
caddy,nginx,postgres) sunt întreținute de Docker împreună cu autorii; - image-urile autorului însuși (cum ar fi
louislam/uptime-kumașivaultwarden/server), recunoscute după numele indicat pe site-ul proiectului; - image-uri de la necunoscuți, cu nume asemănătoare: nu le folosiți.
Folosiți întotdeauna numele indicat pe site-ul oficial al proiectului, nu primul rezultat al unei căutări.
Când da, când nu?
Docker este util pentru programe pe care autorul le livrează ca image, pentru servicii pe care doriți să le puteți șterge sau muta rapid și pentru a rula unul lângă altul mai multe programe cu cerințe diferite. Pentru baza serverului (SSH, firewall, actualizări) nu folosiți containere. Iar un singur site cu nginx și PHP din depozitul de pachete (lecția 7.2) funcționează foarte bine fără Docker. Un container adaugă un strat pe care trebuie să îl înțelegeți și să îl actualizați; folosiți-l atunci când aduce un beneficiu.
Rezumat
- Un image este un șablon numai pentru citire, alcătuit din straturi; un container este o instanță în rulare, cu propriul strat de scriere.
- Containerele folosesc kernelul gazdei și sunt izolate prin namespaces și cgroups: mai ușoare decât o mașină virtuală, dar mai puțin strict separate.
- Stratul de scriere este volatil: datele permanente se pun într-un volum sau într-un bind mount, iar acestea trebuie incluse în backup.
- Un tag se poate schimba, un digest nu;
latestspune puțin. - Publicați porturile pe 127.0.0.1 și folosiți doar image-uri de la autor sau image-uri oficiale.
Test (quiz)
Exercițiu
Exercițiul O9.1: Container sau nu? Alegeți două programe pe care doriți să le rulați pe server (sau folosiți ca exemplu Uptime Kuma și un site WordPress). Pentru fiecare program, aflați dacă autorul livrează un image oficial și sub ce nume, ce tag ați alege, ce director conține datele permanente și pe ce port ascultă. Explicați apoi dacă l-ați rula într-un container sau din depozitul de pachete și de ce.
Exercițiul O9.1: Container sau nu?
Acest exercițiu îl faceți singur; nimeni nu îl verifică. Bifați ce ați făcut și marcați exercițiul ca finalizat.
Uptime Kuma: louislam/uptime-kuma cu un tag de versiune, datele în /app/data, portul 3001, publicat pe 127.0.0.1 în spatele unui reverse proxy. Alegeți un image de la autor sau un Official Image, cu un tag de versiune și un director de date cu nume.
Bifele și notițele dumneavoastră rămân doar în acest browser; nu le trimitem nicăieri. Dacă sunteți autentificat, păstrăm în Cursurile mele doar faptul că ați finalizat exercițiul.
Lecturi suplimentare
- Documentația Docker, What is a container?: https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/
- Documentația Docker, Volumes: https://docs.docker.com/engine/storage/volumes/
- Documentația Docker, Port publishing and mapping: https://docs.docker.com/engine/network/port-publishing/
- Docker Official Images: https://docs.docker.com/docker-hub/repos/manage/trusted-content/official-images/
