Aller au contenu
English

Sécurité à l'exécution

  • Leçon 6
  • 50 min
  • Module 2 : Construire l'image de l'application

Ce que vous saurez faire

Limitez ce qu’une personne malveillante peut faire si elle prend le contrôle de l’API. Assurez-vous aussi que l’image ne contient jamais de secret. Ces deux mesures coûtent peu maintenant, mais coûtent cher à corriger après la publication de l’image.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 50 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

Testé avec Docker Desktop 4.81.0 (Docker Engine 29.6.1) sur macOS, avec une puce Apple. Les étapes pour Windows et Linux n’ont pas été testées.

Démarrez l’API en donnant un nom au conteneur, pour pouvoir l’utiliser dans d’autres commandes. -d exécute le conteneur en arrière-plan :

Terminal, in ~/projects/ticket-api
docker run -d --rm --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0

Demandez au conteneur quel utilisateur l’exécute :

Terminal
docker exec ticket-api id

La sortie :

uid=0(root) gid=0(root) groups=0(root)

root, l’utilisateur numéro 0, qui peut tout faire. Sans ligne USER, un Dockerfile exécute l’application comme root. Amira essaie de voir ce que root peut faire. Elle crée un fichier à côté du code, puis remplace le classificateur par une seule ligne :

Terminal
docker exec ticket-api touch /app/ticket_api/x.py
docker exec ticket-api sh -c 'echo "print(1)" > /app/ticket_api/classifier.py'

Les deux commandes fonctionnent, sans message. Le conteneur exécute maintenant du code modifié. Arrêtez-le avant qu’il fasse autre chose :

Terminal
docker stop ticket-api

--rm supprime le conteneur et, avec lui, le fichier modifié. L’image n’a pas changé. Mais imaginez une personne malveillante qui trouve un bogue dans l’API et peut y exécuter des commandes. Comme root, elle peut modifier le code du service en cours d’exécution, installer des programmes et lire tous les fichiers du conteneur. L’isolation du conteneur l’empêche d’accéder à votre ordinateur. Mais root dans le conteneur porte le même numéro d’utilisateur, 0, que root sur l’hôte. Un bogue dans l’isolation peut alors lui donner les droits de root sur l’hôte.

La solution est simple : l’application n’a pas besoin d’être root. Elle doit seulement lire son code et répondre aux requêtes.

Ajoutez deux lignes au Dockerfile. RUN useradd crée dans l’image un utilisateur normal nommé app. USER app indique que chaque étape suivante et chaque conteneur s’exécutent comme cet utilisateur :

Dockerfile
FROM python:3.14.8-slim-trixie
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_ROOT_USER_ACTION=ignore
RUN useradd --create-home --uid 10001 app
WORKDIR /app
COPY requirements-run.txt .
RUN pip install -r requirements-run.txt
COPY pyproject.toml .
COPY ticket_api ./ticket_api
USER app
EXPOSE 8000
CMD ["fastapi", "run", "--host", "0.0.0.0", "--port", "8000"]
Partie Pourquoi
useradd ... app Un utilisateur normal, sans droits particuliers.
--uid 10001 Un numéro d’utilisateur fixe. Chaque construction donne le même numéro. Un montage de fichier ou une règle de sécurité pourra donc l’utiliser plus tard. Un numéro élevé évite les conflits avec les utilisateurs d’un ordinateur Linux normal, dont les numéros commencent à 1 000.
--create-home Crée /home/app. Certains outils écrivent leurs paramètres dans le dossier personnel.
RUN useradd près du début L’utilisateur change très rarement. Il précède donc la liste des paquets, comme l’indique la règle du cache.
USER app après les lignes COPY pip install et COPY s’exécutent encore comme root. Le code et les paquets appartiennent donc à root. Seule l’application s’exécute comme app.

Voici le Dockerfile terminé de ce cours. Il ne change pas dans les modules suivants. Construisez l’image :

Terminal
docker build -t ticket-api:1.0.0 .

Les lignes des étapes, avec un outil de construction dont le cache est vide (version abrégée) :

=> [1/7] FROM docker.io/library/python:3.14.8-slim-trixie@sha256:f85c569 4.5s
...
=> [2/7] RUN useradd --create-home --uid 10001 app 0.4s
=> [3/7] WORKDIR /app 0.0s
=> [4/7] COPY requirements-run.txt . 0.0s
=> [5/7] RUN pip install -r requirements-run.txt 16.7s
=> [6/7] COPY pyproject.toml . 0.1s
=> [7/7] COPY ticket_api ./ticket_api 0.1s

