Nous allons voir au cours de cet article les différentes politiques de redémarrage automatique que vous pouvez mettre en place afin de solutionner cette problématique.
Avant-Propos
Il est important de rappeler un élément essentiel de Docker. Le programme exécuté par votre conteneur va prendre automatiquement le PID 1
. Prenons deux exemples concrets, avec deux conteneurs que je vais construire avec un Dockerfile.
Voici le premier Dockerfile :
FROM debian:buster-slim
RUN apt-get update && apt-get install -y \
iputils-ping \
procps \
&& rm -rf /var/lib/apt/lists/*
CMD /bin/ping localhost
Et mon second exemple :
FROM debian:buster-slim
RUN apt-get update && apt-get install -y \
iputils-ping \
procps \
&& rm -rf /var/lib/apt/lists/*
CMD ["/bin/ping","localhost"]
Les deux conteneurs vont réaliser un ping
, aucun problème là dessus. Par contre dans mon premier exemple, le PID 1
sera attaché à la commande /bin/sh
alors que dans le second cas, ce PID sera attaché directement à ping
. Vous pouvez vérifier cela avec la commande ps
:
On construit la première image :
docker build -t test1 .
Ensuite on lance un conteneur avec l'image :
docker run -d --name quicktest1 --rm test1
Enfin, vérifions la liste des processus :
docker exec quicktest1 ps fax
PID TTY STAT TIME COMMAND
7 ? Rs 0:00 ps fax
1 ? Ss 0:00 /bin/sh -c /bin/ping localhost
6 ? S 0:00 /bin/ping localhost
Les mêmes commandes avec le second fichier donneront ceci :
docker exec quicktest2 ps fax
PID TTY STAT TIME COMMAND
6 ? Rs 0:00 ps fax
1 ? Ss 0:00 /bin/ping localhost
Lors de l'arrêt d'un conteneur, un signal d'arrêt va être envoyé à votre processus avec le PID 1
mais pas aux autres processus. Dans notre premier exemple le signal va être transmis à /bin/sh
alors que dans le second celui-ci va être envoyé à ping
directement.
Il est important de savoir que la vie de votre conteneur est lié à ce processus 1
! 😇
Votre conteneur peut donc planter si votre processus avec le PID 1
ne fonctionne plus. Sans politique de redémarrage, celui-ci restera éteint. Avant de résoudre ce problème, nous allons construire une image custom afin de réaliser nos tests et de comprendre au mieux les différents types de redémarrage.
🚩Si vous êtes curieux, réalisez les mêmes tests avec une autre distribution, par exemple Alpine ... :
FROM alpine
RUN apk --no-cache add iputils procps
CMD /bin/ping 8.8.8.8
Je vous laisse découvrir la liste des processus 👍 🚩
Construire notre image custom
En réalité nous allons construire une image qui va systématiquement s'arrêter après un certain temps. Par exemple 15 secondes, vous pouvez bien sûr changer ce délai si nécessaire. Pour cela créons un script bash, qui après 15 secondes, s'arrêtera :
crashtest.sh :
#/bin/sh
sleep 15
exit 1
C'est très simple et efficace :
sleep 15
On dort 15 secondes et enfin :
exit 1
On quitte avec le code de retour 1
qui est le code associé à une erreur.
Maintenant que nous avons un script basique qui va systématiquement s'interrompre, nous pouvons créer une image Docker à l'aide d'un fichier Dockerfile :
Dockerfile :
FROM alpine
COPY crashtest.sh /
CMD /crashtest.sh
Je vais donc créer une image pour mon conteneur, depuis l'image alpine
( j'aurais très bien pu réaliser cette image depuis une autre distribution ) et je copie mon script bash
dans mon image avant de l’exécuter au démarrage.
Nous allons maintenant pouvoir construire l'image :
docker build -t crashtest .
Nous allons pouvoir démarrer notre conteneur avec l'image que nous venons de créer : crashtest
.
Utilisons donc la commande docker run
afin de lancer notre instance :
docker run -d --name crashtest_instance_1 crashtest
Vous pouvez vérifier que votre conteneur est lancé :
docker ps
Par défaut cette commande n'affiche pas les conteneurs arrêtés, ajoutez l'option -a
afin de les voir :
docker ps -a
Votre instance doit s'arrêter au bout de 15 secondes.
Nous pouvons donc constater que lorsqu'une application avec le PID 1
dans un conteneur se ferme, ce conteneur s'arrête également ! Cela signifie que par défaut, si l'application lancée par défaut crash, votre conteneur s'arrêtera et ne se relancera pas sans manipulation.
Ajoutons donc notre première politique de redémarrage.
Première RestartPolicy
Il est possible de redémarrer automatiquement les conteneurs en spécifiant une option lors du lancement du conteneur. Pour mieux comprendre les politiques de redémarrage, voyons ce qui se passe lorsque nous utilisons notre première option.
Commençons avec l'option : --restart always
Comme son nom l'indique, cet ajout va permettre de toujours relancer le conteneur après un arrêt :
docker run -d --name crashtest_instance_1 --restart always crashtest
Vérifions mon instance peu avant 15 secondes :
et un peu après :
Cette option va donc permettre de redémarrer systématiquement un conteneur. Y compris après un redémarrage de votre hôte. Pourtant est-ce l'option à mettre en permanence dans vos fichiers ?
Essayons autre chose avec cette option, par exemple d'arrêter notre conteneur :
docker stop crashtest_instance_1
Et redémarrez votre machine. Après le redémarrage, vérifiez l'état de votre instance avec docker ps
:
Et là, surprise, votre instance est actuellement lancée !
Bien évidemment c'est une une limite à l'utilisation de cette option, un conteneur arrêté par vos soins ne doit peut être pas redémarré après un reboot.
Que se passe-t-il si notre conteneur est arrêté pour une raison valable, ou pire, que se passe-t-il si le conteneur est obsolète?
Les différentes Policy
Il existe 4 politiques de redémarrage des conteneurs dans Docker :
- restart: "no"
- restart: "always"
- restart: "on-failure"
- restart: "unless-stopped"
La première no
est en réalité celle par défaut. Votre conteneur ne sera pas redémarré en aucun cas sans intervention externe à Docker.
Nous venons donc de voir always
, et ses limites. Attardons nous sur la suivante : on-failure
Cette policy permet de redémarrer un conteneur si le code de sortie indique une erreur. Par contre si celui-ci renvoie un code de retour success, le conteneur ne sera pas relancé. Essayons cela avec notre image :
docker run -d --name crashtest_instance_1 --restart on-failure:3 crashtest
Je passe également le chiffre 3
à notre instance, il s'agit du nombre de fois que l'instance va essayer de redémarrer toute seule avant de s'arrêter complètement.
Enfin dernière option unless-stopped
.
Cette option est très similaire à always
. Le conteneur sera redémarré, sauf si vous le stoppez de façon explicite. Par exemple à l'aide de la commande docker stop
.
Cet option possède l'avantage de ne pas redémarrer un conteneur après un reboot ou restart du daemon Docker si vous l'avez arrêté.
Il s'agit donc l'option de démarrage que vous rencontrerez le plus souvent !
Nous venons donc de voir ensemble les différentes types de politique de redémarrage que vous pouvez associer à votre conteneur. La meilleure stratégie à adopter dépend de la nature de votre instance. Qu'exécute t-elle ?
Une instance MySQL
ou redis
n'adoptera pas forcément la même policy que votre image custom avec du batch-processing.
Par exemple dans le cas de script, pour effectuer des traitements par lot, l'option on-failure
peut prendre tout son sens.
Alors que dans bon nombre de cas, l'option unless-stopped
sera privilégiée.
Vous possédez maintenant toutes les connaissances nécessaires pour faire votre choix ! Quelle politique de redémarrage allez-vous maintenant utiliser ? Est-ce qu'une d'elle avait déjà vos faveurs !?
En tout cas n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours ! 😇