Speak about tini
This commit is contained in:
parent
8a84f7a527
commit
f46ed6edd1
@ -3,6 +3,7 @@ include ../pandoc-opts.mk
|
||||
SOURCES = tutorial.md \
|
||||
registry.md \
|
||||
../4/filesystem.md ../4/filesystem-ex.md ../4/filesystem-more.md \
|
||||
tini.md \
|
||||
oci.md \
|
||||
runc.md \
|
||||
linuxkit.md linuxkit-content.md \
|
||||
|
119
tutorial/docker-internals/tini.md
Normal file
119
tutorial/docker-internals/tini.md
Normal file
@ -0,0 +1,119 @@
|
||||
\newpage
|
||||
|
||||
De l'importance d'avoir un système d'init dans son conteneur
|
||||
=================
|
||||
|
||||
Dans la dure vie d'un processus, créé par son processus parent (`ppid`),
|
||||
celui-ci gardera son identifiant `pid` durant toute la durée de son
|
||||
exécution. Puis, lorsqu'il aura terminé, celui-ci sera passé dans un statut
|
||||
« zombie », en attendant que son parent direct récupère son code de retour
|
||||
(grâce à l'appel système `wait(2)`).
|
||||
|
||||
|
||||
## Le rôle d'`init`
|
||||
|
||||
Parmi ses attributions, `init`, le PID 1 de notre système, est le processus qui
|
||||
récupère les processus orphelins du système. Lorsque le parent direct d'un
|
||||
processus meurt, ses fils sont reparenté sous le processus `init` et ils
|
||||
obtiennent alors comme `ppid` 1. Ils ne conservent pas le PID de leur défunt
|
||||
parent.
|
||||
|
||||
:::: {.more}
|
||||
|
||||
Depuis les noyaux 3.4, n'importe quel processus peut se déclarer *child
|
||||
subreaper* et récupérer tous les orphelins dans sa descendance de processus.
|
||||
|
||||
Pour se déclarer *child subreaper*, un processus va utiliser `prctl(2)` avec
|
||||
l'argument `PR_SET_CHILD_SUBREAPER`.
|
||||
|
||||
:::::
|
||||
|
||||
Docker procure une isolation, notamment au travers du *namespace* PID : les
|
||||
processus faisant parti du même *namespace* ne voient seulement qu'une partie
|
||||
de l'arbre de processus de l'hôte, et notamment, un PID 1 est recréé, il s'agit
|
||||
du premier processus à s'exécuter dans le *namespace*.
|
||||
|
||||
Ceci n'est pas anodin car ce PID 1 de notre conteneur hérite des mêmes
|
||||
responsabilités qu'`init` : il doit `wait(2)` les zombies qui se créent autour
|
||||
de lui.
|
||||
\
|
||||
|
||||
Dans un conteneur, on a tendance à vouloir directement lancer une application,
|
||||
qui ne s'attend sans doute pas à devoir gérer les processus orphelins. Dans
|
||||
certaines circonstances, il peut donc arriver qu'un conteneur en particulier
|
||||
génère beaucoup de zombies, qui vont finir par rendre la machine instable, car
|
||||
le système sera à court de PID à distribuer.
|
||||
|
||||
On peut citer notamment la JVM, ou encore les conteneurs de construction de
|
||||
logiciels (Jenkins, Drone, ...). En effet, si l'on peut blâmer les programmeurs
|
||||
qui oublient de `wait(2)` leurs fils directs, il peut arriver régulièrement
|
||||
qu'une erreur de programmation crée des zombies dans les conteneurs de
|
||||
construction. Sans processus pour les arrêter, chaque *build* risque d'ajouter
|
||||
de nouveaux zombies.
|
||||
|
||||
|
||||
## `init` dans mes conteneurs
|
||||
|
||||
`tini`[^TINI_PROJECT] est un projet qui implémente un `init` tout à fait
|
||||
minimaliste, en ce sens qu'il se veut être le plus transparent possible, tout
|
||||
en jouant pleinement son rôle de collecteur et suppresseur de zombies.
|
||||
|
||||
[^TINI_PROJECT]: `init` à l'envers : <https://github.com/krallin/tini>
|
||||
|
||||
Sans rien ajouter de plus dans nos conteneurs ou nos `Dockerfile`, nous pouvons
|
||||
l'utiliser en ajoutant simplement `--init` à nos lignes de `docker container
|
||||
run` :
|
||||
|
||||
<div lang="en-US">
|
||||
```
|
||||
docker container run --init -d nemunaire/youp0m
|
||||
```
|
||||
</div>
|
||||
|
||||
Dans cet exemple, avant de lancer notre `ENTRYPOINT`, comme cela devrait être
|
||||
le cas sans l'option `--init`, ici c'est `tini` qui sera appelé. Il lancera
|
||||
ensuite l'`ENTRYPOINT` tel qu'il avait été prévu et commencera à jouer son rôle
|
||||
d'`init` le plus transparent possible, en attendant la venue des zombies.
|
||||
\
|
||||
|
||||
Mais ce n'est pas tout, `tini` aide également à transmettre les signaux
|
||||
d'extinction du conteneur (généralement `SIGTERM`), lorsque l'on fait un
|
||||
`docker stop`.
|
||||
|
||||
::::: {.question}
|
||||
|
||||
### Ne pourrait-on pas simplement ajouter un *thread* dans Jenkins/DroneCI/... ? {-}
|
||||
\
|
||||
|
||||
Alors ce n'est pas vraiment une solution idéale... si Jenkins s'exécute en tant
|
||||
que PID 1, il est paraît difficile de différencier les processus qui ont été
|
||||
re-parentés à Jenkins (et sur lesquels il faut `wait(2)` directement) et ceux
|
||||
qui ont été créés par Jenkins et pour lequel le code de retour est attendu par
|
||||
ailleurs.
|
||||
|
||||
:::::
|
||||
|
||||
|
||||
## Transmission de signaux
|
||||
|
||||
En fait, il se trouve que `bash(1)` est capable de réceptionner les zombies et
|
||||
de `wait(2)`, comme le fait `init(1)`, ce qui évite aussi l'invasion que l'on
|
||||
cherchait à éviter.
|
||||
|
||||
Mais `bash` pose un problème : il ne transmet pas les signaux à ses fils. S'il
|
||||
reçoit un `SIGTERM`, celui-ci ne sera tout simplement pas traité et encore
|
||||
moins transmis à ses fils, à moins d'avoir programmé un tel comportement en
|
||||
shell.
|
||||
|
||||
Voici donc une raison supplémentaire de préférer `tini` à `bash` (ou rien du
|
||||
tout). D'autant plus qu'à moins d'avoir préparé la fin d'exécution, `bash` ne
|
||||
retournera pas le code d'erreur de la commande que l'on a lancé, mais plutôt 0.
|
||||
|
||||
|
||||
## Intégration dans les `Dockerfile`
|
||||
|
||||
`tini` n'est pas nécessaire dans tous les conteneurs. On considère qu'une
|
||||
application qui ne `fork(2)` pas n'a pas besoin de processus `init`. De même,
|
||||
une image contenant un binaire qui gère correctement ses fils et qui n'a pas de
|
||||
petit enfant n'aura pas besoin de `tini`. Par contre dans les autres cas, il
|
||||
semble particulièrement indiqué.
|
Loading…
x
Reference in New Issue
Block a user