Il y a maintenant sept étapes. La nouvelle étape 2 précède toutes les autres. Chaque étape s’exécute donc à nouveau une fois. USER n’a pas d’étape : comme ENV, c’est un paramètre. L’image garde la même taille qu’avant, 309 Mo.

Démarrez la nouvelle image avec la même commande qu’avant :

Terminal
docker run -d --rm --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0
docker exec ticket-api id

La sortie (après l’identifiant du conteneur) :

uid=10001(app) gid=10001(app) groups=10001(app)

Essayez les mêmes modifications que root :

Terminal
docker exec ticket-api touch /app/x
docker exec ticket-api sh -c 'echo "print(1)" > /app/ticket_api/classifier.py'

La sortie :

touch: cannot touch '/app/x': Permission denied
sh: 1: cannot create /app/ticket_api/classifier.py: Permission denied

Les fichiers appartiennent à root, et les autres utilisateurs peuvent seulement les lire :

Terminal
docker exec ticket-api ls -l /app

La sortie :

total 12
-rw-r--r-- 1 root root 296 Oct 7 16:49 pyproject.toml
-rw-r--r-- 1 root root 1092 Oct 7 16:50 requirements-run.txt
drwxr-xr-x 2 root root 4096 Oct 7 17:03 ticket_api

rw-r--r-- signifie que le propriétaire (root) peut lire et écrire, mais que tous les autres peuvent seulement lire. C’est tout ce dont l’application a besoin. /health donne encore {"status":"ok"}. docker top ticket-api montre le processus avec le numéro d’utilisateur :

UID PID PPID C STIME TTY TIME CMD
10001 20466 20442 25 17:10 ? 00:00:00 /usr/local/bin/python3.14 /usr/local/bin/fastapi run --host 0.0.0.0 --port 8000
Deux conteneurs construits à partir du même code. Sans ligne USER, fastapi s'exécute comme root, uid 0. Il peut lire et modifier le code dans /app, puis écrire dans /usr/local, où se trouvent Python et les paquets. Les deux dernières actions sont marquées comme exploitables par une personne malveillante. Avec USER app, fastapi s'exécute comme app, uid 10001. Il peut lire le code dans /app, mais ne peut ni le modifier ni écrire dans /usr/local.
La même application. Comme root, un processus compromis peut modifier le code et les paquets. Comme app, il peut seulement les lire.
Description textuelle du diagramme

Le panneau de gauche montre le conteneur sans ligne USER. fastapi s’exécute comme root, uid 0. Il peut lire le code dans /app. Il peut aussi modifier le code dans /app et écrire dans /usr/local, où se trouvent Python et les paquets. Le schéma marque ces deux lignes : une personne malveillante qui prend le contrôle du processus peut les exploiter. Le panneau de droite montre le conteneur avec USER app. fastapi s’exécute comme app, uid 10001. Il peut lire le code dans /app, mais ne peut ni le modifier ni écrire dans /usr/local.

Arrêtez le conteneur : docker stop ticket-api.

Une image est un fichier que vous copiez : vers un registre, chez Sam ou dans le nuage. Toute personne qui peut la télécharger peut lire chaque couche. Un secret ne doit donc jamais faire partie d’une construction. L’API de billets le fait déjà correctement : la clé entre au démarrage du conteneur, avec -e API_KEY=.... Le module 3 la fournit sous forme de fichier. L’image ne contient aucune clé.

Voici trois façons courantes d’insérer un secret dans une image par erreur :

Erreur Où le secret reste
COPY .env . ou COPY . . avec un .env dans le dossier Dans la couche de ce COPY, sous forme de fichier
ENV API_KEY=... Dans les paramètres de l’image. docker image inspect le montre à tout le monde.
ARG API_KEY et --build-arg API_KEY=... Dans l’historique de construction des lignes RUN qui l’utilisent

Votre liste d’autorisation bloque déjà la première erreur. Amira la teste avec un Dockerfile qui tente de copier .env :

#7 [3/4] COPY .env .
#7 ERROR: failed to calculate checksum of ref 65tu4hq3l2veiuoltpznypryw::0sv39oljj5ojc6ywju1h5qxh8: "/.env": not found

