Un service Compose sans limite peut avaler toute la mémoire de la machine, et un service qui plante reste arrêté tant que personne ne le relance. Cette leçon s'adresse à un débutant qui sait écrire une stack : vous y posez des limites de mémoire, de CPU et de processus, puis vous choisissez ce que Docker fait quand un conteneur s'arrête.
Vous allez surtout constater, sur des conteneurs qui s'arrêtent de quatre façons différentes, lesquels la politique de redémarrage relance et lesquels elle laisse arrêtés. Vous finissez par le délai de grâce, le temps que Docker laisse à un service pour s'arrêter proprement avant de le tuer.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Limiter la mémoire, le CPU et le nombre de processus d'un service Compose, et vérifier que Docker applique ces limites.
- Reconnaître un conteneur tué faute de mémoire avec
OOMKilled. - Choisir une politique de redémarrage avec
restart. - Distinguer ce que la politique relance (un processus qui meurt de lui-même) de ce qu'elle ne relance pas sur le moment (
docker stop,docker kill). - Redémarrer ou relancer un service à la main.
- Raccourcir ou allonger le délai de grâce avec
stop_grace_period.
Prérequis
Section intitulée « Prérequis »Il vous faut un hôte Linux avec Docker Engine 29.9.2 et son plugin Compose 5.6.1, installés depuis le dépôt officiel comme le montre la leçon d'installation de Docker. Les images sont épinglées par leur empreinte. Les fichiers du noyau lus plus bas (/sys/fs/cgroup/...) sont ceux de cgroup v2, la version que Debian 13 utilise.
Vous savez écrire une stack avec la leçon Décrire une application avec Compose, et lire l'état de santé d'un service avec la leçon précédente, Démarrage, disponibilité et santé. Cette leçon-ci reprend docker compose ps et docker inspect sans les réexpliquer.
Deux leçons voisines détaillent ce que cette page ne fait que résumer. Les quatre politiques de redémarrage de docker run sont dans Lire un échec de démarrage Docker, et les signaux d'arrêt dans Cycle de vie et signaux. Si vous voulez comprendre comment le noyau applique les limites, Brider un conteneur l'explique, et Sous le capot détaille les cgroups ; vous n'en avez pas besoin ici.
Vous travaillez dans un dossier ~/budgets, que la leçon vous fait créer. Son nom devient celui du projet : les conteneurs s'appellent budgets-....
Comment limiter la mémoire, le CPU et les processus d'un service ?
Section intitulée « Comment limiter la mémoire, le CPU et les processus d'un service ? »Avec trois clés au niveau du service : mem_limit pour la mémoire, cpus pour le CPU et pids_limit pour le nombre de processus. Une limite est un plafond dur : le conteneur ne peut pas dépasser la mémoire vive ou le CPU qu'elle fixe, quoi que fasse le programme. Pour la mémoire, le swap (mémoire d'échange sur disque) s'ajoute à ce plafond tant que memswap_limit ne le borne pas, comme le montre la section sur le dépassement de mémoire.
Créez le dossier et un premier compose.yaml avec un seul service, indexeur, qui ne fait qu'attendre.
mkdir -p ~/budgetscd ~/budgetsservices: indexeur: image: alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a command: ["sleep", "infinity"] mem_limit: 48m cpus: 0.25 pids_limit: 20mem_limit: 48m fixe la mémoire maximale à 48 Mio, cpus: 0.25 autorise le quart d'un cœur et pids_limit: 20 autorise vingt processus au plus. Ces valeurs sont petites pour que la démonstration les atteigne vite. Dans une vraie stack, vous les choisissez d'après la consommation relevée du service.
Avant de lancer, demandez à Compose la version résolue du fichier. C'est ce que Docker va réellement recevoir.
docker compose config | grep -E 'mem_limit|cpus|pids_limit' cpus: 0.25 mem_limit: "50331648" pids_limit: 20Regardez que les trois limites sont présentes, et que la mémoire est convertie en octets (48 Mio valent 50 331 648 octets).
Cette commande sert aussi de garde-fou. Une limite écrite au mauvais niveau d'indentation n'est pas ignorée en silence : avec Compose 5.6.1, docker compose config s'arrête avec le code de sortie 1 et un message qui nomme la clé fautive. Par exemple, mem_limit glissée sous deploy donne le message suivant, précédé du mot validating et du chemin du fichier.
services.indexeur.deploy additional properties 'mem_limit' not allowedLisez le chemin avant le mot additional : il désigne l'endroit où la clé a été rangée à tort, ici services.indexeur.deploy. Une clé posée au niveau de services donne services.mem_limit must be a mapping, et une clé qui déborde de sa ligne donne une erreur de syntaxe YAML (did not find expected key). Corrigez l'indentation avant d'aller plus loin.
Y a-t-il une autre façon d'écrire ces limites ?
Section intitulée « Y a-t-il une autre façon d'écrire ces limites ? »Oui : la section deploy.resources.limits. Elle vient de la spécification Compose, que d'autres orchestrateurs lisent aussi, et vous la croiserez dans des fichiers écrits pour eux. Avec Compose 5.6.1 sur Docker installé sur votre machine, mem_limit au niveau du service et deploy.resources.limits.memory sont tous deux appliqués. Les deux écritures suivantes donnent donc la même limite de mémoire.
# Forme courte, au niveau du service mem_limit: 48m
# Forme de la spécification, sous deploy deploy: resources: limits: memory: 48MLa même section accepte aussi cpus: "0.25" et pids: 20 sous limits. Un service écrit ainsi, lancé avec Compose 5.6.1, a donné les mêmes valeurs que la forme courte dans docker inspect (mémoire=50331648 cpu=250000000 processus=20) et dans les fichiers du noyau (50331648, 25000 100000 et 20), que vous apprenez à lire juste après. Une limite mal placée sous deploy est refusée avec un message, comme pour la forme courte.
Choisissez l'une ou l'autre, pas les deux, et gardez la même dans tout le fichier. Cette leçon utilise la forme courte, plus facile à lire. Une clé deploy.resources.reservations réserve un minimum, et un poids relatif comme cpu_shares répartit le CPU quand la machine est chargée : ni l'un ni l'autre ne plafonne le conteneur.
Comment vérifier que Docker applique les limites ?
Section intitulée « Comment vérifier que Docker applique les limites ? »En lisant deux endroits : la configuration que Docker a enregistrée, puis ce que le noyau applique réellement. Le noyau Linux applique les limites avec ses cgroups (groupes de contrôle) et les expose dans le conteneur sous forme de fichiers, dans /sys/fs/cgroup/. Lancez le service et comparez.
docker compose up -d indexeurdocker inspect -f 'mémoire={{.HostConfig.Memory}} cpu={{.HostConfig.NanoCpus}} processus={{.HostConfig.PidsLimit}}' budgets-indexeur-1docker compose exec indexeur sh -c 'cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max /sys/fs/cgroup/pids.max'mémoire=50331648 cpu=250000000 processus=205033164825000 10000020Regardez la première ligne : c'est ce que Docker a retenu (NanoCpus compte en milliardièmes de cœur, donc 250 000 000 pour un quart de cœur). Regardez ensuite les trois suivantes, lues dans le conteneur : memory.max donne la mémoire, pids.max les processus, et cpu.max contient deux nombres, le temps de calcul autorisé (25 000 microsecondes) sur chaque période de 100 000. Les deux lectures doivent dire la même chose. Si seule la première est juste, Docker n'a pas appliqué la limite.
Une limite de CPU ne se constate qu'en chargeant le conteneur. Le fichier cpu.stat compte les fois où le noyau a retenu le conteneur parce qu'il dépassait son quota, dans nr_throttled. Lisez ce compteur, faites tourner une boucle active pendant trois secondes, puis relisez-le.
docker compose exec indexeur sh -c 'grep nr_throttled /sys/fs/cgroup/cpu.stat; timeout 3 sh -c "while :; do :; done"; grep nr_throttled /sys/fs/cgroup/cpu.stat'nr_throttled 0Terminatednr_throttled 31Regardez la différence entre les deux compteurs : elle prouve que le quota freine bien le conteneur. Le nombre exact change à chaque essai (0 puis 31 ici, 1 puis 31 à l'essai suivant), c'est la hausse qui compte. Le mot Terminated vient de timeout, qui a arrêté la boucle comme prévu.
Que se passe-t-il quand un service atteint sa limite de processus ?
Section intitulée « Que se passe-t-il quand un service atteint sa limite de processus ? »Le noyau refuse de créer le processus suivant : le programme qui voulait le lancer reçoit une erreur, et rien n'est tué. Pour le voir, demandez au conteneur de lancer trente petits processus d'affilée alors qu'il n'en admet que vingt.
docker compose exec indexeur sh -c 'for i in $(seq 1 30); do sleep 5 & done; wait'sh: can't fork: Resource temporarily unavailableLe message vient du shell du conteneur, qui n'a pas pu créer un nouveau processus et a abandonné sa boucle. Le piège vient ensuite : le conteneur reste inutilisable. Sept secondes après l'essai, docker compose exec indexeur echo ok échouait encore, avec OCI runtime exec failed ... unable to spawn stage-1: Resource temporarily unavailable. Vous ne pouvez plus ouvrir de shell dedans tant qu'il n'est pas recréé, ce que vous ferez dans un instant.
Retenez-en ceci : une limite de processus trop basse ne se contente pas de refuser un lancement, elle peut aussi vous empêcher de vous connecter au conteneur pour comprendre ce qui s'est passé.
Qu'advient-il d'une limite posée avec docker update ?
Section intitulée « Qu'advient-il d'une limite posée avec docker update ? »Avant de recréer le conteneur, posez une limite à la main avec docker update, puis lisez-la. Les deux options --memory et --memory-swap reçoivent ici la même valeur.
docker update --memory 96m --memory-swap 96m budgets-indexeur-1docker inspect -f 'mémoire={{.HostConfig.Memory}} cpu={{.HostConfig.NanoCpus}} processus={{.HostConfig.PidsLimit}}' budgets-indexeur-1budgets-indexeur-1mémoire=100663296 cpu=250000000 processus=20Docker a bien pris la nouvelle valeur (96 Mio valent 100 663 296 octets). Le conteneur ne répond plus à exec depuis l'essai précédent, donc memory.max ne se relit pas ici ; sur un conteneur sain, il a rendu 100663296. Recréez maintenant le conteneur et lisez de nouveau les limites.
docker compose up -d --force-recreate indexeurdocker inspect -f 'mémoire={{.HostConfig.Memory}} cpu={{.HostConfig.NanoCpus}} processus={{.HostConfig.PidsLimit}}' budgets-indexeur-1mémoire=50331648 cpu=250000000 processus=20Les limites écrites dans compose.yaml sont revenues, parce que Compose les applique à chaque création : les 48 Mio ont remplacé les 96 Mio posés à la main. Cette recréation prend une dizaine de secondes (10,4 s mesurées) : sleep infinity ignore la demande d'arrêt (il est le processus 1 du conteneur, sans gestionnaire de signal, comme l'explique la section sur kill -9 1), et Docker attend la fin du délai de grâce, expliqué dans la section « Pourquoi un arrêt peut-il durer plus longtemps que prévu ? », avant de le tuer.
Une limite posée avec docker update ne survit pas à une recréation, et le fichier ne la contient pas. Si elle doit durer, écrivez-la dans compose.yaml.
Que se passe-t-il quand un service dépasse sa limite de mémoire ?
Section intitulée « Que se passe-t-il quand un service dépasse sa limite de mémoire ? »Le noyau tue le processus qui consomme trop, et Docker note la cause dans l'état du conteneur : OOMKilled vaut alors true. OOM veut dire out of memory, plus de mémoire. Seule la limite de mémoire fait tuer le service par le noyau : un dépassement de CPU le ralentit seulement, et un dépassement de processus fait échouer la création du suivant.
Ajoutez un service qui consomme de la mémoire sans jamais s'arrêter : la commande tail /dev/zero lit un flux sans fin de zéros, sans saut de ligne, et garde tout en mémoire. Le service a une limite de 32 Mio, et memswap_limit égal à mem_limit interdit d'utiliser le swap, la mémoire d'échange sur disque, pour contourner la limite. Sa politique de redémarrage on-failure:2 est expliquée plus bas.
gourmand: image: alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a command: ["tail", "/dev/zero"] mem_limit: 32m memswap_limit: 32m restart: "on-failure:2"Lancez-le, laissez-lui quelques secondes, puis lisez son état.
docker compose up -d gourmandsleep 8docker inspect -f 'statut={{.State.Status}} code={{.State.ExitCode}} oom={{.State.OOMKilled}} relances={{.RestartCount}}' budgets-gourmand-1statut=exited code=137 oom=true relances=2Regardez oom=true : le conteneur est mort faute de mémoire, pas parce qu'on l'a arrêté. Le code 137 seul ne suffirait pas à le dire, car c'est aussi celui d'un conteneur tué par docker kill (le programme est alors tué par le signal 9, et 128 + 9 = 137). OOMKilled=true le signale. Notez aussi relances=2 : Docker a relancé le service deux fois, puis a abandonné. Le manque de mémoire est donc une panne comme une autre pour la politique de redémarrage, comme le montre la section suivante.
Comment choisir la politique de redémarrage d'un service ?
Section intitulée « Comment choisir la politique de redémarrage d'un service ? »Avec la clé restart du service. Elle prend les mêmes quatre valeurs que l'option --restart de docker run, détaillées dans la leçon de dépannage citée plus haut.
| Valeur | Docker relance le conteneur… |
|---|---|
"no" (défaut) | jamais |
on-failure[:n] | quand le programme sort avec un code non nul, n fois au plus ; au redémarrage de Docker, seulement si la dernière sortie était en erreur (137 compris), jamais après une sortie en 0 |
always | à chaque sortie, y compris le code 0 ; un conteneur que vous aviez arrêté repart quand Docker redémarre |
unless-stopped | comme always, mais un conteneur que vous avez arrêté vous-même reste arrêté, même quand Docker redémarre |
Pour un service qui doit tourner en permanence, unless-stopped convient bien : il remet le service en route après une panne ou un redémarrage de la machine, sans le relancer si vous l'avez arrêté exprès. Les démonstrations qui suivent l'utilisent.
Écrivez "no" entre guillemets. Avec Compose 5.6.1, restart: no sans guillemets est accepté sans aucun message : docker compose config l'affiche en restart: 'no' et docker inspect rend la politique no. Les guillemets restent la bonne habitude, car en YAML 1.1 le mot no désigne le booléen faux, et d'autres outils le liraient ainsi.
Qu'est-ce qu'une politique relance, et qu'est-ce qu'elle laisse arrêté ?
Section intitulée « Qu'est-ce qu'une politique relance, et qu'est-ce qu'elle laisse arrêté ? »Une politique relance un processus qui meurt de lui-même, et elle ne touche pas à un conteneur que vous arrêtez ou tuez par une commande. Pour le voir, il faut donc deux services : un qui plante tout seul, et un qui vit jusqu'à ce que vous le tuiez. Ajoutez-les au fichier, tous deux sous unless-stopped.
fragile: image: alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a command: ["sh", "-c", "echo départ; sleep 4; exit 1"] restart: unless-stopped
veilleur: image: alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a command: ["sleep", "infinity"] restart: unless-stopped stop_grace_period: 3sLe service fragile affiche un message, attend quatre secondes, puis sort avec le code 1. Le service veilleur attend indéfiniment ; sa ligne stop_grace_period: 3s ne sert pas encore, la section « Pourquoi un arrêt peut-il durer plus longtemps que prévu ? » l'explique. Lancez les deux, attendez une quinzaine de secondes et lisez leurs compteurs.
docker compose up -d fragile veilleursleep 15docker inspect -f '{{.Name}} statut={{.State.Status}} relances={{.RestartCount}}' budgets-fragile-1 budgets-veilleur-1/budgets-fragile-1 statut=running relances=3/budgets-veilleur-1 statut=running relances=0Regardez relances pour fragile : il vaut 3 ici, et 2 ou plus en général, parce que chaque sortie avec le code 1 a été suivie d'un redémarrage. Le nombre exact dépend du moment de la lecture. Le statut peut aussi afficher restarting si vous lisez entre deux essais.
Tuez maintenant veilleur avec docker kill, qui envoie SIGKILL aussitôt, puis relisez son état. La commande docker compose ps -q veilleur donne l'identifiant du conteneur du service, que docker kill attend.
docker kill $(docker compose ps -q veilleur)sleep 3docker inspect -f 'statut={{.State.Status}} code={{.State.ExitCode}} relances={{.RestartCount}}' budgets-veilleur-1b0615a52e79a1e37cc2c7c694c31e85d5fd6c29ccbfcfeab2d6fd14d748986fcstatut=exited code=137 relances=0La première ligne est l'identifiant que docker kill affiche, il change à chaque essai. Le conteneur est resté arrêté (exited), avec le code 137, et son compteur n'a pas bougé : docker kill ne déclenche pas la politique de redémarrage sur le moment, même unless-stopped. Docker considère que vous avez voulu l'arrêter. Le tableau compare les quatre façons d'arrêter un conteneur de cette leçon. La première ligne a été mesurée sous unless-stopped, la deuxième sous on-failure:2, et les deux dernières sous unless-stopped.
| Ce qui arrête le conteneur | Relancé par la politique ? |
|---|---|
Le programme sort de lui-même avec une erreur (fragile) | oui |
Le noyau le tue faute de mémoire (gourmand) | oui, dans la limite fixée par on-failure:n |
docker stop ou docker compose stop | non, sur le moment |
docker kill | non, sur le moment |
Ce « non » vaut sur le moment. Au redémarrage de Docker, always relance un conteneur arrêté à la main, unless-stopped le laisse arrêté, et on-failure relance un conteneur dont la dernière sortie était en erreur, donc aussi un conteneur tué (code 137), mais pas un conteneur sorti en 0. Le détail est dans la leçon Lire un échec de démarrage.
Retenez la conséquence : tuer un conteneur avec docker kill ne simule pas une panne. Pour tester une politique de redémarrage, il faut un programme qui plante de lui-même, comme fragile.
Peut-on simuler une panne en tuant le processus principal depuis l'intérieur ?
Section intitulée « Peut-on simuler une panne en tuant le processus principal depuis l'intérieur ? »Pas avec kill -9 1. Le processus principal du conteneur porte le numéro 1 dans son propre espace de processus, et le noyau le protège des signaux envoyés depuis cet espace : la commande ne fait rien. Essayez sur veilleur, après l'avoir relancé.
docker compose start veilleurdocker compose exec veilleur kill -9 1docker inspect -f 'statut={{.State.Status}} relances={{.RestartCount}}' budgets-veilleur-1statut=running relances=0Le conteneur tourne toujours et le compteur n'a pas bougé : le signal a été ignoré. Ce traitement particulier du processus 1 est le même qui lui fait ignorer SIGTERM, décrit dans Cycle de vie et signaux. Retenez que ce geste, souvent proposé pour « simuler un crash », ne prouve rien.
Comment redémarrer un service à la main ?
Section intitulée « Comment redémarrer un service à la main ? »Avec docker compose restart <service> pour un service qui tourne, et docker compose start <service> pour un service arrêté. La commande restart arrête le conteneur puis le redémarre, en respectant le délai de grâce expliqué dans la section « Pourquoi un arrêt peut-il durer plus longtemps que prévu ? ». Elle ne relit pas le fichier : un changement de limite ou de politique n'est pris en compte que par docker compose up -d, qui recrée le conteneur.
Comment constater que restart ne relit pas le fichier ?
Section intitulée « Comment constater que restart ne relit pas le fichier ? »Prenez un réglage facile à lire, le délai de grâce de veilleur. Changez-le dans le fichier (de 3s à 5s), puis lisez le délai que le conteneur a retenu après un restart, puis après un up -d. Remettez ensuite 3s. Les lignes de progression de Compose ne sont pas reproduites.
sed -i 's/stop_grace_period: 3s/stop_grace_period: 5s/' compose.yamldocker compose restart veilleurdocker inspect -f 'délai={{.Config.StopTimeout}}' budgets-veilleur-1docker compose up -d veilleurdocker inspect -f 'délai={{.Config.StopTimeout}}' budgets-veilleur-1sed -i 's/stop_grace_period: 5s/stop_grace_period: 3s/' compose.yamldocker compose up -d veilleurdélai=3délai=5Après restart, le conteneur garde son ancien délai de 3 secondes ; seul up -d le recrée avec les 5 secondes du fichier. La dernière commande remet le délai à 3 secondes, valeur dont la section « Pourquoi un arrêt peut-il durer plus longtemps que prévu ? » a besoin.
Pourquoi docker compose ps -q ne rend-il rien après un arrêt ?
Section intitulée « Pourquoi docker compose ps -q ne rend-il rien après un arrêt ? »Les scripts retrouvent l'identifiant d'un conteneur avec docker compose ps -q <service>. Ce que cette commande rend dépend de l'état du service : comparez-la avec et sans l'option -a, avant et après un arrêt.
docker compose restart veilleurdocker compose ps -q veilleurdocker compose stop veilleurdocker compose ps -q veilleurdocker compose ps -aq veilleur Container budgets-veilleur-1 Restarting Container budgets-veilleur-1 Started537690deee963829b589a652f700437e7837fcb860dad631a78d988bc357272f[+] stop 1/1 ✔ Container budgets-veilleur-1 Stopped 3.1s537690deee963829b589a652f700437e7837fcb860dad631a78d988bc357272fRegardez les identifiants. Le premier ps -q, lancé alors que le service tourne, rend l'identifiant du conteneur. Le second, lancé après stop, ne rend rien : l'option -q ne liste que les conteneurs en cours d'exécution, et l'absence de ligne n'est pas un oubli. L'option -aq ajoute les conteneurs arrêtés et retrouve l'identifiant. Si la sortie de votre script devient soudain vide, vérifiez d'abord que le service n'est pas simplement arrêté.
Pourquoi un arrêt peut-il durer plus longtemps que prévu ?
Section intitulée « Pourquoi un arrêt peut-il durer plus longtemps que prévu ? »Parce que Docker laisse un délai de grâce au programme avant de le tuer. À l'arrêt, il envoie d'abord un signal de fin (SIGTERM), un message qui demande au programme de s'arrêter, puis il attend. Si le conteneur tourne encore à l'issue du délai, il envoie SIGKILL, un signal que le programme ne peut pas refuser. Le délai par défaut est de dix secondes. Le mécanisme complet, avec les cas où le programme ignore SIGTERM, est dans la leçon Cycle de vie et signaux.
Dans Compose, la clé stop_grace_period règle ce délai pour un service. Le service veilleur ignore SIGTERM : son processus principal, sleep, est le processus 1 du conteneur et n'a aucun gestionnaire pour ce signal. Son délai est réglé à 3 secondes. Mesurez la durée d'un arrêt.
docker compose start veilleurtime docker compose stop veilleurdocker inspect -f 'code={{.State.ExitCode}}' budgets-veilleur-1[+] stop 1/1 ✔ Container budgets-veilleur-1 Stopped 3.2s
real 0m3.237scode=137Lancez start d'abord : sur un conteneur déjà arrêté, stop rend la main aussitôt (0,06 s mesurée) et ne démontre rien. Regardez la durée : trois secondes et quelques, c'est le délai réglé, et non les dix du défaut. Le code 137 confirme que Docker a dû envoyer SIGKILL, faute de réponse à SIGTERM. Un délai trop court expose un service qui a besoin de vider ses données, comme une base, à être tué en plein travail. Un délai trop long retarde chaque arrêt et chaque redéploiement.
Que faire quand ces réglages ne se comportent pas comme prévu ?
Section intitulée « Que faire quand ces réglages ne se comportent pas comme prévu ? »Partez de ce que vous voyez, puis vérifiez la configuration que Docker a vraiment reçue avant de modifier le fichier.
| Ce que vous observez | Cause probable | Ce que vous faites |
|---|---|---|
docker compose config ou up s'arrête sur additional properties '...' not allowed ou must be a mapping | La clé est au mauvais niveau d'indentation | Lisez le chemin du message, replacez la clé sous le service (ou sous deploy.resources.limits), puis relancez docker compose config |
docker compose exec répond unable to spawn stage-1: Resource temporarily unavailable | La limite de processus est atteinte, des zombies restent comptés | Lancez docker compose up -d --force-recreate <service>, et relevez la limite si elle est trop basse |
| Le conteneur sort avec le code 137 | Tué par SIGKILL : manque de mémoire, docker kill ou délai de grâce dépassé | Lisez .State.OOMKilled : true signale un OOM ; false ne l'exclut pas (un 137 sans OOMKilled a été mesuré une fois sur cinq sur Docker 29.9.2). Sans OOMKilled ni événement oom dans docker events, on ne peut pas conclure ; une consommation proche du plafond relevée avant l'arrêt rend l'OOM plausible, comme le montre la leçon sur les plafonds mémoire et de tâches |
Le conteneur consomme plus que mem_limit sans être tué, et OOMKilled reste à false | Sans memswap_limit, il peut utiliser du swap en plus de sa mémoire, si l'hôte en a | Ajoutez memswap_limit avec la même valeur que mem_limit |
Un service tué avec docker kill ne redémarre pas | Comportement normal : une commande d'arrêt ne relance rien sur le moment | Faites planter le programme lui-même pour tester la politique |
kill -9 1 dans le conteneur n'a aucun effet | Le noyau protège le processus numéro 1 | Utilisez un programme qui sort de lui-même ; docker kill depuis l'hôte tue le conteneur mais ne déclenche pas la politique sur le moment |
docker compose ps -q ne rend rien | Le service est arrêté, -q ne liste que les conteneurs qui tournent | Ajoutez -a : docker compose ps -aq <service> |
Un changement de restart ou de limite n'a pas d'effet après docker compose restart | restart ne relit pas le fichier | Lancez docker compose up -d |
| L'arrêt dure dix secondes, puis code 137 | Le programme ignore SIGTERM, le délai par défaut s'écoule | Corrigez la gestion du signal, ou réglez stop_grace_period |
Comment tout nettoyer après la démonstration ?
Section intitulée « Comment tout nettoyer après la démonstration ? »La démonstration laisse quatre conteneurs, un réseau, une image et le dossier ~/budgets. La commande docker compose down supprime les conteneurs et le réseau, y compris ceux qui sont arrêtés.
cd ~/budgetsdocker compose downdocker rmi alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9acd ~ && rm -r budgetsAprès cela, docker ps -a ne montre plus de conteneur budgets-..., et ls ~/budgets répond que le dossier n'existe plus.
Mettre en pratique
Section intitulée « Mettre en pratique »Le lab vous donne un hôte avec Docker et l'image Alpine épinglée, sans aucune charge. Vous écrivez le compose.yaml d'un seul service worker qui attend, avec des limites de mémoire, de CPU et de processus dont l'énoncé fixe les valeurs. Le service ne doit avoir ni montage, ni capacité en plus, ni privilège, et le nom du projet est imposé avec l'option -p. Le CPU doit être un quota dur, pas un poids relatif.
La validation compare ce que Docker a enregistré (HostConfig) avec ce que le noyau applique dans le conteneur (memory.max, cpu.max, pids.max). Elle lance ensuite une boucle active de trois secondes et exige que le compteur de bridage du CPU augmente, puis elle recrée le service de force et relit les trois limites. Elle ne vérifie ni le manque de mémoire ni une panne : ces deux cas restent des démonstrations de la leçon.
À retenir
Section intitulée « À retenir »-
Les clés
mem_limit,cpusetpids_limitposent des plafonds durs sur un service.docker compose configmontre ce que Docker va recevoir, et refuse avec un message une limite écrite au mauvais niveau. -
Une limite se vérifie à deux endroits :
docker inspect(ce que Docker a enregistré) et les fichiersmemory.max,cpu.maxetpids.maxdans le conteneur (ce que le noyau applique). La limite de CPU se prouve par la hausse denr_throttleddanscpu.stat. -
Seule la limite de mémoire fait tuer le service par le noyau.
OOMKilledàtruesignale un OOM ; àfalse, il n'est pas exclu. -
Une limite de processus trop basse ne tue rien, mais elle peut rendre le conteneur inutilisable (
docker compose execéchoue) jusqu'à sa recréation. -
Les limites écrites dans
compose.yamlsurvivent à une recréation. Une limite posée avecdocker updaten'y survit pas. -
La clé
restartrelance un programme qui meurt de lui-même.docker stopetdocker killne la déclenchent pas sur le moment (au redémarrage de Docker, le résultat dépend de la politique), etkill -9 1depuis l'intérieur ne fait rien : seul un programme qui plante tout seul démontre une politique. -
docker compose ps -qne liste que les services qui tournent,-aqinclut les arrêtés.docker compose restartne relit pas le fichier,docker compose up -doui. -
À l'arrêt, Docker envoie SIGTERM puis, après
stop_grace_period(dix secondes par défaut), SIGKILL. Réglez ce délai selon le temps dont le service a besoin pour s'arrêter proprement.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Développement et déploiement : variantes explicites
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications