Aller au contenu

Quiz : Docker, les fondamentaux

100 · Comprendre ⏱ 30 min docker

Ce quiz valide le niveau 100 (Comprendre) des compétences du cours Docker : les fondamentaux. Répondez à chaque question avant d'ouvrir la réponse. Visez au moins 17 bonnes réponses sur 21 ; chaque réponse renvoie à la leçon à relire en cas d'erreur.

Concepts

1. Dans un conteneur Alpine lancé sur un hôte Ubuntu, la commande uname -r affiche :

  • a) la version du noyau d'Alpine
  • b) la version du noyau de l'hôte Ubuntu
  • c) une version de noyau virtuelle fournie par Docker
  • d) rien, car un conteneur n'a pas de noyau
Réponse

b. Un conteneur partage le noyau de l'hôte ; l'image Alpine ne contient que l'espace utilisateur d'Alpine. La réponse d serait juste dans l'idée (le conteneur n'a pas de noyau propre), mais uname interroge le noyau qui exécute le processus, celui de l'hôte. Leçon 1.

2. Quel mécanisme du noyau limite la quantité de mémoire qu'un conteneur peut consommer ?

  • a) les namespaces
  • b) les cgroups
  • c) seccomp
  • d) overlayfs
Réponse

b. Les cgroups limitent ce qu'un processus consomme (memory.max) ; les namespaces limitent ce qu'il voit. Leçon 2.

3. Par défaut, quel namespace un conteneur Docker partage-t-il avec l'hôte ?

  • a) net
  • b) pid
  • c) user
  • d) mnt
Réponse

c. Sans userns-remap ni mode rootless, le namespace user est partagé : l'UID 0 du conteneur est l'UID 0 de l'hôte, réduit par les capabilities, seccomp et AppArmor. Leçons 2 et 12.

4. Un conteneur s'arrête avec le code de sortie 137 et docker inspect indique OOMKilled=true. Que s'est-il passé ?

Réponse

Le processus a dépassé la limite mémoire de son cgroup et le noyau l'a tué par SIGKILL (137 = 128 + 9). Il faut mesurer la consommation réelle et ajuster la limite, ou corriger une fuite de mémoire. Leçons 2 et 12.

5. Pourquoi Kubernetes a-t-il pu cesser d'utiliser Docker Engine en 2022 sans que les équipes aient à reconstruire leurs images ?

Réponse

Les images suivent le format standard de l'OCI, et Kubernetes les exécute avec containerd (ou CRI-O), le même composant que Docker utilise en dessous, puis runc. Seul le pont dockershim a disparu. Leçons 1 et 3.

Architecture et installation

6. Remettez dans l'ordre la chaîne d'exécution d'un docker run : runc, client docker, containerd, dockerd, shim.

Réponse

Client docker → dockerd → containerd → shim (containerd-shim-runc-v2) → runc. runc crée le conteneur puis se termine ; le shim reste le parent du processus. Leçon 3.

7. Un collègue propose d'ajouter le compte de service de l'outil de déploiement au groupe docker d'un serveur de production « pour éviter sudo ». Quel est le problème ?

Réponse

Le groupe docker donne accès au socket du démon, qui tourne en root : c'est un accès root sans mot de passe et sans trace dans les journaux de sudo. Un compte compromis prend le contrôle du serveur. Leçons 3 et 4.

8. Quel réglage de daemon.json évite qu'un conteneur bavard remplisse le disque du serveur ?

Réponse

La rotation des journaux : par exemple "log-driver": "local" avec "log-opts": {"max-size": "10m", "max-file": "5"}. Le pilote par défaut json-file ne fait aucune rotation. Le réglage ne s'applique qu'aux conteneurs créés ensuite. Leçon 4.

Exécuter et diagnostiquer

9. Que fait la commande docker run alpine:3.22 --rm ?

  • a) elle lance Alpine et supprime le conteneur à la fin
  • b) elle échoue, car --rm n'est pas une option valide
  • c) elle tente d'exécuter un programme nommé --rm dans le conteneur
  • d) elle supprime l'image Alpine
Réponse

c. Tout ce qui suit le nom de l'image est transmis au conteneur et remplace le Cmd. Docker répond exec: "--rm": executable file not found in $PATH, code 127, et le conteneur raté reste à l'état Created. Leçon 5.

10. Pourquoi faut-il éviter -t dans une commande docker run dont la sortie est redirigée vers un fichier ?

Réponse

Le pseudo-terminal convertit les fins de ligne \n en \r\n, ce qui insère des \r parasites dans le fichier. En CI, -it échoue en outre si l'entrée n'est pas un terminal. Leçon 5.

11. Un conteneur lancé avec CMD python app.py (forme shell) met dix secondes à s'arrêter à chaque docker stop. Expliquez et corrigez.

Réponse