.env ne fait pas partie du contexte de construction. COPY ne peut donc pas le trouver. L’erreur courante ci-dessous montre ce qui arrive dans un projet sans liste d’autorisation.

Si une construction a elle-même besoin d’un identifiant secret, utilisez un secret de construction. Par exemple, un jeton (token) pour télécharger un paquet privé. RUN --mount=type=secret,id=api_key ... rend le fichier /run/secrets/api_key accessible à cette seule étape RUN. Il n’est jamais enregistré dans une couche. Vous fournissez le fichier avec docker build --secret id=api_key,src=api_key.txt .. Amira l’a testé : l’étape a lu 31 octets dans /run/secrets/api_key. Dans un conteneur créé à partir de l’image, ls /run/secrets indique No such file or directory (aucun fichier ou dossier de ce nom). L’API de billets n’a pas besoin de secret de construction.

Erreur courante

La clé reste dans l'image, même après RUN rm

Faites ce test dans un nouveau dossier, ~/projects/leak-demo, avec une fausse clé. N’utilisez jamais une vraie clé pour ce test.

Symptôme : aucun signe visible. Un collègue a écrit un Dockerfile qui copie .env, puis le supprime, « par sécurité » :

~/projects/leak-demo/Dockerfile
FROM python:3.14.8-slim-trixie
WORKDIR /app
COPY .env .
RUN rm .env

Le dossier contient un fichier .env avec une seule ligne, API_KEY=demo-key-7731-not-real, et aucun .dockerignore. La construction fonctionne :

Terminal, in ~/projects/leak-demo
docker build -t leak-demo:1 .
docker run --rm leak-demo:1 ls -a /app

La sortie (les lignes des étapes, puis ls) :

=> [1/4] FROM docker.io/library/python:3.14.8-slim-trixie@sha256:f85c569 0.0s
=> CACHED [2/4] WORKDIR /app 0.0s
=> [3/4] COPY .env . 0.0s
=> [4/4] RUN rm .env 0.4s
.
..

/app est vide. La clé semble avoir disparu.

Diagnostic : examinez les couches. docker history montre que la couche COPY .env . est toujours là :

Terminal
docker history leak-demo:1
IMAGE CREATED CREATED BY SIZE COMMENT
54aae749bcd1 2 seconds ago RUN /bin/sh -c rm .env # buildkit 8.19kB buildkit.dockerfile.v0
<missing> 2 seconds ago COPY .env . # buildkit 12.3kB buildkit.dockerfile.v0
<missing> 19 minutes ago WORKDIR /app 8.19kB buildkit.dockerfile.v0
<missing> 39 hours ago CMD ["python3"] 0B buildkit.dockerfile.v0
...

Les couches sont en lecture seule. RUN rm .env ne peut pas modifier la couche en dessous. Il ajoute seulement une nouvelle couche avec un marqueur, un fichier whiteout (masquage), qui cache .env dans un conteneur en cours d’exécution. Prouvez-le : enregistrez l’image dans un fichier, décompressez-le et trouvez la couche contenant app/.env :

