Virtualisation

TD1 - Virtualisation & conteneurisation

Infrastructure de conteneurisation

{chroot & cgroup & namespace & OverlayFS}

Rappel : À tout moment, pour retrouver plus de détails sur les commandes et outils Linux, référez-vous aux pages du manuel à l'aide de l'instruction : man commande.

1. chroot

La commande chroot nous laisse isoler les programmes en changeant le répertoire racine, tout en gardant l'arborescence extérieure invisible.

Étape 1 : Créer un répertoire racine avec des sous-répertoires bin, lib et lib64.

mkdir -p racine/{bin,lib,lib64}
mkdirracine

Étape 2 : On s'intéresse à utiliser les commandes bash, ls et kill. Copier les fichiers nécessaires afin de pouvoir utiliser ces commandes dans le chroot.

mylibs=$(ldd $(which ls) | cut -d' ' -f3)
            cp $mylibs racine/lib
            mylibs=$(ldd $(which kill) | cut -d' ' -f3)
            cp $mylibs racine/lib
            mylibs=$(ldd $(which bash) | cut -d' ' -f3)
            cp $mylibs racine/lib
            
bashtoracinebin killandlstoracinebin lescpsdescommandes verifdescpscommande

Étape 3 : Lancer chroot pour changer la racine vers le répertoire chroot déjà créé.

chroot racine bash
chrooteandexit

Étape 4 : Pour vérifier que l'encapsulation ne garantit pas la sécurité dans chroot, créer un processus sleep 500 dans le terminal principal, et essayer ensuite de le tuer dans votre session chroot.

☠️ Télécharger la vidéo du chroot et kill 🏴‍☠️

2. cgroup

La configuration des cgroup se fait directement par manipulation des fichiers correspondants.

2.1 Processus

On va créer un groupe de contrôle qui va limiter le nombre de processus maximal :

Étape 1 : Créer un sous-groupe de contrôle :

mkdir /sys/fs/cgroup/pidsgrp
mkdirpidsgrp

Étape 2 : Établir une limite de 5 processus :

echo 5 | tee /sys/fs/cgroup/pidsgrp/pids.max
sudotee5pidsmax

Étape 3 : Associer la ligne de commande courante au groupe de contrôle déjà créé :

echo $$ | tee /sys/fs/cgroup/pidsgrp/cgroup.procs
            more /proc/self/cgroup
teelepidsmax

Étape 4 : Essayer de lancer plus de 5 processus :

for i in $(seq 15); do sleep 100 & done
Télécharger la vidéo du bash&retry lors de la modif sur pids max

2.2 Mémoire

Contrôler l'utilisation mémoire d'un groupe s'effectue en manipulant le fichier memory.max dans le répertoire du cgroup en question :

Étape 1 : Créer un sous-groupe memgroup pour tester le contrôle de mémoire :

mkdir /sys/fs/cgroup/memgroup
mkdirmemgroup

Étape 2 : Changer la limite pour que notre nouveau cgroup ne dépasse pas 128MB d'utilisation mémoire :

echo 128M > /sys/fs/cgroup/memgroup/memory.max
memorymax128M

Étape 3 : Associer un bash au cgroup déjà créé :

echo $$ > /sys/fs/cgroup/memgroup/cgroup.procs
cgroupprocs

Étape 4 : Lancer une commande qui va consommer 256MB de mémoire et surveiller son exécution à l'aide de la commande top :

yes | tr -d '\\n' | head -c 256M | pv | grep n
yesmemory

Étape 5 : Associer un autre bash au cgroup créé, et essayer de lancer un autre processus lourd en utilisation mémoire.

Innovation: (commande pas utilisée en cours)

stress --vm 1 --vm-bytes 256M --timeout 30s

La commande stress est utilisée pour générer une charge sur le système, en particulier sur la mémoire dans ce cas.

Explication des options :

- --vm 1 : Crée une tâche qui va consommer de la mémoire (une "virtual memory worker").

- --vm-bytes 256M : Alloue 256MB de mémoire à la tâche.

- --timeout 30s : Fait tourner cette tâche pendant 30 secondes, après quoi elle s'arrête automatiquement.

Cette commande permet de tester comment le système et les cgroups réagissent face à une forte demande de mémoire sur une période limitée, en consommant 256MB pendant 30 secondes.