La forme shell fait de /bin/sh le PID 1. En PID 1, le shell ignore SIGTERM, qu'il ne gère pas, et ne le transmet pas à Python ; Docker attend le délai de grâce puis envoie SIGKILL (code 137). Correction : forme exec, CMD ["python", "app.py"], et une application qui gère SIGTERM. Leçons 6 et 8.

12. Le gabarit docker inspect -f '{{.NetworkSettings.IPAddress}}' échoue avec Docker 29. Pourquoi, et que faire ?

Réponse

Ce champ, déprécié depuis 2015, a été retiré de l'API. Il faut parcourir les réseaux : {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}. Mieux : ne pas dépendre de l'adresse IP d'un conteneur et utiliser son nom sur un réseau défini par l'utilisateur. Leçons 6 et 10.

Images

13. Quelle est la différence entre nginx:1.29 et nginx@sha256:1881968a... ?

Réponse

La première est une étiquette, un pointeur que l'éditeur peut déplacer vers une autre image. La seconde est une empreinte, le hachage du contenu : elle désigne pour toujours les mêmes octets. En production, on épingle par empreinte. Leçon 7.

14. Un Dockerfile copie un fichier .env dans une étape, puis le supprime avec RUN rm .env à l'étape suivante. Le secret est-il dans l'image ?

Réponse

Oui. La couche de la copie contient toujours le fichier ; la suppression n'ajoute qu'un marqueur dans la couche suivante. N'importe qui peut récupérer le fichier en extrayant les couches (docker save). Il faut l'exclure du contexte avec .dockerignore et considérer le secret comme compromis. Leçons 7 et 8.

15. Pourquoi copier requirements.txt et installer les dépendances avant de copier le code dans un Dockerfile ?

Réponse

Pour profiter du cache de construction : une étape est réexécutée dès qu'elle ou une étape précédente change. Le code change à chaque commit, les dépendances rarement ; dans cet ordre, une modification du code ne réinstalle pas les dépendances. Leçon 8.

Données et réseau

16. Vous recréez le conteneur d'une base PostgreSQL lancée sans option -v, et la base est vide. Les données sont-elles perdues ?

Réponse

Pas forcément. L'image déclare un VOLUME : Docker avait créé un volume anonyme, que la suppression du conteneur a laissé orphelin. On le retrouve avec docker volume ls --filter dangling=true et on le remonte dans un nouveau conteneur. Surtout, ne pas lancer docker volume prune avant. Leçon 9.

17. Pourquoi préférer --mount type=bind,src=./donnees,... à -v ./donnees:... dans un script ?

Réponse

Si le répertoire source n'existe pas, -v le crée silencieusement en root (ce qui produit ensuite des erreurs de droits), alors que --mount refuse avec une erreur explicite. Leçon 9.

18. Deux conteneurs lancés sans option --network ne peuvent pas se joindre par leur nom. Pourquoi ?

Réponse

Ils sont sur le réseau bridge par défaut, qui n'a pas de résolution de noms. Les réseaux créés avec docker network create ont un DNS embarqué (127.0.0.11) qui résout les noms des conteneurs. Leçon 10.

19. Sur un serveur Ubuntu protégé par UFW (deny incoming), un conteneur est lancé avec -p 5432:5432. La base est-elle joignable depuis Internet ?

Réponse

Oui. Le port publié est traité par une règle de DNAT puis passe par la chaîne FORWARD, où les règles de Docker sont évaluées avant celles d'UFW. Il faut publier sur 127.0.0.1 (ou ne pas publier du tout une base), et filtrer dans la chaîne DOCKER-USER ou en amont. Leçon 10.

Compose et sécurité

20. Vous modifiez app.py puis lancez docker compose up -d. Rien ne change. Pourquoi, et quelle commande utiliser ?

Réponse

Compose ne recrée un service que si sa configuration ou son image locale change ; or rien n'a reconstruit l'image, qui est donc identique. Il faut docker compose up -d --build. En développement, on monte plutôt le code dans le conteneur avec un rechargement à chaud. Leçon 11.

21. Un conteneur applicatif tourne sous l'UID 1000. Son image contient un binaire setuid root. Quelle option empêche l'application, si elle est compromise, de devenir root en l'exécutant ?

  • a) --cap-drop ALL
  • b) --security-opt no-new-privileges
  • c) --read-only
  • d) --pids-limit 100
Réponse

b. Le drapeau no_new_privs fait ignorer le bit setuid à l'exécution : le processus garde l'UID effectif 1000. --cap-drop ALL réduirait les pouvoirs du root obtenu, mais pas l'obtention de l'UID 0 lui-même ; les deux options se combinent. Leçon 12.

Pour valider le niveau 200

Le niveau 200 (Pratiquer) se valide par le lab Signalements : un projet Compose qui passe toutes les vérifications du script fourni.

Plan du cours