Labs : des conteneurs à partir d’un fichier compose

Certaines leçons demandent plus qu’un simple fichier : une base de données à interroger, une API devant elle, un cache à côté. Un lab réunit ces services, décrits dans un fichier compose rédigé sur la page du lab ou conservé dans votre propre dépôt GitHub.

Configurez-le, vérifiez ce que recevra chaque élève, publiez-le et testez votre propre copie, puis distribuez-le comme n’importe quelle tâche. Chaque élève démarre sa copie depuis la page du lab ; elle est conservée d’une session à l’autre, et il la rend ensuite pour que vous la notiez.

Pour en essayer un vous-même, sans module, ouvrez « Créer » dans la barre et choisissez « Lancer un conteneur ». Les visiteurs y ont accès aussi : sans compte, n’importe qui peut lancer le lab Git ou le lab SQL, et tous les autres exemples demandent un compte gratuit. Une carte « Laboratoires d’exemple » en propose six, une base de données avec une API web, MySQL avec les données d’une boutique et phpMyAdmin devant, Git avec Gitea et les labs de sécurité Juice Shop, DVWA et WebGoat, chacun avec sa propre page dans ce manuel et avec un « Démarrer » qui le lance en un clic, ou « Modifier le fichier d’abord » pour le changer avant de le lancer. Sur cette page, « Démarrer » exécute ce qui est à l’écran en conservant les volumes, l’ouvrir n’écrit rien, le fichier s’enregistre une seconde après que vous avez cessé de taper pour un compte et n’est que vérifié pour un visiteur, et « Annuler » reprend un exemple que vous avez chargé.

Un lab peut aussi être une VM Ubuntu : choisissez « VM Ubuntu » comme type de lab, et chaque élève reçoit sa propre machine, préparée par un script que vous écrivez. Voir « Configurer un lab VM » plus bas.

Configurer un lab

Sous « Lab », choisissez la carte « Lab de conteneurs » pour ajouter une tâche. Un nouveau lab démarre en mode « Écrit ici », avec un exemple déjà en place : une base de données et, devant elle, une API web que le terminal peut interroger avec psql et curl. Publiez-le tel quel pour voir un lab en fonctionnement, ou modifiez-le d’abord. Le fichier s’enregistre une seconde après votre dernière frappe, et il est vérifié à chaque enregistrement. Un fichier écrit ici ne peut utiliser que des images existantes, puisqu’il n’y a pas de dépôt à partir duquel construire une image ou lire un env_file.

Pour un lab plus conséquent, choisissez « Dans un dépôt GitHub » : l’historique du lab est conservé, et il fonctionne de la même façon avec docker compose sur votre propre machine. « Utiliser l’exemple » fait pointer le lab vers mitsurutech/lab-sample, dont le README constitue une leçon d’accompagnement ; créez-en un fork pour l’adapter. Un second, mitsurutech/lab-mysql-phpmyadmin, propose MySQL avec une base de données d’apprentissage et phpMyAdmin, avec des exercices SQL dans son README ; indiquez son adresse pour l’utiliser. Un troisième, mitsurutech/lab-git-gitea, est un serveur Git, Gitea, avec un dépôt à cloner, où faire un commit et un push depuis le terminal, qui dispose de git. Sinon, indiquez l’adresse du dépôt, la branche et le chemin du fichier compose depuis la racine du dépôt. Pour un dépôt privé, cochez « Dépôt privé » et collez un jeton d’accès personnel à granularité fine, limité à la lecture seule sur ce dépôt. Il est stocké chiffré et n’est plus jamais affiché.

Le formulaire s’enregistre automatiquement. À chaque enregistrement, le fichier est relu au dernier commit de la branche, et la page montre ce que recevra chaque élève : chaque service, son image d’origine, sa mémoire, ses ports et les volumes conservés.

La carte Laboratoires d’exemple de la page Conteneur, qui liste chaque lab prêt à l’emploi avec un bouton Démarrer, un bouton Modifier le fichier d’abord et un lien vers son code sur GitHub
La carte des labs d’exemple : chaque lab démarre en un clic, sans rien à configurer ; les labs Git et SQL ne demandent aucun compte, les autres un compte gratuit.

Ce qu’un fichier compose peut demander