Étape 6 : Quelles observations pourrait-on extraire en surveillant l'utilisation mémoire des deux processus ?

☠️ Télécharger la vidéo où le pc est en monde memory reboot letopaveclesdeuxprocess

Étape 6 : Quelles observations pourrait-on extraire en surveillant l'utilisation mémoire des deux processus ?

En surveillant l'utilisation mémoire des deux processus à l'aide de top, voici les observations que l'on peut faire :

1. Respect de la limite mémoire : Les deux processus sont contraints par la limite de 128MB imposée par le cgroup. Même si un processus tente de consommer plus de mémoire, il sera restreint à la limite définie.

2. Répartition de la mémoire : Chaque processus consomme une partie de la mémoire disponible. Par exemple, le premier processus peut consommer environ 100MB tandis que le second utilise environ 25MB. Cela montre que la mémoire est bien partagée entre les processus.

3. Impact des dépassements de mémoire : Si un processus tente de consommer plus que la mémoire disponible dans le cgroup, il pourrait être stoppé ou rencontrer des erreurs. Cela peut provoquer des arrêts ou des ralentissements pour les autres processus dans le même cgroup.

4. Gestion efficace de la mémoire : Le cgroup s'assure que la limite mémoire est respectée, empêchant un processus de monopoliser les ressources. Cela permet de garantir une utilisation équilibrée de la mémoire entre les différents processus.

5. Ralentissements possibles : Si la limite est atteinte, les processus peuvent être ralentis, et tu pourrais observer une augmentation de l'utilisation du swap si le système tente de compenser la restriction mémoire.

En résumé, les processus sont bien limités à 128MB et la mémoire est partagée entre eux. Toute tentative de dépassement entraînerait un comportement inattendu, et la gestion de la mémoire est bien assurée par le cgroup.

2.3 Processeur

Contrôler l'utilisation CPU d'un groupe s'effectue en manipulant le fichier cpu.max dans le répertoire du cgroup en question :

Étape 1 : Créer un sous-groupe cpugroup pour tester le contrôle de l'utilisation du processeur :

mkdir /sys/fs/cgroup/cpugroup
mkdircpugroup

Étape 2 : Changer la limite pour que notre nouveau cgroup ne dépasse pas 10% d'utilisation processeur :

echo 10000 > /sys/fs/cgroup/cpugroup/cpu.max
erreurecriturecpumax

Étape 3 : Associer un bash au cgroup déjà créé :

echo $$ > /sys/fs/cgroup/cpugroup/cgroup.procs

Étape 4 : Lancer une commande qui va consommer le processeur et surveiller son exécution à l'aide de la commande top :

yes | pv
☠️ Télécharger la vidéo pour voir un processus à 10% max d'utilisation CPU

Étape 5 : Associer un autre bash au cgroup créé, et essayer de lancer un autre processus lourd en utilisation processeur.

☠️ Télécharger la vidéo pour voir un processus à 10% max d'utilisation CPU

Étape 6 : Quelles observations pourrait-on extraire en surveillant l'utilisation processeur des deux processus ?

Étape 7 : Tuer tous les processus appartenant aux cgroups créés :

echo 1 > /sys/fs/cgroup/memgroup/cgroup.kill
            echo 1 > /sys/fs/cgroup/cpugroup/cgroup.kill
☠️ Télécharger la vidéo pour voir les deux processus killés ☠️

2.4 Entrée/Sortie

Étape 1 : Créer un nouveau cgroup pour limiter le débit de lecture et d'écriture sur un matériel donné :

mkdir /sys/fs/cgroup/iogroup

Étape 2 : Associer le terminal courant à ce cgroup :

echo $$ > /sys/fs/cgroup/iogroup/cgroup.procs

Étape 3 : Assurer que le I/O est activé pour le cgroup :

echo '+io' > /sys/fs/cgroup/iogroup/cgroup.subtree_control

Étape 4 : Retrouver le numéro majeur et mineur du disque dur :

more /proc/devices # ou ls -l /dev/nvme

Étape 5 : Limiter la vitesse de lecture à 1000KBps et 2000KBps pour écriture :

echo '259:0 rbps=1024000 wbps=2048000' > /sys/fs/cgroup/iogroup/io.max

Étape 6 : Tester cette configuration :