Fenêtre de terminal
docker image save leak-demo:1 -o leak-demo.tar
mkdir layers && tar -xf leak-demo.tar -C layers
for f in layers/blobs/sha256/*; do tar -tf "$f" 2>/dev/null | grep -qx app/.env && echo "$f"; done

La sortie (le fichier de l’image fait 42 Mo) :

layers/blobs/sha256/ed3d3b3e5e58ff6e9574e76ff47278683ebdfd02bf7f54b38b1953fc56b04942

Lisez le fichier de cette couche. Utilisez le nom dans votre propre sortie :

Terminal
tar -xOf layers/blobs/sha256/ed3d3b3e5e58ff6e9574e76ff47278683ebdfd02bf7f54b38b1953fc56b04942 app/.env
API_KEY=demo-key-7731-not-real

La clé, au complet. layers/manifest.json liste les sept couches de l’image dans l’ordre. D’abord les quatre de l’image de base, puis WORKDIR, puis celle-ci (couche 6). La couche 7 vient de RUN rm. Elle contient seulement app/ et app/.wh..env, le fichier whiteout.

L'image est une pile de couches, la plus récente en haut. La couche 7, issue de RUN rm .env, contient seulement un marqueur, .wh..env, qui cache .env. La couche 6, issue de COPY .env ., contient app/.env avec la clé d'API. La couche 5, issue de WORKDIR /app, contient le dossier. Les couches 1 à 4 forment l'image de base. Dans un conteneur, ls -a /app ne montre aucun .env. Mais dans l'image enregistrée, la couche 6 contient toujours le fichier et la clé.
RUN rm ajoute une couche qui cache le fichier. La couche en dessous le contient toujours, et toute personne ayant l'image peut le lire.
Description textuelle du diagramme

L’image est une pile de couches, la plus récente en haut. Les couches 1 à 4 viennent de FROM python:3.14.8-slim-trixie. La couche 5 vient de WORKDIR /app et contient le dossier. La couche 6 vient de COPY .env . et contient app/.env avec la clé d’API. Le schéma la met en évidence. La couche 7 vient de RUN rm .env et contient seulement un marqueur, .wh..env, qui cache .env. Une note en bas indique que, dans un conteneur, ls -a /app ne montre aucun .env. Mais dans l’image enregistrée, la couche 6 contient toujours le fichier et la clé.

ENV est encore plus facile à lire. Avec ENV API_KEY=demo-key-7731-not-real dans un Dockerfile, docker image inspect --format '{{json .Config.Env}}' l’affiche avec les autres paramètres :

["PATH=/usr/local/bin:...","PYTHON_VERSION=3.14.8","PYTHON_SHA256=c221...","API_KEY=demo-key-7731-not-real"]

Cause : chaque couche d’une image reste dans l’image. Une étape ultérieure peut cacher un fichier, mais elle ne peut pas le supprimer.

Correction :

  1. Considérez la clé comme publique. Créez une nouvelle clé et cessez d’utiliser l’ancienne. Supprimer l’image ne suffit pas : vous ne savez pas qui l’a copiée.
  2. Supprimez l’image partout : docker image rm leak-demo:1 sur votre ordinateur, et dans chaque registre où vous l’avez envoyée.
  3. Modifiez le Dockerfile pour que le secret n’entre jamais dans une construction. Aucun COPY de fichiers secrets, aucun ENV ou ARG contenant un secret. Fournissez le secret au démarrage du conteneur. Utilisez un .dockerignore comme liste d’autorisation.

Pour un vrai service d’IA : les clés d’API des fournisseurs de modèles ont une valeur monétaire pour une personne malveillante. De plus, les images vont dans des registres accessibles à de nombreuses personnes et de nombreux systèmes. Une image doit pouvoir être publiée sans risque. Gardez toutes les clés, tous les jetons et tous les mots de passe hors de l’image. Insérez-les seulement au démarrage du conteneur.

Nettoyez le test : docker image rm leak-demo:1. Supprimez ensuite le dossier ~/projects/leak-demo avec son .env, leak-demo.tar et layers.

  1. Ajoutez RUN useradd --create-home --uid 10001 app après les lignes ENV de votre Dockerfile.
  2. Ajoutez USER app après COPY ticket_api ./ticket_api.
  3. Comparez votre fichier avec le Dockerfile terminé dans Exécuter comme utilisateur autre que root. Il doit être identique.
  4. Construisez l’image avec docker build -t ticket-api:1.0.0 .. La construction comporte sept étapes.
  5. Démarrez le conteneur avec docker run -d --rm --name ticket-api -p 127.0.0.1:8000:8000 -e API_KEY=local-dev-key ticket-api:1.0.0.
  6. Exécutez docker exec ticket-api id. La sortie commence par uid=10001(app).
  7. Exécutez docker exec ticket-api touch /app/x. La sortie est Permission denied (permission refusée).
  8. Appelez /health. Il donne {"status":"ok"}.
  9. Arrêtez le conteneur avec docker stop ticket-api.
Afficher la sortie attendue des étapes 6 et 7
uid=10001(app) gid=10001(app) groups=10001(app)
touch: cannot touch '/app/x': Permission denied

Si l’étape 6 indique uid=0(root), l’image n’a pas de ligne USER app, ou vous avez démarré une ancienne image. Construisez-la à nouveau et vérifiez que docker image inspect ticket-api:1.0.0 --format '{{.Config.User}}' affiche app.

À vous de jouer : où l’application peut-elle écrire?

Section intitulée « À vous de jouer : où l’application peut-elle écrire? »

L’application ne peut pas écrire dans /app. Faites une prédiction : peut-elle écrire dans /tmp? Dans /home/app? Vérifiez avec docker exec ticket-api touch /tmp/x et docker exec ticket-api touch /home/app/x, puis expliquez.

Afficher la réponse

Les deux fonctionnent. /tmp est accessible à tous les utilisateurs (ls -ld /tmp affiche drwxrwxrwt). /home/app appartient à app, car useradd --create-home l’a créé pour cet utilisateur (drwx------ 2 app app). L’application peut donc encore écrire des fichiers temporaires, mais pas son propre code. Les fichiers dans /tmp et /home/app sont dans la couche accessible en écriture du conteneur. Ils disparaissent avec le conteneur. Le module 3 montre où placer les données qui doivent persister.

L’exercice du module : construire l’image de l’API de billets et inspecter sa configuration. Ce sont les paramètres que l’image contient en plus de ses fichiers. D’abord, un petit exemple détaillé.

docker image inspect affiche la description complète de l’image en JSON. --format sélectionne un champ avec un gabarit : {{.Config.User}} est l’utilisateur défini par USER.

Terminal
docker image inspect ticket-api:1.0.0 --format '{{.Config.User}}'
app
  1. Construisez l’image à partir du Dockerfile terminé, sous le nom ticket-api:1.0.0, si ce n’est pas déjà fait.
  2. Affichez ces champs avec docker image inspect ticket-api:1.0.0 --format '...' : {{.Config.User}}, {{json .Config.Cmd}}, {{json .Config.ExposedPorts}}, {{.Config.WorkingDir}} et {{json .Config.Env}}.
  3. Pour chaque champ, écrivez une ligne indiquant quelle ligne du Dockerfile l’a défini.
  4. Examinez Env (variables d’environnement). Contient-il une clé ou un mot de passe? Quelles variables viennent de l’image de base?
  5. Exécutez docker history ticket-api:1.0.0. Nommez les couches de 0 octet et expliquez pourquoi leur taille est nulle.
Afficher le corrigé

Une seule commande pour les quatre premiers champs :

Fenêtre de terminal
docker image inspect ticket-api:1.0.0 --format 'User={{.Config.User}} Cmd={{json .Config.Cmd}} ExposedPorts={{json .Config.ExposedPorts}} WorkingDir={{.Config.WorkingDir}}'
User=app Cmd=["fastapi","run","--host","0.0.0.0","--port","8000"] ExposedPorts={"8000/tcp":{}} WorkingDir=/app
Fenêtre de terminal
docker image inspect ticket-api:1.0.0 --format '{{json .Config.Env}}'
["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","PYTHON_VERSION=3.14.8","PYTHON_SHA256=c2215904f02b175596dc49351585104f4bc20341e1c47378b26a2c274360ce73","PYTHONDONTWRITEBYTECODE=1","PYTHONUNBUFFERED=1","PIP_NO_CACHE_DIR=1","PIP_DISABLE_PIP_VERSION_CHECK=1","PIP_ROOT_USER_ACTION=ignore"]
  1. User (utilisateur) vient de USER app; Cmd (commande), de CMD [...]; ExposedPorts (ports exposés), de EXPOSE 8000; WorkingDir (répertoire de travail), de WORKDIR /app. Les cinq dernières variables dans Env viennent des lignes ENV.

  2. Aucune clé et aucun mot de passe. API_KEY n’y figure pas, car vous la fournissez seulement avec -e au démarrage d’un conteneur. PATH, PYTHON_VERSION et PYTHON_SHA256 viennent de l’image de base.

  3. docker history (premières lignes) :

    CMD ["fastapi" "run" "--host" "0.0.0.0" "--p… 0B
    EXPOSE [8000/tcp] 0B
    USER app 0B
    COPY ticket_api ./ticket_api # buildkit 49.2kB
    COPY pyproject.toml . # buildkit 12.3kB
    RUN /bin/sh -c pip install -r requirements-r… 84.1MB
    COPY requirements-run.txt . # buildkit 12.3kB
    WORKDIR /app 8.19kB
    RUN /bin/sh -c useradd --create-home --uid 1… 69.6kB
    ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFER… 0B

    CMD, EXPOSE, USER et ENV font 0 octet. Ils modifient seulement la configuration affichée à l’étape 2, pas les fichiers. Leurs couches sont donc vides.

Vérifiez votre compréhension

Rappel : nommez les six instructions du premier Dockerfile de ticket-api. Pour chacune, dites si elle crée des fichiers dans une couche ou seulement un paramètre.

Afficher la réponse

Quatre instructions créent des fichiers : FROM (les couches de l’image de base), WORKDIR (le dossier), COPY (les fichiers copiés) et RUN (ce que la commande a modifié, comme les paquets installés). EXPOSE (le port documenté) et CMD (la commande de démarrage) écrivent seulement des paramètres, avec des couches de 0 octet. Dans le Dockerfile terminé, ENV et USER définissent aussi seulement des paramètres.

Vérifiez votre compréhension

Prédiction : dans le Dockerfile terminé, vous modifiez requirements-run.txt pour ajouter un paquet. Lesquelles des sept étapes s'exécutent de nouveau, et l'utilisateur app est-il recréé?

Afficher la réponse

Les étapes 1 à 3 (FROM, RUN useradd, WORKDIR) proviennent du cache. L’utilisateur app n’est donc pas recréé. L’étape 4, COPY requirements-run.txt ., s’exécute parce que le fichier a changé. Les étapes 5 à 7 (pip install, COPY pyproject.toml, COPY ticket_api) s’exécutent parce qu’elles viennent après. pip install est l’étape lente : environ 13 secondes ici.

Vérifiez votre compréhension

Vérification de réalisation : ajoutez un commentaire à ticket_api/models.py, reconstruisez ticket-api:1.0.0 et expliquez quelles couches Docker a réutilisées et pourquoi. Ensuite, retirez le commentaire.

Afficher la réponse

La sortie de la construction (abrégée) :

[+] Building 0.4s (12/12) FINISHED docker:desktop-linux
...
=> CACHED [2/7] RUN useradd --create-home --uid 10001 app 0.0s
=> CACHED [3/7] WORKDIR /app 0.0s
=> CACHED [4/7] COPY requirements-run.txt . 0.0s
=> CACHED [5/7] RUN pip install -r requirements-run.txt 0.0s
=> CACHED [6/7] COPY pyproject.toml . 0.0s
=> [7/7] COPY ticket_api ./ticket_api 0.0s

Docker a réutilisé les couches des étapes 2 à 6 : leurs instructions et leurs fichiers d’entrée n’ont pas changé. Leurs couches en cache sont donc encore correctes. L’image de base de l’étape 1 est la même. Seule l’étape 7 s’est exécutée, parce que ticket_api/models.py, un fichier qu’elle copie, a changé. C’est la dernière étape, donc aucune autre ne la suit. Les paramètres (USER, EXPOSE, CMD) sont écrits de nouveau, ce qui ne prend aucun temps. La construction a pris 0,4 seconde plutôt qu’environ 16.

Récapitulatif

Dans ce module, vous avez construit l’image de l’application :

  • Un Dockerfile avec FROM, WORKDIR, COPY, RUN, EXPOSE et CMD sous forme exec, et requirements-run.txt créé dans un conteneur Linux.
  • Une construction rapide : les paquets avant le code, pour reconstruire en moins d’une seconde après une modification du code. Une liste d’autorisation .dockerignore qui envoie trois éléments à la construction.
  • Une petite image de base fiable à version fixe, python:3.14.8-slim-trixie, et des lignes ENV qui maintiennent l’image à 309 Mo.
  • Une image sûre : l’application s’exécute sous app (uid 10001) et ne peut pas modifier son propre code. Aucun secret ne se trouve dans les couches. Un fichier supprimé par une étape ultérieure reste dans l’image.

Réinitialisation. Pour commencer le module 3 dans un état propre :

  1. Listez vos conteneurs avec docker ps -a. Arrêtez et supprimez tout conteneur ticket-api : docker rm -f ticket-api.
  2. Supprimez les images de test : docker image rm hello-image leak-demo:1. Si vous les avez déjà supprimées, Docker indique No such image (aucune image de ce nom).
  3. Supprimez les dossiers ~/projects/hello-image et ~/projects/leak-demo.
  4. Exécutez docker system df pour voir l’espace utilisé par les images, les conteneurs et le cache de construction. N’exécutez pas de commande qui supprime tout : d’autres projets sur votre ordinateur peuvent utiliser le même Docker.
  5. Si votre projet ne fonctionne plus, copiez l’instantané end-of-m02 et reconstruisez ticket-api:1.0.0.

Ensuite, vous vous connecterez au conteneur par des ports. Vous apprendrez pourquoi 127.0.0.1 signifie autre chose dans un conteneur.

Leçon suivante : Ports et accès au service