Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Prochaine révision
Révision précédente
technique:docker:good_practices:init [2020/10/13 14:29] – ↷ Nom de la page changé de technique:docker:good_practices:init à technique:docker:good_practices:multi qduchemitechnique:docker:good_practices:init [2020/10/13 16:03] (Version actuelle) qduchemi
Ligne 1: Ligne 1:
 +{{indexmenu_n>25}}
 +# Utiliser un système d'initialisation
  
 +<bootnote warning>Si vous utilisez un script shell comme entrypoint, la documentation sur [[technique:docker:good_practices:multi|les entrypoints et les processus multiples]] sera utile en premier lieu.</bootnote>
 +
 +## Problématique
 +
 +Parfois, le `Dockerfile` spécifie une commande (`CMD`) qui s'exécute en arrière-plan (comme un démon). Or Docker a besoin que le premier processus qui s'exécute dans le conteneur s'exécute en premier plan, sans quoi il s'éteint.
 +
 +Autre problème, même si la commande s'exécute en premier plan, elle porte le PID 1.
 +
 +<bootnote>Le premier processus d'un conteneur (de PID 1) est souvent référencé comme `init`, en référence [aux systèmes Unix](https://fr.wikipedia.org/wiki/Init).</bootnote>
 +
 +Ce processus est le parent de tous les autres, et doit transmettre les signaux qu'il reçoit à ses enfants (par exemple, un signal de terminaison).
 +
 +<bootnote>
 +Un peu d'explications : un **signal**, au sens Linux, est une sorte de notification asynchrone que l'on peut envoyer à un processus. Il existe de nombreux signaux, par exemple :
 +   * `SIGTERM`, pour demander à un processus de se terminer
 +   * `SIGINT`, envoyé avec un `Ctrl+C`, par exemple
 +   * `SIGKILL`, pour tuer un processus
 +   * `SIGHUP`, souvent utilisé pour recharger la configuration d'un service
 +Ces signaux peuvent être envoyés avec la commande `kill`.
 +</bootnote>
 +
 +Les processus peuvent enregistrer des *handlers*, qui sont exécutés lorsqu'un signal d'un certain type est reçu. Si jamais un processus n'enregistre pas de *handler* pour un signal particulier, le noyau passe au comportement par défaut : tuer le processus.
 +
 +Sauf... pour le processus de PID 1. Dans ce cas, il n'y a pas de comportement par défaut.
 +
 +<bootnote important>Concrètement, lorsque l'on fait un `docker stop`, un `SIGTERM` est envoyé au processus de PID 1. Si celui-ci n'a pas de *handler* pour `SIGTERM`, il ne se passe rien car il n'y a pas de comportement par défaut, et au bout d'un timeout assez long (~30 secondes), Docker envoie un `SIGKILL`.</bootnote>
 +
 +Or, la plupart des applications lancées avec le PID 1 (par exemple Python) n'ont pas de *handlers*.
 +
 +## Solution
 +
 +Compose, depuis la version 3.7, adresse ce problème avec une directive très simple :
 +```yaml
 +services:
 +  exemple:
 +    init: true
 +```
 +
 +Cette directive a pour effet d'exécuter [tini](https://github.com/krallin/tini) en PID 1, puis la commande ou l'entrypoint en tant qu'enfant. 
 +
 +Tini va : 
 +
 +* S'exécuter en premier plan.
 +* S'assurer qu'il n'y a pas de [processus zombie](https://en.wikipedia.org/wiki/Zombie_process), qui peuvent à forcer remplir la table des processus sur l'hôte.
 +* Transmettre les signaux aux enfants : comme ils n'ont pas le PID 1, alors le fonctionnement par défaut (à savoir tuer le processus s'il n'a pas installé de *handler*) fonctionne.
 +* Attendre la terminaison de son enfant pour terminer, même si celui-ci s'exécute en arrière plan.
 +* Terminer avec le code de retour de son enfant, ce qui permet de savoir s'il y a eu une erreur ou non.
 +
 +<bootnote>Un rôle subsidiaire du processus `init`, sur les systèmes Linux, est justement de faire le ménage dans les processus zombie.</bootnote>