yes | pv > test

3. Namespace

Pour lancer une commande dans un nouvel espace de nom, on utilise la commande unshare. Dans la suite, on va apprendre son utilisation dans les exemples.

3.1 UTS

Cet espace de nom nous permet de redéfinir le nom d’hôte dans le sous-espace.

Étape 1 : Vérifier le nom d’hôte dans notre système :

hostname

Étape 2 : Créer un espace de nom UTS :

unshare -ur bash

Étape 3 : Changer le nom d’hôte dans l’espace de nom. Vérifier ensuite que le changement a eu lieu :

hostname nouveau_nom
            hostname

Étape 4 : Dans un shell du système, vérifier que le nom d’hôte n’a pas changé :

hostname

3.2 PID

Pour créer un nouvel espace de nom pour les identifiants des processus, il suffit d’utiliser la commande suivante :

Étape 1 : Création de deux namespaces réseau :

unshare -pf --mount-proc=/proc -r bash

Ou bien, si on souhaite effectuer le montage à l’intérieur :

unshare -pf -m -r bash
            mount -t proc proc /proc

Étape 2 : Lancer des processus à l’intérieur de notre nouvel espace de nom et vérifier ensuite les processus visibles :

3.3 Réseau

Dans cette partie, on va créer deux espaces de nom réseau, et on va les connecter par un réseau virtuel comme l'indique le schéma de la figure 1 :

Schéma de connexion entre espaceA et espaceB via vethA et vethB

Fig. 1 : Construction d'un réseau virtuel entre 2 espaces de noms.

Étape 1 : Création de deux namespaces réseau :

ip netns add espaceA
            ip netns add espaceB

Étape 2 : Création d’une paire d’interfaces ethernet virtuel (veth) qu’on va utiliser pour connecter nos namespaces réseau. Et afficher ensuite la liste des interfaces disponibles :

ip link add vethA type veth peer name vethB
            ip link list

Étape 3 : Affecter chacune des interfaces virtuelles créées aux namespaces correspondants :

ip link set vethA netns espaceA # ou bien fichier /proc/PID/ns/net
            ip link set vethB netns espaceB

Pour consulter les interfaces réseaux depuis notre namespace espaceA, on utilise ip netns exec de la façon suivante :

ip netns exec espaceA ip link

Étape 4 : On va affecter des adresses IP aux interfaces veth à l'intérieur de chaque namespace en lançant les commandes suivantes :

ip netns exec espaceA ip addr add 192.168.0.1/24 dev vethA
            ip netns exec espaceB ip addr add 192.168.0.2/24 dev vethB

Étape 5 : On va activer maintenant les interfaces virtuelles à l'intérieur de leurs espaces de nom :

ip netns exec espaceA ip link set vethA up
            ip netns exec espaceB ip link set vethB up

Étape 6 : On ajoute ensuite les routes par défaut pour chaque espace de nom. Cela permet au système de savoir comment acheminer les paquets à n'importe quelle adresse IP :

ip netns exec espaceA ip route add default via 192.168.0.1 dev vethA
            ip netns exec espaceB ip route add default via 192.168.0.2 dev vethB

Étape 7 : Enfin, on peut vérifier le bon fonctionnement en "pingant" à l'intérieur de espaceA :

ip netns exec espaceA ping 192.168.0.2

4. OverlayFS

Monter notre système de fichiers sur plusieurs couches afin de pouvoir réutiliser des informations précédentes. Les changements des couches plus hautes ne vont pas modifier les couches plus basses.

Étape 1 : Créer des répertoires représentant, par ordre, le répertoire contenant respectivement les informations de la couche inférieure, celles de la couche supérieure, le travail effectué et les informations manipulées par l'utilisateur directement :

mkdir -p overlay/{lower,upper,work,merged}

Note : Considérez à remplir lower par quelques fichiers pour pouvoir les manipuler dans les étapes suivantes.

Étape 2 : Ensuite, on doit monter le système de fichiers de type overlay en utilisant les répertoires déjà créés :

mount overlay -t overlay -o lowerdir=overlay/lower,\
            upperdir=overlay/upper,workdir=overlay/work overlay/merged

Étape 3 : Effectuer des manipulations sur des fichiers dans le répertoire merged, et consulter les changements dans les répertoires originaux.