Mini-test 7
FROM, WORKDIR, COPY, RUN, ENV, ARG, EXPOSE, CMD, ENTRYPOINT, AS
9 questions + en savoir plus
Cliquez Voir la solution sur chaque slide
Question 1 / 9 choisir une image par projet
Glissez chaque image dans le projet ou elle est le bon point de depart.
httpd:2.4 (Apache statique pre-configure, sert /usr/local/apache2/htdocs/)php:8.3-apache (Apache + mod_php pre-configures, sert /var/www/html/)python:3.12-slim (Python pret a lancer, slim = bon compromis taille / compatibilite)node:20-alpine (Node + npm, alpine pour la taille — Node n'a pas de probleme musl)mariadb:11 (image officielle, pas besoin de Dockerfile, juste docker run avec volumes/env)nginx:alpine (rapide, leger, configurable via fichier .conf)Principe : toujours partir de l'image la plus specifique qui couvre le besoin. Eviter ubuntu et installer un service manuellement quand une image officielle existe deja.
Pour le portfolio statique, alternative possible : nginx:alpine aurait aussi marche (sert des fichiers statiques aussi). Le choix se fait sur les preferences (Apache vs Nginx, mais les deux conviennent pour du statique).
Question 2 / 9 FROM ubuntu:24.04
ubuntu:24.04 pour servir un site PHP.
Glissez chaque composant dans la bonne categorie.
Glissez chaque composant dans la categorie correcte. Cliquez sur une etiquette deposee pour la retirer.
Tourne deja : apt + bash + coreutils, userland Ubuntu. C'est tout.
A faire : tout le reste. Concretement :
RUN apt update && apt install -y \ apache2 libapache2-mod-php php COPY ./site /var/www/html/ CMD ["apache2ctl", "-D", "FOREGROUND"]
Quand choisir ubuntu malgre tout ce travail : on a besoin de paquets exotiques, scripts d'init complexes, ou plusieurs services. Sinon, partir de php:8.3-apache evite tout ce code.
Question 3 / 9 FROM httpd:2.4
httpd:2.4 pour servir un site
statique (HTML, CSS, JS). Glissez chaque composant dans la
bonne categorie.
Glissez chaque composant. Cliquez sur une etiquette deposee pour la retirer.
Tourne deja : Apache configure et lance, htdocs comme racine, EXPOSE 80, CMD pour apache en avant-plan, userland Debian minimal.
A faire : juste COPY ./site /usr/local/apache2/htdocs/. C'est tout. Le Dockerfile complet :
FROM httpd:2.4 COPY ./site /usr/local/apache2/htdocs/
Important : pas de PHP, pas de mod_php. Si on en avait besoin, on partirait plutot de php:8.3-apache.
Question 4 / 9 FROM php:8.3-apache
php:8.3-apache pour servir un site PHP dynamique.
Glissez chaque composant dans la bonne categorie.
Glissez chaque composant. Cliquez sur une etiquette deposee pour la retirer.
Tourne deja : Apache + mod_php pre-actives, l'interpreteur PHP 8.3, /var/www/html comme racine, EXPOSE 80, CMD pour Apache en avant-plan.
A faire : juste copier vos fichiers .php. Le Dockerfile complet :
FROM php:8.3-apache COPY ./site /var/www/html/
C'est la voie courte pour un site PHP. Tout est pre-configure : il ne reste que vos fichiers.
Pour ajouter des extensions PHP (ex: mysqli, gd), RUN docker-php-ext-install mysqli gd dans le Dockerfile.
Question 5 / 9 WORKDIR vs cd dans RUN : predire ls
README.md, package.json, et un dossier src/.
Comparez 2 variantes du Dockerfile et donnez la sortie du RUN ls final dans chaque cas.
Sortie du RUN ls final ?
Sortie du RUN ls final ?
Variante A — avec WORKDIR :
README.md package.json src
WORKDIR /app cree /app et s'y positionne pour toutes les couches suivantes (RUN et COPY)COPY . . copie le build context dans /app (le . de destination = WORKDIR courant)RUN ls s'execute dans /app — donc liste les 3 entrees copieesVariante B — avec cd dans un RUN :
README.md bin dev etc home lib media mnt opt package.json proc root run sbin src srv sys tmp usr var
Le contenu de la racine / melange avec les fichiers du build context.
RUN mkdir -p /app : OK, cree le repertoireRUN cd /app : entre dans /app mais cette couche se termine immediatement — le cd est perdu. La couche suivante repart du WORKDIR ambiant, qui est encore / par defaut.COPY . . : la destination . = WORKDIR courant = /. Les fichiers vont a la racine, melanges avec bin, etc, etc.RUN ls : execute dans / aussi, donc liste toutRegle : dans un Dockerfile, on n'utilise jamais cd entre des RUN. Toujours WORKDIR, qui persiste entre couches.
Question 6 / 9 COPY : source et destination
/home/nadine/dev/mon-app/.
Il contient un Dockerfile et un sous-dossier sources/. Vous etes
DANS ce dossier, et vous lancez docker build -t img . (le
. final = le build context).
(a) Sur le laptop, ou est physiquement ./sources avant le COPY ? Donnez le chemin absolu.
(b) Et ou arrive son contenu dans l'image ?
(c) Et que ferait COPY . . au lieu de COPY ./sources /app/code ?
(a) Avant le COPY, sur le laptop : /home/nadine/dev/mon-app/sources/
Le path ./sources du Dockerfile est relatif au build context, qui est le dernier argument de docker build (ici . = le repertoire courant = /home/nadine/dev/mon-app).
(b) Apres le COPY, dans l'image : /app/code/ (avec main.py, utils.py, config.yaml)
La destination /app/code est un chemin absolu DANS l'image. (Si la destination etait relative comme ./code, ce serait relatif au WORKDIR courant.)
(c) COPY . . :
. = tout le build context (/home/nadine/dev/mon-app/). = le WORKDIR courant (/app)sources/ sont copies dans /app/Regle absolue : on ne peut PAS faire COPY ../autre-dossier : on ne peut JAMAIS sortir du build context (securite + reproductibilite).
Astuce : .dockerignore filtre ce qui sera dans le build context AVANT que COPY le voit. Indispensable pour eviter de copier node_modules, .git, etc.
Question 7 / 9 ENV vs ARG (drag & drop)
ENV et ARG sont les deux directives de variables
d'un Dockerfile. Elles ont l'air similaires mais vivent dans des temps differents.
Glissez chaque caracteristique dans la directive concernee.
Une caracteristique peut aller dans une seule colonne. Cliquez sur une etiquette deposee pour la retirer.
docker inspectdocker run -e KEY=val--build-arg KEY=valENV (4) : runtime, visible via docker inspect, modifiable avec docker run -e, lue par l'app via getenv / process.env. Persiste dans l'image finale.
ARG (4) : build seulement, disparait apres le build, modifiable avec --build-arg, typique pour version / commit hash / date de build. N'existe pas au runtime.
Le pont entre les deux : ENV qui prend la valeur d'un ARG
L'ARG est temporaire (build seulement), mais on l'utilise pour graver sa valeur dans une ENV qui, elle, persiste. Au runtime, l'app peut lire APP_VERSION via les variables d'environnement.
Verifier les ENV d'une image deja construite :
docker inspect img --format '{{.Config.Env}}'
Note : un ARG sans valeur par defaut (ex: ARG BUILD_DATE) doit etre passe via --build-arg, sinon il est vide.
Question 8 / 9 EXPOSE + docker run + browser
EXPOSE 80. L'image servira un site web. Vous voulez ouvrir http://localhost:8080
dans votre navigateur et tomber sur l'app. Quelle combinaison docker run + URL est coherente ?
Vous voulez ouvrir http://localhost:8080 dans votre navigateur. Choisissez d'abord la commande docker run, puis l'URL que vous tapez dans le browser.
docker run -p 8080:80 imgdocker run -p 80:8080 imgdocker run imgdocker run -P imghttp://localhost:80http://localhost:8080http://container:80http://localhost:32768Bonnes reponses : 1=A (docker run -p 8080:80 img), 2=B (http://localhost:8080).
Format -p HOTE:CONTAINER : on relie le port 8080 de l'hote (cote machine, cote browser) au port 80 du container (cote application, le port que Apache ecoute). Le browser tape localhost:8080, ca arrive sur le port 80 du container, donc sur Apache.
Pourquoi pas les autres docker run ?
-p 80:8080 = hote 80 -> container 8080. Mais l'app ecoute sur 80 dans le container, pas 8080. Rien n'ecoute cote container 8080.docker run img sans -p = aucun port publie sur l'hote. EXPOSE 80 est juste de la metadata, ne publie rien.-P (majuscule) publie automatiquement les ports EXPOSE sur des ports aleatoires de l'hote (ex: 32768). Pas 8080.Pourquoi pas les autres URLs ?
localhost:80 = le port 80 de votre laptop, ou rien n'ecoute (le container est sur 8080).container:80 n'est pas un hostname resolu sur votre laptop.localhost:32768 est le port quand on utilise -P majuscule (port aleatoire), pas -p 8080:80.Memo : -p HOTE:CONTAINER — le 1er nombre = ce que tu tapes dans le browser, le 2e = le port que l'app ecoute dans le container.
Question 9 / 9 CMD vs ENTRYPOINT
Que fait docker run img --debug dans chaque version ?
Version A (CMD) :
L'argument du docker run remplace le CMD au complet. Le container lance :
--debug # echec : --debug n'existe pas comme commande
Version B (ENTRYPOINT) :
L'argument du docker run est ajoute a l'ENTRYPOINT. Le container lance :
python bot.py --debug # le mode debug du bot
Resume :
ENTRYPOINT ["python", "bot.py"] + CMD ["--config", "default.yml"].
Le CMD est alors une valeur par defaut pour les arguments.Surcharger un ENTRYPOINT (rare, mais possible) :
docker run --entrypoint /bin/sh img
En savoir plus
Couches RUN, taille d'image, multi-stage
3 questions avancees
Pourquoi 30 Mo au lieu de 1 Go
En savoir plus RUN : une couche, ou plusieurs
Expliquez pourquoi la difference de taille :
Version B est plus petite.
Pourquoi : chaque RUN cree une nouvelle couche dans l'image.
Une fois une couche figee, on ne peut plus rien y supprimer. Dans Version A :
Resultat : les 50 Mo des listes apt restent dans la couche 1, gonflent l'image finale.
Dans Version B, tout se passe dans une seule couche. Le rm se fait avant que la couche soit figee, donc les fichiers temporaires n'existent jamais reellement.
Regle : chainer les RUN apt avec && et un rm -rf /var/lib/apt/lists/* a la fin. Idem pour npm cache clean, yum clean all, etc.
En savoir plus AS : multi-stage teaser
(a) Combien y a-t-il de stages ? (b) A quoi sert le nom builder ? (c) Quelle est la taille finale de l'image ?
(a) Deux stages (separes par les deux FROM).
(b) AS builder donne un nom au premier stage pour pouvoir y faire reference plus tard avec COPY --from=builder. Sans le nom, on devrait utiliser un numero (--from=0) qui est moins lisible.
(c) L'image finale contient :
alpine:3.19 (~5 Mo)bot compile (quelques Mo)L'image golang:1.22 (~800 Mo) avec tout le compilateur Go et les sources n'est PAS dans l'image finale. C'est le point cle du multi-stage : separer le build (gros) du runtime (petit).
Autres patterns :
# Stages anonymes (acces par numero) FROM node:20 AS stage1 FROM nginx:alpine COPY --from=0 ... # equivalent a --from=stage1 # Plusieurs stages, on peut tous les chainer FROM ... AS deps FROM ... AS builder FROM ... AS runtime
(Le mini-test 8 Multi-stage ira plus loin sur ces patterns.)
En savoir plus optimisation : taille + multi-stage
Partie A — alpine vs slim vs full : pour un bot IRC en Python, on hesite entre 3 variantes :
Donnez (1) la taille approximative de chacune, (2) un avantage et un piege de chaque, (3) celle que vous choisiriez par defaut.
Partie B — multi-stage avec COPY --from : pour aller plus loin, on construit dans une image avec compilateur, puis on copie juste le resultat dans une image legere. Donnez un exemple de Dockerfile multi-stage pour un programme Go (ou Rust, ou C). Indiquez quelle est la taille de l'image finale par rapport a l'image de build.
Partie A - alpine / slim / full
apt install
necessaire pour libjpeg, gcc, etc.Choix par defaut : slim. Pour la plupart des projets Python, c'est le meilleur compromis. Alpine est tentant mais cause souvent des heures de debug a cause de musl.
Meme logique pour node:20 vs node:20-slim vs node:20-alpine.
Partie B - multi-stage
FROM golang:1.22 AS builder WORKDIR /src COPY . . RUN CGO_ENABLED=0 go build -o bot . FROM alpine:3.19 COPY --from=builder /src/bot /usr/local/bin/bot CMD ["/usr/local/bin/bot"]
bot.L'image golang:1.22 n'est PAS dans l'image finale. C'est le point cle du multi-stage : separer le build (gros, outils de compilation) du runtime (petit, juste l'executable).
Encore plus petit : scratch
FROM golang:1.22 AS builder ... FROM scratch COPY --from=builder /src/bot /bot CMD ["/bot"]
scratch est l'image vide absolue (0 octet). L'image finale ne contient que votre binaire. Marche pour Go (statique), Rust (avec musl). Marche PAS pour Python/Node (interpreteur necessaire).
Resume - 2 leviers :
FROM ... AS + COPY --from)Bilan
R pour recommencer.