Un lab tourne sur des machines partagées par toute la classe : un fichier ne peut donc demander que ce qu’on peut accorder sans risque à trente élèves à la fois. Les images proviennent de Docker Hub ou du registre de GitHub (ghcr.io), ou sont construites à partir d’un dossier d’un dépôt public : la publication les construit d’abord sur GitHub, et l’image obtenue est publique ; gardez donc les mots de passe dans un env_file plutôt que dans le Dockerfile. Un lab peut compter jusqu’à 6 services et 2048 Mo de mémoire par copie d’élève ; un service sans limite propre compte pour 512 Mo.

Tout ce qui toucherait à la machine elle-même est refusé : conteneurs privilégiés, dossiers de la machine montés dans un conteneur, socket Docker, réseau de l’hôte, ports publiés sur la machine, et valeurs tirées de son environnement avec ${...}. Si un fichier en contient, chaque ligne fautive est signalée : tous les problèmes apparaissent d’un coup, chacun avec son numéro de ligne, pour que vous puissiez tout corriger en un seul push. Un signe dollar que le conteneur doit voir s’écrit $$, comme avec docker compose, si bien qu’une commande peut toujours écrire $$HOME ou "$$@".

Tester votre propre copie

Une fois un commit publié, appuyez sur « Démarrer » sur la page du lab. Vos services démarrent dans l’ordre fixé par le depends_on du fichier, chacun accessible aux autres par son nom de service, avec un terminal à côté : « Ouvrir le terminal » une fois le lab lancé. « Arrêter et enregistrer » conserve chaque volume nommé ainsi que le dossier personnel du terminal, et « Redémarrer » les restaure.

Dans le terminal, la commande docker agit sur les services du lab, désignés par leur nom dans le fichier : docker ps pour les lister et vérifier leur état, docker logs api pour lire ce que l’un d’eux a affiché, docker exec -it db sh pour ouvrir un shell dans l’un d’eux, docker restart, stop et start, docker inspect, top, stats et cp. Rien ne peut être créé, téléchargé, construit ou supprimé, et seuls les services de cette copie sont accessibles. Comme sur un vrai serveur, impossible d’entrer dans une image qui ne contient pas de shell.

Pour ouvrir un service dans le navigateur, ajoutez-lui une étiquette dans le fichier, lab.open: "8080", pour un port déclaré sous ports ou expose. Pendant que la copie tourne, sa carte affiche un lien vers chaque port ouvert, à une URL qui lui est propre. Seuls l’élève propriétaire de la copie et le personnel du module peuvent l’ouvrir. Pour appeler une API depuis Postman, curl ou un programme, envoyez le jeton de la copie, affiché sur la carte, dans l’en-tête X-Lab-Token ; « Nouveau jeton » le remplace s’il a fuité. Les ports ne transportent que du trafic web : un client de base de données installé sur votre ordinateur ne peut pas s’y connecter ; utilisez pour cela le terminal du lab. Si vous ouvrez un port qui sert d’ordinaire à une base de données ou à un autre service non web (5432, 3306, 6379, 27017, etc.), la page le signale et indique où le joindre à la place ; c’est un avertissement, jamais un refus.

Les volumes et le dossier personnel enregistrés peuvent occuper 200 Mo au total. La page vous prévient quand une copie approche de cette limite, et si une copie la dépasse au moment de l’arrêt, c’est l’enregistrement précédent qui est conservé.

Distribuer le lab

Cochez le lab dans l’onglet « Tâches » du module et appuyez sur « Distribuer », comme pour toute tâche : les élèves reçoivent la notification et l’e-mail habituels. Leur lien ouvre la page du lab, avec vos consignes, un bouton « Démarrer » pour lancer leur propre copie et le fichier compose qu’elle exécute.

Chaque élève reste sur le commit à partir duquel il a démarré. Quand vous publiez un correctif, la page du lab indique combien de copies ne sont pas à jour, et « Mettre tout le monde à jour » les met à niveau au prochain démarrage de chacun, en conservant ce que les élèves ont enregistré.

Une leçon travaillée dans un lab

Une leçon peut s’appuyer sur un lab, un lab de conteneurs ou une VM Ubuntu, pour que la classe lise et tape au même endroit. Dans l’éditeur de la leçon, choisissez le lab sous « Lab », en haut ; seuls les labs du module sont proposés. Plusieurs leçons peuvent partager un lab, par exemple Git partie 1 et Git partie 2 : chacun garde alors une seule copie et un seul terminal pour toutes.

