Poques tecnologies han canviat tant la manera de desenvolupar i desplegar programari l’última dècada com ho han fet els contenidors. Parlem de Docker, de Kubernetes, d’imatges que arrenquen en mil·lisegons i que es mouen igual entre el portàtil d’un desenvolupador i un clúster en producció. Darrere de tota aquesta màgia no hi ha cap truc propietari: hi ha un grapat de funcions del kernel Linux que feia anys que maduraven abans que ningú pronunciés la paraula “contenidor”. Entendre aquestes peces és entendre per què els contenidors són tan lleugers i per què, en el fons, depenen tant de Linux.
Un contenidor no és una màquina virtual
La confusió més estesa és imaginar un contenidor com una màquina virtual en miniatura. No ho és, i la diferència importa molt. Una màquina virtual emula maquinari complet i executa el seu propi sistema operatiu, amb el seu propi kernel, a sobre d’un hipervisor. Això vol dir gigabytes de disc, minuts d’arrencada i un consum de memòria notable només per existir.
Un contenidor, en canvi, comparteix el kernel de l’amfitrió. No arrenca cap segon sistema operatiu: són processos normals de l’amfitrió que el kernel manté aïllats els uns dels altres perquè cadascun cregui que està sol a la màquina. Per això un contenidor arrenca en una fracció de segon i pesa uns pocs megabytes en lloc de diversos gigues. La contrapartida és clara: tots els contenidors d’una màquina executen el mateix kernel, el de l’amfitrió. No pots córrer un kernel diferent dins d’un contenidor.
Namespaces: la il·lusió de l’aïllament
La primera peça clau són els namespaces. Un namespace embolcalla un recurs global del sistema en una abstracció que fa creure als processos del seu interior que tenen la seva pròpia instància aïllada d’aquest recurs. Linux n’ofereix de diversos tipus: PID (els processos del contenidor tenen la seva pròpia numeració i no veuen els de l’amfitrió), Network (cada contenidor pot tenir la seva pròpia pila de xarxa, interfícies i IP), Mount (la seva pròpia vista del sistema de fitxers), UTS (el seu propi hostname), IPC, User i Cgroup.
Gràcies als namespaces, el procés que corre dins d’un contenidor “veu” un sistema que sembla només seu: el seu propi PID 1, la seva pròpia xarxa, els seus propis punts de muntatge. El kernel construeix la il·lusió amb cura, però des de dins resulta indistingible d’una màquina dedicada.
Cgroups: posar límits als recursos
Aïllar no n’hi ha prou. Cal evitar que un contenidor es mengi tota la CPU o tota la memòria i deixi la resta sense recursos. D’això se n’encarreguen els control groups, o cgroups. Aquesta funcionalitat va néixer a Google els anys 2006-2007 amb el nom de “process containers” i es va integrar al kernel Linux amb la versió 2.6.24, el 2008.
Els cgroups permeten mesurar i limitar quanta CPU, memòria, amplada de banda d’E/S o nombre de processos pot consumir un grup de processos. Si llances un contenidor amb un límit de 512 MB de RAM, són els cgroups els qui fan complir aquesta frontera a nivell de kernel. La versió 2 de cgroups, ja consolidada a les distribucions modernes, va unificar la jerarquia i va millorar força la gestió de memòria i d’E/S.
Capabilities i sistemes de fitxers de capes
Queden dos ingredients que completen el quadre. Les capabilities divideixen els antics privilegis totpoderosos de root en permisos petits i independents. En lloc de donar a un contenidor poder absolut, li concedeixes només allò que necessita (per exemple, obrir un port baix) i li retires la resta, així redueixes molt el dany possible si alguna cosa es veu compromesa.
L’altra peça són els sistemes de fitxers d’unió (union filesystems), com OverlayFS. Permeten apilar capes de només lectura i afegir-hi a sobre una capa d’escriptura. Així funcionen les imatges: una capa base, capes amb dependències, la teva aplicació a sobre, i tot compartit entre contenidors sense duplicar dades al disc. Això explica per què una imatge d’Alpine Linux ocupa tot just uns megabytes i per què desplegar la còpia número cent d’una imatge no consumeix cent vegades l’espai.
De LXC a Docker, runc i l’estàndard OCI
Tota aquesta tecnologia existia dispersa i costava d’usar. Projectes com LXC ja combinaven namespaces i cgroups per crear entorns aïllats, però la corba d’aprenentatge era pronunciada. El 2013 va aparèixer Docker, construït al principi sobre LXC, i va canviar les regles amb dues idees genials: empaquetar-ho tot en imatges portables i oferir una experiència d’ús senzilla. De cop, “funciona a la meva màquina” va deixar de ser una excusa.
Docker va substituir després LXC per la seva pròpia llibreria, libcontainer, per parlar directament amb el kernel. I el juny de 2015 va néixer l’Open Container Initiative (OCI), impulsada per Docker, CoreOS i d’altres, per estandarditzar el format. Docker va donar runc, el runtime de referència que compleix l’especificació OCI i que avui executa contenidors per sota de Docker, containerd i bona part de l’ecosistema de Kubernetes.
Per què tot això importa
Quan llances un contenidor sobre Ubuntu, Debian o qualsevol distribució, no fas servir cap caixa negra: estàs orquestrant namespaces, cgroups, capabilities i OverlayFS, tots del kernel de l’amfitrió. Per això els contenidors són natius de Linux i per això, a macOS o Windows, Docker en realitat arrenca una petita màquina virtual Linux per sota: necessita aquest kernel per funcionar.
Comprendre aquests fonaments no és cap exercici acadèmic. T’ajuda a depurar problemes de xarxa o de permisos, a captar els límits de seguretat reals d’un contenidor i a prendre millors decisions de desplegament. Els contenidors no van inventar res de nou: van posar una interfície elegant a sobre de dues dècades d’evolució del kernel Linux. I aquest, precisament, és el seu major assoliment.
