Speak about tini
This commit is contained in:
parent
8a84f7a527
commit
f46ed6edd1
@ -3,6 +3,7 @@ include ../pandoc-opts.mk
|
|||||||
SOURCES = tutorial.md \
|
SOURCES = tutorial.md \
|
||||||
registry.md \
|
registry.md \
|
||||||
../4/filesystem.md ../4/filesystem-ex.md ../4/filesystem-more.md \
|
../4/filesystem.md ../4/filesystem-ex.md ../4/filesystem-more.md \
|
||||||
|
tini.md \
|
||||||
oci.md \
|
oci.md \
|
||||||
runc.md \
|
runc.md \
|
||||||
linuxkit.md linuxkit-content.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