Ajoutez un bloc Commande pour chaque étape : une commande shell de 20 lignes au plus. Dans la leçon, chaque lecteur voit en haut la barre du lab : Démarrer et Arrêter, un lien vers chaque port ouvert et « Ouvrir le terminal », qui ouvre le terminal complet dans un nouvel onglet. « Exécuter » sur un bloc Commande saisit la commande dans son propre terminal, qui en garde donc l’historique, et ce qu’elle a affiché apparaît sous le bloc. Une commande encore en cours après quelques secondes, qui attend un mot de passe par exemple, se poursuit dans le terminal. Le bouton n’est actif qu’une fois le lab démarré, et il ne démarre jamais le lab lui-même. Le terminal d’un lab VM est la machine Ubuntu : Exécuter tape donc dans la machine une fois qu’elle a démarré et que son script de préparation est terminé ; avant, il dit que la machine démarre encore et ne tape rien.

Le personnel a aussi sa propre copie : une commande lancée au vidéoprojecteur s’exécute dans la vôtre. Un élève dont le travail de lab est rendu ou noté ne peut pas non plus le démarrer depuis la leçon. Un bloc Commande dans une leçon sans lab est signalé dans la liste de ce qui n’est pas prêt, dans l’éditeur.

Suivre les élèves et prendre la main

La page du lab affiche la liste de la classe : qui a démarré une copie, qui a son terminal ouvert en ce moment, et deux boutons par élève. « Regarder » ouvre son terminal en lecture seule : vous voyez ce qu’il tape en temps réel. « Prendre la main » vous permet d’y écrire : la page de l’élève lui indique que son enseignant écrit dans son terminal, son journal d’accès l’enregistre, et « Rendre la main » y met fin. La première fois que vous utilisez l’un ou l’autre, un code vous est envoyé par e-mail, comme pour tout terminal.

Si un élève est absent, « Ouvrir la copie enregistrée » démarre ce qu’il a enregistré en dernier, dans votre propre session et avec son propre terminal. Rien de ce que vous y modifiez ne se répercute sur sa copie. Elle est refusée tant que la copie de cet élève tourne : regardez-la plutôt.

Rendu et correction

L’élève arrête son lab et appuie sur « Rendre ». Ce que sa copie a enregistré est figé à cet instant, et la copie est verrouillée : il ne peut plus la démarrer tant que vous ne la lui avez pas renvoyée. Un lab en cours d’exécution ne peut pas être rendu : vous notez donc exactement ce qui a été enregistré.

Sur la page du lab, la liste de la classe indique qui a rendu son travail et qui a été noté, avec un bouton « Noter » à côté de chaque élève. Il ouvre le lab tel que l’élève l’a laissé : la date du rendu, ce qu’il a modifié sur une VM, la conversation avec lui, ainsi que la note et le commentaire. Depuis la liste de la classe, « Ouvrir la copie enregistrée » démarre ce qu’il a rendu, sans aucune modification, dans votre propre session.

Une fois la note attribuée, l’élève est prévenu et le lab reste verrouillé. « Renvoyer » le rouvre, avec un motif si vous en indiquez un, que l’élève lit dans la notification et dans la conversation. Son rendu reste en place jusqu’à ce qu’il rende de nouveau son travail.

Publier une version

« Publier cette version » fige le commit que vous venez de vérifier. Les copies des élèves démarrent à partir de ce commit, et pousser sur la branche ne change rien pour eux tant que vous ne publiez pas à nouveau.

Seuls les volumes nommés et le dossier personnel du terminal sont conservés d’une session à l’autre pour chaque élève. Toute autre modification faite dans un conteneur disparaît à l’arrêt du lab, comme pour des conteneurs en production. Une nouvelle image ne modifie pas les données déjà présentes dans un volume : les données initiales et les scripts de démarrage ne s’exécutent que sur un volume vide.

Chaque service s’exécute sous l’utilisateur défini par son image, comme avec docker compose sur votre machine, sauf si le fichier en indique un autre. Une copie dont les conteneurs écrivent plus de 2 Go hors des volumes est arrêtée, volumes enregistrés, pour qu’un journal qui s’emballe ne puisse pas remplir le disque partagé par la classe.

Configurer un lab VM

Sous « Lab », choisissez la carte « Lab VM Ubuntu » pour ajouter une tâche. Au lieu d’un fichier compose, la page contient une courte description de la machine : cpus, memory en Mo, disk en Go, open pour les ports accessibles depuis le navigateur, et setup, un script exécuté en root au premier démarrage de la VM de chaque élève. L’exemple de départ installe un serveur web : publiez, appuyez sur « Démarrer », et vous le voyez tourner aussitôt.

