Conteneurisation
Introduction
Un conteneur (container) est une forme légère de virtualisation où l'on encapsule uniquement ce qui est nécessaire à l'application. Le conteneur est exécuté sur le système hôte.
Il n'y a pas de système d'exploitation à proprement dit dans un conteneur. Uniquement les binaires et les bibliothèques (libraries) nécessaires.
Au départ, des outils linux
chroot
(1960) pour change root qui change la racine du filesystem pour un processus.- BSD jail (2000) amélioration du
chroot
en séparant également les processus, les utilisateurs et le réseau. Les processus se trouvent dans une sandbox (bac à sable). - namespace (2002) est une fonctionnalisé du noyau (kernel) qui séparent les ressources de manière telle qu'un ensemble de processus voient un ensemble de ressources tandis qu'un autre ensemble de processus voit un autre ensemble de ressource. Les namespaces permettent une isolation des processus.
- cgroups (2007) pour control groups limitent les ressources (CPU, mémoire, IO, réseau…) disponibles pour un processus. Les cgroups ont été initiés par Google (2006) avant d'être intégré au noyau linux >2.6.24
- LXC (2008) pour linux containers combinent les cgroups et les namespaces… et souffrent de problème de sécurité.
Quelques projets (VServer, OpenVZ, Warden, LMCTFY let my contain that for you) ont vécus et ont été vaincus par Docker.
Docker (2013) est l'héritier direct de LXC puisqu'il utilisait LXC et l'a ensuite remplacé par libcontainer
.
Pourquoi Docker et pas un autre ?
Quelques fonctionnalités de Docker :
- un démon Docker CLI avec une API et un modèle client-serveur;
- un fichier Dockerfile qui est un fichier texte définissant le conteneur à partir d'une image de base à laquelle on ajoute des fonctionnalités;
- un dépôt d'images Docker Registry pour partager ses images;
- un fichier Docker-compose définissant une application multi-conteneurs en un fichier YAML.
Un (pré)-conteneur à l'ancienne
Un conteneur se base sur les namespaces et les cgroups disponibles dans un noyau linux. À partir de ces deux technologies, il doit être possible de créer un conteneur.
namespace
~:# unshare --fork --pid --mount-proc bash
~:# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.2 0.0 8208 4944 pts/21 S 14:10 0:00 bash
root 9 0.0 0.0 11256 4760 pts/21 R+ 14:10 0:00 ps aux
Crée un namespace tel que :
- le process id (PID) devient le PID 1 sans autre processus;
- le processus peut utiliser n'importe quel port sans entrer en conflit avec d'autres ports utilisés;
- peut monter (et démonter) des systèmes de fichiers sans affecter ceux de l'hôte;
cgroup
~:# cgcreate -a user -g memory:my-group
~:# ls -l /sys/fs/cgroup/my-group
~:$ echo 10000000 > /sys/fs/cgroup/my-group/memory.max
~:# cgexec -g memory:my-group bash
Crée un cgroup et le limite à 10MB. Lancer un processus demandant un peu trop de mémoire sera refusé.
Par exemple, un simple apt update
prend un « temps anormalement long ».
REMARQUE
Nécessite l'installation du paquet cgroup-tools
.
The twelve factor app
À l'origine de la conteneurisation une méthodologie proposée par Adam Wiggins de la société Heroku pour proposer du software as a service (saas).
Le code de base (codebase) : Une version du code pour une (ou plusieurs) versions du code déployé. Quand le code change, un nouveau déploiement peut être réalisé facilement.
Dépendances (dependencies) : Les dépendances sont explicitement déclarées.
Par exemple :
dockerfileFROM debian RUN apt update && \ apt install -y cosway && \\ apt clean CMD ["/usr/games/cowsthink", "-f tux", "Who am I"]
Configuration : Les configurations doivent être stockées et il sera possible de stocker des valeurs dans ces variables et de les passer à l'environnement lors de l'exécution du conteneur.
Services externes (backing services : Les services externes sont traités comme des ressources attachées.
Build, release and run (build, release, run) : L'image construite est clairement séparée de son exécution.
Processus (processes)_: les applications sont sans états (stateless). La persistence est assurée par des services backend.
stateless versus statefull
Un processus statefull va garder un status en mémoire tandis qu'un service stateless ne conserve rien ; il peut être supprimé et recrée sans qu'aucune information ne soit perdue.
Association de ports (port binding) ou encore port mapping permet de lier un port de l'hôte à un port du conteneur.
Concurrence (concurrency) : la mise à l'échelle (scale) ou la montée en charge est aisée puisque un conteneur est stateless. Placés derrière un load balancer une application peut avoir un ou plusieurs conteneurs (qui peuvent être lancés à la volée)
Jetable (disposability) : Maximize robustness with fast startup and graceful shutdown. Les conteneurs peuvent être démarrés à la demande et supprimés tout aussi aisément.
Parité dev/prod (dev/prod parity)_ : _Keep development, staging, and production as similar as possible. Un conteneur est l'exécution d'une image. Cette image ne change pas.
Logs (logs) : Les logs sont des flux d'évènements qui peuvent être lus à l'extérieur du conteneur.
Processus d'administration (admin processus) : Run admin/management tasks as one-off processes. Ces tâches doivent être lancées à l'extérieur du conteneur.
Conteneur versus machine virtuelle
Si l'on compare une machine virtuelle à une maison qui a une infrastructure propre pour l'eau, l'électricité… et un ensemble de pièces qui la constituent. Que l'on veuille une grande, très grande ou toute petite maison, on a toutes les pièces de la maison. On peut comparer un conteneur à une seule pièce de la maison — dont on peut faire ce que l'on veut : une chambre, un petit bureau ou encore un grand loft — qui peut bénéficier des ressources de la maison comme l'eau, l'électricité…
Comme un nouvelle instance d'un conteneur n'a pas besoin d'un nouveau système d'exploitation, un conteneur génère moins d'overhead (mémoire, CPU, I/O.)
INFO
Un conteneur porte ce nom par analogie aux conteneurs maritimes qui peuvent contenir des choses fort différentes mais qui ont tous le même format leur permettant d'être acheminer sur n'importe quel moyen de transport.
Fini le : Chez moi, ça marche !
L'idée de la conteneurisation est de ne prendre que les binaires et les bibliothèques (libraries) nécessaires à l'application et des les transporter dans une enveloppe — le conteneur — qu'en container runtime (comme Docker) pourra exécuter.
REMARQUE
Un container runtime permet :
- l'exécution du conteneur (run);
- la création d'une image (build);
- la gestion des ces images (images, rm);
- la gestion des différentes instances de conteneurs (ps, rm);
- le partage d'images (push, pull).
Le container runtime a un format propre.
Les conteneurs se basent sur les namespaces et les cgroups : le namespace définit ce que le processus peut voir tandis que les cgroups, ce à quoi il a droit (CPU, RAM, I/O, etc.).
Conteneur et image
Un conteneur n'est pas une image et une image n'est pas un conteneur.
L'image est au programme ce que le conteneur est au processus : une image est un ensemble de fichiers tandis qu'un conteneur est une exécution d'une image.
Une fois que l'on dispose d'une image on peut :
- l'exécuter,
- la partager.
Pour la partager, on utilise un registry comme dockerhub et le partage d'une image est aussi simple qu'un push
et un pull
.
REMARQUE
Il également techniquement possible d'en faire un tar
et de le partager mais ce n'est pas l'habitude.
Par rapport à une machine virtuelle, un conteneur change la manière de faire :
- là où les services (
http
par exemple) tournaient en arrière plan, ils restent en avant plan dans un conteneur; - les logs quant-à eux sont simplement envoyés, tous, sur
stdout
.
Cas particulier des conteneurs LXC
REMARQUE
Sur proxmox, les conteneurs ne sont pas gérés par docker mais sont de simples conteneurs LXC. Ils sont plus vus comme des « conteneurs systèmes » (system containers) plutôt que comme des « conteneurs applicatifs » (app containers).
Avant de lancer un conteneur, il est nécessaire de télécharger l'un ou l'autre template. Ça se fait via l'interface web
Il sera alors possible de créer un conteneur et de le lancer.
Par défaut les conteneurs sont basiques et proposent principalement des distributions linux. Pour avoir des conteneurs plus complets, on peut utiliser le projet Turnkey Linux.
Pour accéder aux images du projet, mettre à jour la base de templates des conteneurs via pveam update
. Ensuite, les nouveaux templates devraient apparaitre.
Créer un template custom
Il est également possible de créer un template LXC personnalisé sous Proxmox. Il sera sous la forme d'un tgz partageable.
En quelques étapes :
créer et personnaliser le conteneur
- déployer un conteneur LXC basé sur une image existante (par exemple, Alpine ou Debian).
- s'y connecter et installer le ou les services visées.
ASTUCE
Pour le test, installer par exemple l'application
cowsay
et ajouter un script dans/etc/profile.d/cowsay.sh
qui contientbashif command -v cowsay >/dev/null 2>&1; then cowsay "wouf" fi
éteindre le conteneur (via l'interface web ou
pct shutdown <ID_CT>
)créer un template exportable
- supprimer l'interface réseau du conteneur via l'interface web
- générer une archive du conteneur avec
vzdump
:bashvzdump <ID_CT> --dumpdir /var/lib/vz/template/cache --compress gzip --mode stop
- renommer l'archive pour qu'elle soit facilement identifiable :bash
mv /var/lib/vz/template/cache/vzdump-lxc-<ID>.tar.gz /var/lib/vz/template/cache/my-beautiful-name_1.0.tar.gz
déployer un conteneur depuis ce template de la manière habituelle
Le template est maintenant disponible et peut être exporté.
Docker work arround
Si l'on veut impérativement utiliser docker (principalement pour sa base d'images et la facilité de ses dockerfiles), il est possible de
- installer un conteneur alpine par exemple, avec les options nesting et keyctl;
- installer docker et l'image désirée
ATTENTION
Ne pas installer docker sur l'hyperviseurs « en-dessous » de proxmox.
— Pourquoi ?
— Si j'utilise Proxmox pour la virtualisation, j'évite de mettre d'autres services car ça peut être un faille de sécurité. Proxmox va permettre le migration de conteneurs que je n'aurai pas sans Proxmox ainsi que la haute disponibilité (HA).
À LIRE AUSSI