Dumb-Init est un superviseur de processus simple et un système INIT conçu pour fonctionner comme PID 1 dans des environnements de conteneurs minimaux (tels que Docker). Il est déployé comme un petit binaire lié à la statique écrite en C.
Les conteneurs légers ont popularisé l'idée d'exécuter un seul processus ou service sans systèmes d'initiation normaux comme SystemD ou Sysvinit. Cependant, l'omission d'un système INIT conduit souvent à une manipulation incorrecte des processus et des signaux, et peut entraîner des problèmes tels que des conteneurs qui ne peuvent pas être arrêtés gracieusement ou des conteneurs qui fuisent qui auraient dû être détruits.
dumb-init vous permet de préfixer simplement votre commande avec dumb-init . Il agit comme PID 1 et engendre immédiatement votre commande en tant que processus d'enfant, en prenant soin de gérer correctement et de transférer les signaux lorsqu'ils sont reçus.
Normalement, lorsque vous lancez un conteneur Docker, le processus que vous exécutez devient PID 1, ce qui lui donne les bizarreries et les responsabilités qui accompagnent le système INIT pour le conteneur.
Il y a deux problèmes communs que cela présente:
Dans la plupart des cas, les signaux ne seront pas gérés correctement.
Le noyau Linux applique une manipulation spéciale du signal aux processus qui s'exécutent comme PID 1.
Lorsque les processus sont envoyés un signal sur un système Linux normal, le noyau vérifiera d'abord tous les gestionnaires personnalisés que le processus s'est inscrit à ce signal et se repliera autrement à un comportement par défaut (par exemple, tuant le processus sur SIGTERM ).
Cependant, si le processus recevant le signal est PID 1, il reçoit un traitement spécial par le noyau; S'il n'a pas enregistré de gestionnaire pour le signal, le noyau ne retombera pas à un comportement par défaut et rien ne se passe. En d'autres termes, si votre processus ne gère pas explicitement ces signaux, l'envoi SIGTERM n'aura aucun effet.
Un exemple courant est les travaux CI qui font docker run my-container script : l'envoi SIGTERM au processus docker run tuera généralement la commande docker run , mais laissera le conteneur s'exécuter en arrière-plan.
Les processus de zombies orphelins ne sont pas correctement récoltés.
Un processus devient un zombie lorsqu'il sort et reste un zombie jusqu'à ce que son parent appelle une variation de l'appel du système wait() . Il reste dans la table de processus comme un processus "disparu". En règle générale, un processus parent appellera wait() immédiatement et évitera les zombies de longue durée.
Si un parent sort devant son enfant, l'enfant est "orphelin" et est ravi dans le PID 1. Le système d'initial est donc responsable de wait() -ing sur les processus de zombies orphelins.
Bien sûr, la plupart des processus n'attendront pas wait() sur des processus aléatoires qui s'y attachent, donc les conteneurs se terminent souvent avec des dizaines de zombies enracinés au PID 1.
dumb-init dumb-init fonctionne sous le nom de PID 1, agissant comme un système d'initial simple. Il lance un seul processus, puis les proxies ont tous reçu des signaux à une session enracinée à ce processus enfant.
Étant donné que votre processus réel n'est plus PID 1, lorsqu'il reçoit des signaux de dumb-init , les gestionnaires de signaux par défaut seront appliqués et votre processus se comportera comme vous vous en doutez. Si votre processus décède, dumb-init mourra également, en prenant soin de nettoyer tout autre processus qui pourrait rester.
Dans son mode par défaut, dumb-init établit une session enracinée à l'enfant et envoie des signaux à l'ensemble du groupe de processus. Ceci est utile si vous avez un enfant mal abouti (comme un script shell) qui ne signalera normalement pas à ses enfants avant de mourir.
Cela peut en fait être utile en dehors des conteneurs Docker dans des superviseurs de processus réguliers comme Daemontools ou SuperVisord pour superviser les scripts Shell. Normalement, un signal comme SIGTERM reçu par un shell n'est pas transmis aux sous-processus; Au lieu de cela, seul le processus de shell meurt. Avec Dumb-Init, vous pouvez simplement écrire des scripts de coquille avec Dumb-Init dans le shebang:
#!/usr/bin/dumb-init /bin/sh
my-web-server & # launch a process in the background
my-other-server # launch another process in the foreground
Habituellement, un SIGTERM envoyé à la coquille tuerait la coquille mais laisserait ces processus en cours d'exécution (à la fois l'arrière-plan et le premier plan!). Avec Dumb-init, vos sous-processus recevront les mêmes signaux que votre coquille.
Si vous souhaitez que les signaux soient envoyés uniquement à l'enfant direct, vous pouvez exécuter l'argument --single-child ou définir la variable d'environnement DUMB_INIT_SETSID=0 lors de l'exécution dumb-init . Dans ce mode, Dumb-Init est complètement transparent; Vous pouvez même filer plusieurs filet ensemble (comme dumb-init dumb-init echo 'oh, hi' ).
Dumb-init permet de réécrire des signaux entrants avant de les proxier. Ceci est utile dans les cas où vous avez un superviseur Docker (comme Mesos ou Kubernetes) qui envoie toujours un signal standard (par exemple Sigterm). Certaines applications nécessitent un signal d'arrêt différent pour effectuer un nettoyage gracieux.
Par exemple, pour réécrire le signal SIGTERM (numéro 15) à Sigquit (numéro 3), il suffit d'ajouter --rewrite 15:3 sur la ligne de commande.
Pour supprimer entièrement un signal, vous pouvez le réécrire au numéro spécial 0 .
Lors de l'exécution en mode setSID, il n'est pas suffisant pour transmettre SIGTSTP / SIGTTIN / SIGTTOU dans la plupart des cas, car si le processus n'a pas ajouté de gestionnaire de signaux personnalisé pour ces signaux, alors le noyau n'appliquera pas de comportement de traitement de signal par défaut (qui serait suspendu le processus) car il est membre d'un groupe de processus orphelin. Pour cette raison, nous définissons les réécritures par défaut sur SIGSTOP à partir de ces trois signaux. Vous pouvez vous retirer de ce comportement en réécrivant les signaux à leurs valeurs d'origine, si vous le souhaitez.
Une mise en garde avec cette fonctionnalité: pour les signaux de contrôle du travail ( SIGTSTP , SIGTTIN , SIGTTOU ), Dumb-Init se suspendra toujours après avoir reçu le signal, même si vous le réécrivez à autre chose.
Vous avez quelques options pour utiliser dumb-init :
De nombreuses distributions Linux populaires (y compris Debian (depuis stretch ) et Debian Derivatives telles que Ubuntu (depuis bionic )) contiennent désormais des forfaits stupides dans leurs référentiels officiels.
Sur les distributions basées sur Debian, vous pouvez exécuter apt install dumb-init pour installer Dumb-Init, tout comme vous installez n'importe quel autre package.
Remarque: La plupart des versions fournies par la distribution de Dumb-Init ne sont pas liées à la statique, contrairement aux versions que nous fournissons (voir les autres options ci-dessous). Ceci est normalement parfaitement bien, mais signifie que ces versions de Dumb-Init ne fonctionneront généralement pas lorsqu'ils sont copiés sur d'autres distros Linux, contrairement aux versions liées statiquement que nous fournissons.
Si vous avez un serveur APT interne, le téléchargement du .deb sur votre serveur est le moyen recommandé d'utiliser dumb-init . Dans vos dockerfiles, vous pouvez simplement apt install dumb-init et il sera disponible.
Les packages Debian sont disponibles dans l'onglet GitHub Releases, ou vous pouvez exécuter make builddeb vous-même.
.deb (Debian / Ubuntu) Si vous n'avez pas de serveur APT interne, vous pouvez utiliser dpkg -i pour installer le package .deb . Vous pouvez choisir la façon dont vous obtenez le .deb sur votre conteneur (montant un répertoire ou wget -It, il y a quelques options).
Une possibilité est avec les commandes suivantes dans votre dockerfile:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.debÉtant donné que Dumb-init est libéré en tant que binaire lié à la statique, vous pouvez généralement le placer dans vos images. Voici un exemple de le fait dans un dockerfile:
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-init Bien que dumb-init soit entièrement écrit en C, nous fournissons également un package Python qui compile et installe le binaire. Il peut être installé à partir de PYPI à l'aide de pip . Vous voudrez d'abord installer un compilateur C (sur Debian / Ubuntu, apt-get install gcc est suffisant), puis il suffit pip install dumb-init .
À partir de 1.2.0, l'emballage de PYPI est disponible en tant qu'archive de roues prédéfinie et n'a pas besoin d'être compilé sur les distributions Linux communes.
Une fois installé dans votre conteneur Docker, préfixez simplement vos commandes avec dumb-init (et assurez-vous d'utiliser la syntaxe JSON recommandée).
Dans un dockerfile, c'est une bonne pratique d'utiliser Dumb-Init comme point d'entrée de votre conteneur. Un "point d'entrée" est une commande partielle qui est apparentée à votre instruction CMD , ce qui en fait un excellent ajustement pour Dumb-Init:
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD [ "/my/script" , "--with" , "--args" ] Si vous déclarez un point d'entrée dans une image de base, toutes les images qui en descendent n'ont pas besoin de déclarer également stupide. Ils peuvent simplement définir un CMD comme d'habitude.
Pour une utilisation ponctuelle interactive, vous pouvez simplement le prétendre manuellement:
$ docker run my_container dumb-init python -c 'while True: pass'
L'exécution de cette même commande sans dumb-init entraînerait une fin de stopage du conteneur sans SIGKILL , mais avec dumb-init , vous pouvez l'envoyer des signaux plus humains comme SIGTERM .
Il est important que vous utilisiez la syntaxe JSON pour CMD et ENTRYPOINT . Sinon, Docker invoque un shell pour exécuter votre commande, ce qui donne le shell en tant que pid 1 au lieu de stupide.
Souvent, les conteneurs veulent effectuer un travail avant le démarrage qui ne peut pas être fait pendant la construction. Par exemple, vous souhaiterez peut-être l'emporter certains fichiers de configuration en fonction des variables d'environnement.
La meilleure façon d'intégrer cela avec Dumb-Init est comme ceci:
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
CMD [ "bash" , "-c" , "do-some-pre-start-thing && exec my-server" ]En utilisant toujours Dumb-Init comme point d'entrée, vous avez toujours un système d'initial approprié en place.
La partie exec de la commande bash est importante car elle remplace le processus bash par votre serveur, de sorte que le shell n'existe que momentanément au début.
La construction du binaire stupide nécessite un compilateur de travail et des en-têtes LIBC et par défaut à GLIBC.
$ make
Dumb-init, compilé statique, est supérieur à 700 Ko en raison de GLIBC, mais MUSL est maintenant une option. Sur Debian / Ubuntu apt-get install musl-tools pour installer la source et les emballages, puis juste:
$ CC=musl-gcc make
Lorsqu'il est compilé statiquement avec MUSL, la taille binaire est d'environ 20 Ko.
Nous utilisons les conventions standard de Debian pour spécifier les dépendances de construction (consultez debian/control ). Un moyen facile de commencer est d' apt-get install build-essential devscripts equivs , puis sudo mk-build-deps -i --remove pour installer automatiquement toutes les dépendances de construction manquantes. Vous pouvez ensuite utiliser make builddeb pour construire des packages Debian stupides.
Si vous préférez une construction de package Debian automatisée à l'aide de Docker, exécutez simplement make builddeb-docker . C'est plus facile, mais vous oblige à faire fonctionner Docker sur votre machine.