La VM de chaque élève est conservée d’une session à l’autre, avec tout le contenu de son disque, et vous pouvez regarder le terminal d’un élève ou en prendre la main comme dans n’importe quel lab. Le script setup s’exécute une seule fois par élève, et sa sortie se trouve dans /var/log/mitsurutech-setup.log sur sa VM. La VM a accès aux paquets Ubuntu, à PyPI, à npm et à GitHub, et à rien d’autre.

Pour noter une VM rendue sans la lire vous-même, ajoutez check, un script de la même forme que setup. Lancer la vérification sur la ligne d’un élève dans la liste de la classe démarre une copie de sa machine enregistrée, sans rien enregistrer en retour, exécute votre script en root, et indique s’il a réussi (code de sortie 0), ce qu’il a affiché et quand. L’élève n’est pas informé du résultat, et rien de ce que fait le script ne modifie sa machine.

Un élève d’un établissement reçoit la taille que vous écrivez, jusqu’à 4 CPU, 4096 Mo de mémoire et 20 Go de disque. Tout ce que vous omettez reprend la valeur par défaut de l’établissement, 2 CPU, 2 Go et 10 Go. Quelqu’un qui n’est dans aucun établissement et ouvre le lab est limité à la taille de sa propre catégorie.

Quand un élève arrête sa VM, la liste de la classe montre ce qu’il a modifié : paquets installés, fichiers écrits en dehors des dossiers système et historique de /etc. Vous pouvez vous y référer pour noter son travail sans démarrer sa VM.

Des labs réseau dans une seule VM

Un routeur a besoin de sa propre machine et un élève n’a qu’une VM : les labs réseau d’exemple construisent donc toute la topologie à l’intérieur. Chaque routeur et chaque machine est un espace de noms réseau, chaque lien une paire de câbles virtuels, et chaque routeur exécute son propre FRRouting, avec la console vtysh que les élèves retrouveront sur du vrai matériel. La page d’un lab VM en propose trois pour commencer : routage statique, OSPF, et BGP entre deux systèmes autonomes. En utiliser un remplace le texte, et « Annuler » rétablit le vôtre.

Tout est installé par le script setup du lab, depuis l’archive de paquets d’Ubuntu, au premier démarrage de la VM, en une dizaine de secondes. Il installe aussi une commande netlab : netlab status liste les machines, netlab shell r1 ouvre la console d’un routeur et netlab shell h1 celle d’une machine, et netlab reset relance le lab. Les vraies commandes fonctionnent aussi, et c’est le but : sudo ip netns exec h1 ping 10.0.3.2, sudo ip netns exec r1 tcpdump -ni to-r2, sudo vtysh -N r1. Chaque lab laisse dans le dossier personnel de l’élève un README qui dit quoi faire, sans donner aucune réponse.

Chaque exemple a une vérification. Elle s’exécute sur une copie de la machine enregistrée après un démarrage à neuf : elle ne voit donc que ce que l’élève a enregistré avec write memory (quitter netlab shell enregistre aussi), et elle ne réussit que si l’état réel est bon : les routes sont dans les tables de routage, les voisins OSPF sont Full ou la session BGP est Established, h1 atteint h2 en passant par les routeurs, et le réseau privé reste injoignable. Déplacer une adresse, une règle de pare-feu ou une route tapée avec ip route et jamais enregistrée ne la fait pas réussir, et la première ligne de ce qu’elle a affiché en donne la raison.

Pour créer votre propre topologie, copiez un exemple et modifiez la topologie dans son setup : une ligne par routeur, machine et lien, et daemons ospfd ou daemons bgpd pour lancer ce protocole sur chaque routeur. L’outil, une topologie et un README tiennent largement dans les 16 Ko de setup.

Questions fréquentes

Vaut-il mieux écrire un lab ici ou le garder sur GitHub ?
« Écrit ici » est le plus rapide pour faire un essai, et suffit pour un lab composé d’images toutes prêtes. Gardez-le sur GitHub pour construire des images à partir d’un Dockerfile, conserver son historique, le tester sur votre machine avec docker compose et le partager avec vos collègues. Passer d’un mode à l’autre ne fait pas perdre ce que vous aviez réglé dans le premier.
Puis-je utiliser une image que j’ai construite moi-même ?
Oui. Publiez-la sur ghcr.io en tant qu’image publique et indiquez son nom dans le fichier, ou laissez le fichier la construire à partir d’un dossier du dépôt.
Les élèves peuvent-ils voir le fichier compose ?
Oui, en lecture seule, sur la page du lab, à côté de leur propre copie : c’est ainsi qu’ils découvrent de quoi le lab est fait. Le jeton, en revanche, n’est jamais montré à personne.