Exécuter plusieurs processus dans un conteneur
Le processus principal en cours d'exécution d'un conteneur est l'ENTRYPOINT
et/ou CMD
à la
fin du Dockerfile
. C'est une bonne pratique de séparer les domaines de préoccupation en
utilisant un service par conteneur. Ce service peut se forker en plusieurs
processus (par exemple, le serveur web Apache démarre plusieurs processus worker).
Il est acceptable d'avoir plusieurs processus, mais pour tirer le meilleur parti de Docker,
évitez qu'un conteneur soit responsable de multiples aspects de votre application globale. Vous pouvez connecter plusieurs conteneurs en utilisant des réseaux définis par l'utilisateur et
des volumes partagés.
Le processus principal du conteneur est responsable de la gestion de tous les processus qu'il
démarre. Dans certains cas, le processus principal n'est pas bien conçu, et ne gère pas
gracieusement la "moisson" (arrêt) des processus enfants lorsque le conteneur se ferme. Si
votre processus appartient à cette catégorie, vous pouvez utiliser l'option --init
lorsque vous
exécutez le conteneur. Le drapeau --init
insère un petit processus init dans le
conteneur comme processus principal, et gère la moisson de tous les processus lorsque le
conteneur se ferme. Gérer de tels processus de cette manière est supérieur à l'utilisation d'un
processus init complet tel que sysvinit
ou systemd
pour gérer le cycle de vie des processus
dans votre conteneur.
Si vous devez exécuter plus d'un service dans un conteneur, vous pouvez y parvenir de plusieurs manières différentes.
Utiliser un script wrapper
Mettez toutes vos commandes dans un script wrapper, complet avec des informations de test et
de débogage. Exécutez le script wrapper comme votre CMD
. Voici un
exemple naïf. D'abord, le script wrapper :
#!/bin/bash
# Démarrer le premier processus
./my_first_process &
# Démarrer le second processus
./my_second_process &
# Attendre qu'un processus se termine
wait -n
# Sortir avec le statut du processus qui s'est terminé en premier
exit $?
Ensuite, le Dockerfile :
# syntax=docker/dockerfile:1
FROM ubuntu:latest
COPY my_first_process my_first_process
COPY my_second_process my_second_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.sh
Utiliser les contrôles de tâches Bash
Si vous avez un processus principal qui doit démarrer en premier et rester en cours d'exécution mais vous avez temporairement besoin d'exécuter d'autres processus (peut-être pour interagir avec le processus principal) alors vous pouvez utiliser le contrôle de tâches de bash. D'abord, le script wrapper :
#!/bin/bash
# activer le contrôle de tâches de bash
set -m
# Démarrer le processus principal et le mettre en arrière-plan
./my_main_process &
# Démarrer le processus helper
./my_helper_process
# le my_helper_process pourrait avoir besoin de savoir comment attendre
# que le processus principal démarre avant de faire son travail et retourner
# maintenant nous ramenons le processus principal au premier plan
# et l'y laissons
fg %1
# syntax=docker/dockerfile:1
FROM ubuntu:latest
COPY my_main_process my_main_process
COPY my_helper_process my_helper_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.sh
Utiliser un gestionnaire de processus
Utilisez un gestionnaire de processus comme supervisord
. C'est plus complexe que les autres
options, car cela nécessite d'inclure supervisord
et sa configuration dans
votre image (ou baser votre image sur une qui inclut supervisord
), ainsi que
les différentes applications qu'il gère. Ensuite vous démarrez supervisord
, qui
gère vos processus pour vous.
L'exemple de Dockerfile suivant montre cette approche. L'exemple suppose que ces fichiers existent à la racine du contexte de build :
supervisord.conf
my_first_process
my_second_process
# syntax=docker/dockerfile:1
FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY my_first_process my_first_process
COPY my_second_process my_second_process
CMD ["/usr/bin/supervisord"]
Si vous voulez vous assurer que les deux processus sortent leur stdout
et stderr
vers
les logs du conteneur, vous pouvez ajouter ce qui suit au fichier supervisord.conf
:
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
[program:app]
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true