Table des matières
CMD
s6-overlay est un ensemble de scripts et d'utilitaires faciles à installer (il suffit d'extraire une ou deux archive tar !) vous permettant d'utiliser des images Docker existantes tout en utilisant s6 comme pid 1 pour votre conteneur et superviseur de processus pour vos services.
Créez le Dockerfile suivant et essayez-le :
# Use your favorite image
FROM ubuntu
ARG S6_OVERLAY_VERSION=3.2.0.2
RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["/usr/sbin/nginx"]
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t demo .
docker-host $ docker run --name s6demo -d -p 80:80 demo
docker-host $ docker top s6demo acxf
PID TTY STAT TIME COMMAND
11735 ? Ss 0:00 _ s6-svscan
11772 ? S 0:00 _ s6-supervise
11773 ? Ss 0:00 | _ s6-linux-init-s
11771 ? Ss 0:00 _ rc.init
11812 ? S 0:00 | _ nginx
11814 ? S 0:00 | _ nginx
11816 ? S 0:00 | _ nginx
11813 ? S 0:00 | _ nginx
11815 ? S 0:00 | _ nginx
11779 ? S 0:00 _ s6-supervise
11785 ? Ss 0:00 | _ s6-ipcserverd
11778 ? S 0:00 _ s6-supervise
docker-host $ curl --head http://127.0.0.1/
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 17 Jan 2022 13:33:58 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 17 Jan 2022 13:32:11 GMT
Connection: keep-alive
ETag: "61e56fdb-264"
Accept-Ranges: bytes
Si vous migrez d'une version précédente de s6-overlay ( v2 ) vers la nouvelle version ( v3 ), vous devrez peut-être apporter quelques modifications à vos services ou à la façon dont vous utilisez s6-overlay pour que tout fonctionne correctement. Ce document tente d'être précis sur le fonctionnement de la v3, mais nous avons une page séparée répertoriant les principales différences et les éléments que vous remarquerez probablement. Veuillez le lire si vous êtes dans cette situation !
Le projet a les objectifs suivants :
cont-init.d
), la finalisation ( cont-finish.d
) et ses propres services avec des dépendances entre euxPID 1
appropriées6
et s6-portable-utils
. Ils incluent des utilitaires pratiques et composables qui nous rendent la vie beaucoup plus facile.logutil-service
qui utilise s6-log
sous le capot.USER
de Docker, pour exécuter l'ensemble de votre arborescence de processus en tant qu'utilisateur spécifique. Non compatible avec toutes les fonctionnalités, détails dans la section notes. L'un des mantras Docker souvent répétés est « un processus par conteneur », mais nous ne sommes pas d'accord. Il n’y a rien de mal en soi à exécuter plusieurs processus dans un conteneur. Notre politique est plus abstraite : « une chose par conteneur » : un conteneur doit faire une chose, comme « exécuter un service de chat » ou « exécuter gitlab ». Cela peut impliquer plusieurs processus, ce qui est très bien.
L'autre raison pour laquelle les auteurs d'images se détournent des superviseurs de processus est qu'ils pensent qu'un superviseur de processus doit redémarrer les services défaillants, ce qui signifie que le conteneur Docker ne mourra jamais.
Cela brise effectivement l'écosystème Docker - la plupart des images exécutent un processus qui se terminera en cas d'erreur. En quittant en cas d'erreur, vous autorisez l'administrateur système à gérer les échecs comme il le souhaite. Si votre image ne se ferme jamais, vous avez maintenant besoin d'une méthode alternative de récupération d'erreur et de notification d'échec.
Notre politique est que si « la chose » échoue, alors le conteneur devrait également échouer. Pour ce faire, nous déterminons quels processus peuvent redémarrer et lesquels doivent faire tomber le conteneur. Par exemple, si cron
ou syslog
échoue, votre conteneur peut très probablement le redémarrer sans aucun effet négatif, mais si ejabberd
échoue, le conteneur doit se fermer pour que l'administrateur système puisse prendre des mesures.
Notre interprétation de « The Docker Way » est la suivante :
et notre système d'initialisation est conçu pour faire exactement cela. Vos images se comporteront comme les autres images Docker et s'intégreront à l'écosystème d'images existant.
Voir « Écrire un script de fin facultatif » dans la section Utilisation pour plus de détails sur l'arrêt de « la chose ».
Notre init de superposition est correctement personnalisé pour fonctionner de manière appropriée dans des environnements conteneurisés. Cette section explique brièvement le fonctionnement des étapes mais si vous voulez savoir comment un système d'initialisation complet devrait fonctionner, vous pouvez lire cet article : Comment exécuter s6-svscan en tant que processus 1
/etc/cont-init.d
./etc/s6-overlay/s6-rc.d
, en suivant les dépendances/etc/services.d
) dans un répertoire temporaire et demandez à S6 de les démarrer (et de les superviser)./etc/cont-finish.d
.TERM
. De toute façon, il ne devrait plus y avoir de processus restants.KILL
. Ensuite, le conteneur sort. s6-overlay se présente sous la forme d'un ensemble d'archives tar que vous pouvez extraire sur votre image. Les archives tar dont vous avez besoin dépendent de l’image que vous utilisez ; la plupart des gens auront besoin des deux premiers, et les autres sont des extras que vous pouvez utiliser à votre convenance.
s6-overlay-noarch.tar.xz
: cette archive tar contient les scripts implémentant l'overlay. Nous l'appelons « noarch » car il est indépendant de l'architecture : il ne contient que des scripts et autres fichiers texte. Tous ceux qui souhaitent exécuter la superposition s6 doivent extraire cette archive tar.s6-overlay-x86_64.tar.xz
: remplacez x86_64
par l'architecture de votre système. Cette archive tar contient tous les binaires nécessaires de l'écosystème s6, tous liés statiquement et à l'écart des binaires de votre image. À moins que vous soyez sûr que votre image est déjà livrée avec tous les packages fournissant les binaires utilisés dans la superposition, vous devez extraire cette archive tar.s6-overlay-symlinks-noarch.tar.xz
: cette archive tar contient des liens symboliques vers les scripts s6-overlay afin qu'ils soient accessibles via /usr/bin
. Ce n'est normalement pas nécessaire, tous les scripts sont accessibles via la variable d'environnement PATH, mais si vous avez d'anciens scripts utilisateur contenant des shebangs tels que #!/usr/bin/with-contenv
, l'installation de ces liens symboliques les fera fonctionner.s6-overlay-symlinks-arch.tar.xz
: cette archive tar contient des liens symboliques vers les binaires de l'écosystème s6 fournis par la deuxième archive tar, pour les rendre accessibles via /usr/bin
. Ce n'est normalement pas nécessaire, mais si vous avez d'anciens scripts utilisateur contenant des shebangs tels que #!/usr/bin/execlineb
, l'installation de ces liens symboliques les fera fonctionner.syslogd-overlay-noarch.tar.xz
: cette archive tar contient des définitions pour un service syslogd
. Si vous exécutez des démons qui ne peuvent pas se connecter à stderr pour profiter de l'infrastructure de journalisation s6, mais que vous codez en dur l'utilisation de l'ancien mécanisme syslog()
, vous pouvez extraire cette archive tar et votre conteneur exécutera une émulation légère d'un démon syslogd
, Ainsi, vos journaux Syslog seront capturés et stockés sur le disque.Pour installer ces archives tar, ajoutez des lignes à votre Dockerfile qui correspondent à la fonctionnalité que vous souhaitez installer. Par exemple, la plupart des gens utiliseraient ce qui suit :
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
Assurez-vous de conserver les autorisations de fichiers lors de l'extraction (c'est-à-dire d'utiliser l'option -p
pour tar
.)
Le projet est distribué sous la forme d'un ensemble de fichiers standards .tar.xz, que vous extrayez à la racine de votre image. (Vous avez besoin du package xz-utils pour tar
pour comprendre les fichiers .tar.xz
; il est disponible dans chaque distribution, mais pas toujours dans les images de conteneur par défaut, vous devrez donc peut apt install xz-utils
ou apk add xz
, ou équivalent, avant de pouvoir développer les archives.)
Ensuite, définissez votre ENTRYPOINT
sur /init
.
À l'heure actuelle, nous vous recommandons d'utiliser la directive ADD
de Docker au lieu d'exécuter wget
ou curl
dans une directive RUN
- Docker est capable de gérer l'URL https lorsque vous utilisez ADD
, alors que votre image de base pourrait ne pas être en mesure d'utiliser https, ou même ne pas l'avoir. wget
ou curl
installé du tout.
À partir de là, vous avez plusieurs options :
CMD
de votre image.CMD
L'utilisation CMD
est un moyen pratique de profiter de la superposition. Votre CMD
peut être donné au moment de la construction dans le Dockerfile, ou au moment de l'exécution sur la ligne de commande, dans les deux cas, c'est très bien. Il sera exécuté comme un processus normal dans l'environnement mis en place par s6 ; en cas de panne ou de sortie, le conteneur s'arrêtera proprement et sortira. Vous pouvez exécuter des programmes interactifs de cette manière : seul le CMD recevra votre commande interactive, les processus de support ne seront pas impactés.
Par exemple:
FROM busybox
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t s6demo .
docker-host $ docker run -ti s6demo /bin/sh
/package/admin/s6-overlay/libexec/preinit: notice: /var/run is not a symlink to /run, fixing it
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
/ # ps
PID USER TIME COMMAND
1 root 0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service
17 root 0:00 {rc.init} /bin/sh -e /run/s6/basedir/scripts/rc.init top /bin/sh
18 root 0:00 s6-supervise s6-linux-init-shutdownd
20 root 0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -c /run/s6/basedir -g 3000 -C -B
24 root 0:00 s6-supervise s6rc-fdholder
25 root 0:00 s6-supervise s6rc-oneshot-runner
31 root 0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcserver-access -v0 -E -l0 -i data/rules -- /packa
58 root 0:00 /bin/sh
66 root 0:00 ps
/ # exit
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped
docker-host $
L'autre façon d'utiliser un conteneur avec superposition s6 est de rendre vos services supervisés. Vous pouvez superviser n'importe quel nombre de services ; généralement, ce ne sont que des services de support pour le démon principal que vous exécutez en tant que CMD, mais si c'est ce que vous voulez, rien ne vous empêche d'avoir un CMD vide et d'exécuter également votre démon principal en tant que service supervisé. Dans ce cas, le démon sera redémarré par s6 à chaque sortie ; le conteneur ne s'arrêtera que lorsque vous lui demanderez de le faire, soit via une commande docker stop
, soit depuis l'intérieur du conteneur avec la commande /run/s6/basedir/bin/halt
.
Il existe deux manières de réaliser un service supervisé. L'ancienne méthode, qui est toujours prise en charge, consiste à créer un répertoire de services "pur s6". Créez un répertoire avec le nom de votre service dans /etc/services.d
et placez-y un fichier run
exécutable ; c'est le fichier dans lequel vous placerez l'exécution de votre processus de longue durée. Pour plus de détails sur la supervision des répertoires de services et sur la façon dont vous pouvez configurer la manière dont S6 gère votre démon, vous pouvez consulter la documentation de servicedir. Un exemple simple ressemblerait à ceci :
/etc/services.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
La nouvelle méthode consiste à créer un répertoire de définition de source s6-rc dans le répertoire /etc/s6-overlay/s6-rc.d
et à ajouter le nom de ce répertoire au bundle user
, c'est-à-dire à créer un fichier vide portant le même nom. dans le répertoire /etc/s6-overlay/s6-rc.d/user/contents.d
. Le format d'un répertoire de définition de source est décrit dans cette page. Notez que vous pouvez définir des longruns , c'est-à-dire des démons qui seront supervisés par s6 comme avec la méthode /etc/services.d
, mais aussi des oneshots , c'est-à-dire des programmes qui s'exécuteront une fois et se termineront. Votre service principal est probablement un service à long terme , pas ponctuel : vous avez probablement besoin d'un démon pour rester.
L'avantage de ce nouveau format est qu'il permet de définir des dépendances entre services : si B dépend de A , alors A démarrera en premier, puis B démarrera quand A sera prêt, et quand on demandera au conteneur de sortir, B s'arrêtera d'abord, puis A . Si vous avez une architecture complexe où différents processus dépendent les uns des autres, ou simplement où vous devez mélanger des oneshots et des longruns dans un ordre précis, cela peut être pour vous.
L'exemple ci-dessus pourrait être réécrit de cette façon :
/etc/s6-overlay/s6-rc.d/myapp/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
/etc/s6-overlay/s6-rc.d/user/contents.d/myapp
: fichier vide. (Cela ajoute myapp
à l'ensemble des services que s6-rc démarrera au démarrage du conteneur.)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
: fichier vide. (Cela indique à s6-rc de démarrer myapp
uniquement lorsque tous les services de base sont prêts : cela évite les conditions de concurrence.)
Nous vous encourageons à passer au nouveau format, mais si vous n'avez pas besoin de ses avantages, vous pouvez vous en tenir aux répertoires de services habituels dans /etc/services.d
, cela fonctionnera tout aussi bien.
Si vous exécutez votre service principal en tant que CMD, vous n'avez rien à faire : lorsque votre CMD se ferme, ou lorsque vous exécutez docker stop
, le conteneur se fermera naturellement avec le même code de sortie que votre service. (Sachez cependant que dans le cas docker stop
, votre service obtiendra un SIGTERM, auquel cas le code de sortie dépendra entièrement de la façon dont votre service le gère - il pourrait le piéger et quitter 0, le piéger et quitter autre chose. , ou ne pas le piéger et laisser le shell sortir son propre code - normalement 130.)
Cependant, si vous exécutez votre service principal en tant que service supervisé, les choses sont différentes et vous devez indiquer au conteneur avec quel code quitter lorsque vous lui envoyez une commande docker stop
. Pour ce faire, vous devez écrire un script finish
:
/etc/services.d
, vous avez besoin d'un script exécutable /etc/services.d/myapp/finish
./etc/s6-overlay/s6-rc.d/myapp/finish
contenant votre script (le fichier peut ou non être exécutable). Ce script finish
sera exécuté à la fermeture de votre service et prendra deux arguments :
Dans le script finish
, vous devez écrire le code de sortie du conteneur souhaité dans le fichier /run/s6-linux-init-container-results/exitcode
- et c'est tout.
Par exemple, le script finish
du service myapp
ci-dessus pourrait ressembler à ceci :
#! /bin/sh
if test " $1 " -eq 256 ; then
e= $(( 128 + $2 ))
else
e= " $1 "
fi
echo " $e " > /run/s6-linux-init-container-results/exitcode
Lorsque vous envoyez une commande docker stop
à votre conteneur, le service myapp
sera supprimé et ce script sera exécuté ; il écrira soit le code de sortie de myapp
(si myapp
capte le signal TERM) soit 130 (si myapp
ne capte pas le signal TERM) dans le fichier spécial /run/s6-linux-init-container-results/exitcode
, qui sera lu par s6-overlay à la fin de la procédure d'arrêt du conteneur, et votre conteneur se terminera avec cette valeur.
Cette section décrit une fonctionnalité des versions de s6-overlay antérieures à la v3. fix-attrs est toujours pris en charge dans la v3, mais est obsolète , pour plusieurs raisons : l'une d'elles est qu'il n'est généralement pas une bonne politique de changer de propriétaire de manière dynamique alors que cela peut être fait de manière statique. Une autre raison est que cela ne fonctionne pas avec les conteneurs USER. Au lieu de fix-attrs, nous vous recommandons désormais de vous occuper de la propriété et des autorisations sur les montages d'hôtes hors ligne, avant d'exécuter le conteneur . Cela devrait être fait dans votre Dockerfile, lorsque vous disposez de toutes les informations nécessaires.
Cela dit, voici ce que nous avons écrit pour les versions précédentes et qui est toujours applicable aujourd'hui (mais s'il vous plaît, arrêtez de vous en fier) :
Parfois, il est intéressant de corriger la propriété et les autorisations avant de continuer car, par exemple, vous avez monté/mappé un dossier hôte dans votre conteneur. Notre superposition fournit un moyen de résoudre ce problème en utilisant les fichiers dans /etc/fix-attrs.d
. Voici le format de modèle suivi par les fichiers fix-attrs :
path recurse account fmode dmode
path
: chemin du fichier ou du répertoire.recurse
: (Défini sur true
ou false
) Si un dossier est trouvé, parcourez tous les fichiers et dossiers qu'il contient.account
: Compte cible. Il est possible d'utiliser par défaut la valeur de secours uid:gid
si le compte n'est pas trouvé. Par exemple, nobody,32768:32768
essaierait d'abord d'utiliser le compte nobody
, puis reviendrait à uid 32768
à la place. Si, par exemple, le compte daemon
est UID=2
et GID=2
, voici les valeurs possibles pour le champ account
:daemon: UID=2 GID=2
daemon,3:4: UID=2 GID=2
2:2,3:4: UID=2 GID=2
daemon:11111,3:4: UID=2 GID=11111
11111:daemon,3:4: UID=11111 GID=2
daemon:daemon,3:4: UID=2 GID=2
daemon:unexisting,3:4: UID=2 GID=4
unexisting:daemon,3:4: UID=3 GID=2
11111:11111,3:4: UID=11111 GID=11111
fmode
: Mode fichier cible. Par exemple, 0644
.dmode
: Mode répertoire/dossier cible. Par exemple, 0755
.Voici quelques exemples de travail :
/etc/fix-attrs.d/01-mysql-data-dir
:
/var/lib/mysql true mysql 0600 0700
/etc/fix-attrs.d/02-mysql-log-dirs
:
/var/log/mysql-error-logs true nobody,32768:32768 0644 2700
/var/log/mysql-general-logs true nobody,32768:32768 0644 2700
/var/log/mysql-slow-query-logs true nobody,32768:32768 0644 2700
Voici l'ancienne façon de procéder :
Après avoir corrigé les attributs (via /etc/fix-attrs.d/
) et avant de démarrer les services fournis par l'utilisateur (via s6-rc ou /etc/services.d
), notre superposition exécutera tous les scripts trouvés dans /etc/cont-init.d
, par exemple :
/etc/cont-init.d/02-confd-onetime
:
#!/command/execlineb -P
with-contenv
s6-envuidgid nginx
multisubstitute
{
import -u -D0 UID
import -u -D0 GID
import -u CONFD_PREFIX
define CONFD_CHECK_CMD "/usr/sbin/nginx -t -c {{ .src }}"
}
confd --onetime --prefix="${CONFD_PREFIX}" --tmpl-uid="${UID}" --tmpl-gid="${GID}" --tmpl-src="/etc/nginx/nginx.conf.tmpl" --tmpl-dest="/etc/nginx/nginx.conf" --tmpl-check-cmd="${CONFD_CHECK_CMD}" etcd
Cette méthode est toujours prise en charge. Cependant, il existe désormais un moyen plus générique et plus efficace de le faire : écrire vos tâches d'initialisation et de finalisation ponctuelles en tant que services s6-rc, en ajoutant des répertoires de définition de service dans /etc/s6-overlay/s6-rc.d
, en les intégrant du bundle user
(ils sont donc réellement démarrés au démarrage du conteneur) et en les faisant dépendre du bundle base
(ils ne sont donc démarrés qu'après base
).
Toutes les informations sur s6-rc peuvent être trouvées ici.
Au démarrage du conteneur, les opérations sont effectuées dans cet ordre :
/etc/fix-attrs.d
./etc/cont-init.d
sont exécutés séquentiellement.user
sont démarrés par s6-rc, dans un ordre défini par les dépendances. Les services peuvent être ponctuels (tâches d'initialisation) ou longruns (démons qui s'exécuteront tout au long de la durée de vie du conteneur). Si les services dépendent de base
, ils sont garantis de démarrer à ce stade et pas plus tôt ; s'ils ne le font pas, ils peuvent avoir été démarrés plus tôt, ce qui peut provoquer des conditions de concurrence - il est donc recommandé de toujours les faire dépendre de base
./etc/services.d
sont démarrés.user2
avec la dépendance correcte sont démarrés. (La plupart des gens n'ont pas besoin de l'utiliser ; si vous n'êtes pas sûr, restez fidèle au forfait user
.)Lorsque le conteneur est arrêté, soit parce que l'administrateur a envoyé une commande d'arrêt, soit parce que le CMD s'est arrêté, les opérations sont effectuées dans l'ordre inverse :
user2
avec la dépendance correcte sont arrêtés./etc/services.d
sont arrêtés.down
dans le répertoire de définition des sources est exécuté ; c'est ainsi que s6-rc peut effectuer des tâches de finalisation./etc/cont-finish.d
sont exécutés séquentiellement. Le but du bundle user2
est de permettre aux services utilisateur qui y sont déclarés de démarrer après ceux de /etc/services.d
; mais pour ce faire, chaque service de user2
doit déclarer une dépendance à legacy-services
. En d’autres termes, pour qu’un service foobar
démarre tardivement, vous devez :
/etc/s6-overlay/s6-rc.d/foobar
comme tout autre service s6-rc./etc/s6-overlay/s6-rc.d/foobar/dependencies.d/legacy-services
/etc/s6-overlay/s6-rc.d/user2/contents.d/foobar
. Cela garantira que foobar
démarrera après tout dans /etc/services.d
.
Par défaut, les services créés dans /etc/services.d
redémarreront automatiquement. Si un service doit arrêter le conteneur, vous devriez probablement l'exécuter en tant que CMD à la place ; mais si vous préférez l'exécuter en tant que service supervisé, vous devrez alors écrire un script finish
, qui sera exécuté lorsque le service sera arrêté ; pour arrêter le conteneur, la commande /run/s6/basedir/bin/halt
doit être invoquée. Voici un exemple de script de fin :
/etc/services.d/myapp/finish
:
#!/command/execlineb -S0
foreground { redirfd -w 1 /run/s6-linux-init-container-results/exitcode echo 0 }
/run/s6/basedir/bin/halt
La première ligne du script écrit 0
dans le fichier /run/s6-linux-init-container-results/exitcode
. La deuxième ligne arrête le conteneur. Lorsque vous arrêtez le conteneur via la commande /run/s6/basedir/bin/halt
exécutée depuis l'intérieur du conteneur, /run/s6-linux-init-container-results/exitcode
est lu et son contenu est utilisé comme code de sortie pour la commande docker run
qui a lancé le conteneur. Si le fichier n'existe pas, ou si le conteneur est arrêté avec docker stop
ou pour une autre raison, ce code de sortie est par défaut 0.
Il est possible d'effectuer des opérations plus avancées dans un script de fin. Par exemple, voici un script qui ne fait arrêter le service que lorsqu'il quitte une valeur différente de zéro :
/etc/services.d/myapp/finish
:
#!/command/execlineb -S1
if { eltest ${1} -ne 0 -a ${1} -ne 256 }
/run/s6/basedir/bin/halt
Notez qu'en général, les scripts de fin ne doivent être utilisés que pour les nettoyages locaux après la mort d'un démon. Si un service est si important que le conteneur doit s'arrêter lorsqu'il meurt, nous vous recommandons vivement de l'exécuter en tant que CMD.
Chaque service peut avoir son enregistreur dédié. Un enregistreur est un service S6 qui lit automatiquement à partir de la sortie standard de votre service et enregistre les données dans un fichier pivoté automatiquement à l'endroit souhaité. Notez que les démons se connectent généralement à stderr, pas à stdout, vous devriez donc probablement démarrer le script d'exécution de votre service avec exec 2>&1
dans le shell, ou avec fdmove -c 2 1
dans execline, afin d'attraper stderr .
s6-overlay fournit un utilitaire appelé logutil-service
qui est un wrapper sur le programme s6-log
. Cet assistant effectue les opérations suivantes :
S6_LOGGING_SCRIPT
nobody
(par défaut 65534:65534
s'il n'existe pas) s6-log s'exécutera alors indéfiniment, lisant les données de votre service et les écrivant dans le répertoire que vous avez spécifié dans logutil-service
.
Veuillez noter:
s6-setuidgid
nobody
nobody
. Vous pouvez créer des dossiers de journaux dans des scripts cont-init.d
ou sous forme de oneshots s6-rc. Voici un exemple de service journalisé myapp
implémenté à l'ancienne :
/etc/cont-init.d/myapp-log-prepare
:
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
/etc/services.d/myapp/run
:
#! /bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr
/etc/services.d/myapp/log/run
:
#! /bin/sh
exec logutil-service /var/log/myapp
Et voici le même service, myapp, implémenté dans s6-rc.
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/dependencies.d/base
: fichier vide
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/type
:
oneshot
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up
:
if { mkdir -p /var/log/myapp }
if { chown nobody:nogroup /var/log/myapp }
chmod 02755 /var/log/myapp
(Début de la section détaillée.)
Ainsi, les fichiers up
et down
sont spéciaux : ce ne sont pas des scripts shell, mais des lignes de commande uniques interprétées par execlineb. Vous ne devriez pas avoir à vous soucier de l'exécution ; vous devez seulement vous rappeler qu'un fichier up
contient une seule ligne de commande. Donc si vous avez besoin d'un script avec plusieurs instructions, voici comment procéder :
up
. Voici comment vous procéderiez normalement pour écrire le fichier up
pour myapp-log-prepare
:
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up
:
/etc/s6-overlay/scripts/myapp-log-prepare
/etc/s6-overlay/scripts/myapp-log-prepare
: (doit être exécutable)
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
L'emplacement du script réel est arbitraire, il doit simplement correspondre à ce que vous écrivez dans le fichier up
.
Mais ici, il se trouve que le script est suffisamment simple pour pouvoir tenir entièrement dans le fichier up
sans le rendre trop complexe ou trop difficile à comprendre. Nous avons donc choisi de l'inclure comme exemple pour montrer que vous pouvez faire plus avec les fichiers up
, si vous le souhaitez. Vous pouvez lire la documentation complète du langage execline ici.
(Fin de la section détaillée, cliquez à nouveau sur le triangle ci-dessus pour le réduire.)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
: fichier vide
/etc/s6-overlay/s6-rc.d/myapp-log/dependencies.d/myapp-log-prepare
: fichier vide
/etc/s6-overlay/s6-rc.d/myapp/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp/run
:
#! /bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr
/etc/s6-overlay/s6-rc.d/myapp/producer-for
:
myapp-log
/etc/s6-overlay/s6-rc.d/myapp-log/type
:
longrun
/etc/s6-overlay/s6-rc.d/myapp-log/run
:
#! /bin/sh
exec logutil-service /var/log/myapp
/etc/s6-overlay/s6-rc.d/myapp-log/consumer-for
:
myapp
/etc/s6-overlay/s6-rc.d/myapp-log/pipeline-name
:
myapp-pipeline
/etc/s6-overlay/s6-rc.d/user/contents.d/myapp-pipeline
: fichier vide
Cela fait beaucoup de fichiers ! Un résumé de ce que tout cela signifie est :
myapp | myapp-log
Le pipeline myapp | myapp-log
reçoit un nom, myapp-pipeline
, et ce nom est déclaré comme faisant partie du bundle user
, il sera donc démarré au démarrage du conteneur.myapp-log-prepare
, myapp-log
et myapp
dépendent tous du bundle base
, ce qui signifie qu'ils ne seront démarrés que lorsque le système sera réellement prêt à les démarrer. Elle accomplit vraiment les mêmes choses que la méthode /etc/cont-init.d
plus /etc/services.d
, mais elle est beaucoup plus propre en dessous et peut gérer des graphiques de dépendances beaucoup plus complexes, donc chaque fois que vous en avez l'occasion, nous vous recommandons vous vous familiarisez avec la manière s6-rc de déclarer vos prestations et vos loggers. La syntaxe complète d'un répertoire de définition de service, y compris la déclaration si votre service est à long terme ou unique, la déclaration des pipelines, l'ajout de délais d'attente spécifiques au service si vous en avez besoin, etc., peuvent être trouvées ici.
Lorsqu'il s'agit d'exécuter un service, qu'il s'agisse d'un service ou d'un enregistreur, une bonne pratique consiste à supprimer les privilèges avant de l'exécuter. s6
inclut déjà des utilitaires pour faire exactement ce genre de choses :
En execline
:
#!/command/execlineb -P
s6-setuidgid daemon
myservice
En sh
:
#! /bin/sh
exec s6-setuidgid daemon myservice
Si vous souhaitez en savoir plus sur ces utilitaires, veuillez consulter : s6-setuidgid
, s6-envuidgid
et s6-applyuidgid
.
Si vous souhaitez que votre script personnalisé dispose d'environnements de conteneurs : vous pouvez utiliser l'assistant with-contenv
, qui les poussera tous dans votre environnement d'exécution, par exemple :
/etc/cont-init.d/01-contenv-example
:
#! /command/with-contenv sh
env
Ce script affichera le contenu de votre environnement de conteneur.
Les versions récentes de Docker permettent d'exécuter des conteneurs avec un système de fichiers racine en lecture seule. Si votre conteneur se trouve dans un tel cas, vous devez définir S6_READ_ONLY_ROOT=1
pour informer s6-overlay qu'il ne doit pas tenter d'écrire dans certaines zones. Au lieu de cela, il effectuera des copies dans un tmpfs monté sur /run
.
Notez que s6-overlay suppose que :
/run
existe et est accessible en écriture. Si ce n'est pas le cas, il tentera d'y monter un tmpfs./var/run
est un lien symbolique vers /run
, pour des raisons de compatibilité avec les versions précédentes. Si ce n’est pas le cas, cela le fera. En général, vos paramètres Docker par défaut devraient déjà fournir un tmpfs approprié dans /run
.
Il est possible d'une manière ou d'une autre de modifier le comportement de s6-overlay en fournissant un ensemble déjà prédéfini de variables d'environnement au contexte d'exécution :
PATH
(default = /command:/usr/bin:/bin
) : c'est le PATH par défaut qu'auront tous les services du conteneur, y compris le CMD. Définissez cette variable si vous disposez de nombreux services qui dépendent de binaires stockés dans un autre répertoire, par exemple /usr/sbin
. Notez que /command
, /usr/bin
et /bin
seront toujours ajoutés à ce chemin s'ils ne figurent pas déjà dans celui que vous fournissez.S6_KEEP_ENV
(par défaut = 0) : si défini, alors l'environnement n'est pas réinitialisé et l'ensemble de l'arborescence de supervision voit l'ensemble original de variables d'environnement. Il passe du with-contenv
à un nop.S6_LOGGING
(par défaut = 0) :0
: Affiche tout sur stdout/stderr.1
: Utilise un enregistreur catch-all
interne et conserve tout ce qu'il contient, il se trouve dans /var/log/s6-uncaught-logs
. Tout ce qui est exécuté en tant que CMD
est toujours affiché sur stdout/stderr.2
: utilise un enregistreur catch-all
interne et conserve tout ce qu'il contient, y compris la sortie de CMD
. Absolument rien n'est écrit sur stdout/stderr.S6_CATCHALL_USER
(par défaut = root) : s'il est défini, et si S6_LOGGING
est 1 ou 2, alors l'enregistreur fourre-tout est exécuté en tant qu'utilisateur, qui doit être défini dans /etc/passwd
de votre image. Chaque séparation des privilèges contribue un peu à la sécurité.S6_BEHAVIOUR_IF_STAGE2_FAILS
(par défaut = 0) : détermine ce que le conteneur doit faire si l'un des scripts de service échoue. Cela comprend :fix-attrs
/etc/cont-init.d
ou du nouveau style échoue/etc/services.d
de l'ancien style ou un nouveau style s6-rc est marqué comme attendant une notification de préparation et ne parvient pas à être prêt dans le temps imparti (voir S6_CMD_WAIT_FOR_SERVICES_MAXTIME
ci-dessous). Les valeurs valides pour S6_BEHAVIOUR_IF_STAGE2_FAILS
sont les suivantes :0
: Continuez en silence même si un script a échoué.1
: Continuez mais avertissez avec un message d'erreur ennuyeux.2
: Arrêtez le conteneur.S6_KILL_FINISH_MAXTIME
(par défaut = 5000) : combien de temps (en millisecondes) le système doit attendre, au moment de l'arrêt, qu'un script dans /etc/cont-finish.d
se termine naturellement. Passé cette durée, le script recevra un SIGKILL. Gardez à l'esprit que les scripts dans /etc/cont.finish.d
sont exécutés de manière séquentielle et que la séquence d'arrêt attendra potentiellement S6_KILL_FINISH_MAXTIME
millisecondes pour chaque script.S6_SERVICES_READYTIME
(par défaut = 50) : avec les services déclarés dans /etc/services.d
, il existe une condition de concurrence inévitable entre le moment où les services sont démarrés et le moment où leur état de préparation peut être testé. Pour éviter cette course, nous dormons un peu, par défaut 50 millisecondes, avant de tester notre état de préparation. Si votre ordinateur est lent ou très occupé, vous pouvez obtenir des erreurs ressemblant à s6-svwait: fatal: unable to s6_svstatus_read: No such file or directory
. Dans ce cas, vous devez augmenter le temps de veille en le déclarant (en millisecondes) dans la variable S6_SERVICES_READYTIME
. Notez que cela ne concerne que /etc/services.d
; s6-rc est immunisé contre les conditions de concurrence critique.S6_SERVICES_GRACETIME
(par défaut = 3000) : combien de temps (en millisecondes) s6
doit attendre, au moment de l'arrêt, que les services déclarés dans /etc/services.d
meurent avant de procéder au reste de l'arrêt.S6_KILL_GRACETIME
(par défaut = 3000) : combien de temps (en millisecondes) s6
doit attendre, à la fin de la procédure d'arrêt, lorsque tous les processus ont reçu un signal TERM, pour qu'ils meurent avant d'envoyer un signal KILL
pour s'assurer qu'ils sont morts .S6_LOGGING_SCRIPT
(par défaut = "n20 s1000000 T") : cet environnement décide quoi enregistrer et comment, par défaut, chaque ligne sera précédée de ISO8601, pivotée lorsque le fichier de journalisation actuel atteint 1 Mo et archivée, au maximum, avec 20 fichiers.S6_CMD_ARG0
(par défaut = non défini) : la valeur de cette variable d'environnement sera ajoutée à tous les arguments CMD
transmis par Docker. Utilisez-le si vous migrez une image existante vers s6-overlay et que vous souhaitez en faire un remplacement instantané : définir cette variable sur la valeur d'un ENTRYPOINT précédemment utilisé vous aidera à effectuer la transition.S6_CMD_USE_TERMINAL
(par défaut = 0) : définissez cette valeur sur 1 si vous disposez d'un CMD qui a besoin d'un terminal pour sa sortie (généralement lorsque vous exécutez votre conteneur avec docker run -it
) et que vous avez défini S6_LOGGING
sur une valeur différente de zéro. Ce paramètre fera en sorte que votre CMD soit réellement envoyé à votre terminal ; l'inconvénient est que sa sortie ne sera pas enregistrée. Par défaut (lorsque cette variable est 0 ou non définie), les sorties stdout et stderr de votre CMD sont enregistrées lorsque S6_LOGGING
est différent de zéro, ce qui signifie qu'elles vont dans un tube même si vous l'exécutez dans un terminal interactif.S6_FIX_ATTRS_HIDDEN
(par défaut = 0) : contrôle la façon dont les scripts fix-attrs.d
traitent les fichiers et les répertoires.0
: les fichiers et répertoires cachés sont exclus.1
: Tous les fichiers et répertoires sont traités.S6_CMD_WAIT_FOR_SERVICES
(par défaut = 0) : par défaut, lorsque le conteneur démarre, les services dans /etc/services.d
seront démarrés et l'exécution procédera au démarrage du bundle user2
et du CMD, si l'un d'entre eux est défini. Si S6_CMD_WAIT_FOR_SERVICES
est non zéro, cependant, la séquence de démarrage du conteneur attendra que les services dans /etc/services.d
soient prêts avant de procéder avec le reste de la séquence. Notez que cela n'est significatif que si les services dans /etc/services.d
informent leur préparation à S6.S6_CMD_WAIT_FOR_SERVICES_MAXTIME
(Default = 0, c'est-à-dire infinie): le temps maximum (en millisecondes) Les services pourraient prendre pour évoquer avant de procéder à l'exécution de CMD. Définissez cette variable sur une valeur positive si vous avez des services qui peuvent potentiellement bloquer indéfiniment et que vous préférez que le conteneur échoue si tout est passé après une heure donnée. Notez que cette valeur inclut également le temps de configuration de l'initialisation du conteneur hérité ( /etc/cont-init.d
) et les services ( /etc/services.d
), alors prenez-le en compte lors du calcul d'une valeur appropriée. Dans les versions de S6-Overlay jusqu'à 3.1.6.2, la valeur par défaut était de 5000 (cinq secondes), mais elle a provoqué des défaillances de conteneurs plus indésirables que les problèmes résolus, donc maintenant il n'y a pas de délai par défaut: S6-Overlay attendra aussi longtemps que l'est nécessaire pour que tous les services soient élevés.S6_READ_ONLY_ROOT
(default = 0): /run/s6/etc
de l'exécution dans un conteneur dont le système de fichiers racine est en lecture seule, définissez cet Env sur 1 pour informer /etc
étape init Il tente de modifier les autorisations, etc. Voir le système de fichiers racine en lecture seule pour plus d'informations.S6_SYNC_DISKS
(Default = 0): Définissez cet Env sur 1 pour informer l'installation de l'étape 3 qu'il devrait tenter de synchroniser les systèmes de fichiers avant d'arrêter le conteneur. Remarque: Cela synchronisera probablement tous les systèmes de fichiers de l'hôte.S6_STAGE2_HOOK
(Default = Aucun): Si cette variable existe, son contenu sera interprété comme un extrait de shell qui sera exécuté au début 2, avant le début des services. Cela peut être utilisé, par exemple, pour corriger dynamiquement la base de données de service au moment de l'exécution juste avant sa compilation et l'exécution. La mauvaise valeur peut empêcher votre conteneur d'exécuter ou de mettre en danger votre sécurité, alors utilisez-le uniquement si vous savez exactement ce que vous faites. En cas de doute, laissez cette variable non définie.S6_VERBOSITY
(default = 2): contrôle la verbosité de S6-RC et potentiellement d'autres outils, au début du conteneur et à l'heure d'arrêt. La valeur par défaut, 2, est normalement verbeuse: elle répertorie les opérations de démarrage et d'arrêt du service. Vous pouvez rendre le conteneur plus silencieux en diminuant ce numéro: 1 n'imprimera que des avertissements et des erreurs, et 0 n'imprimera que des erreurs. Vous pouvez également rendre le conteneur plus verbeux, c'est-à-dire le traçage d'impression et les informations de débogage, en augmentant ce nombre jusqu'à 5, mais la sortie deviendra rapidement très bruyante, et la plupart des gens ne devraient pas en avoir besoin.S6_CMD_RECEIVE_SIGNALS
(default = 0): décide si les signaux envoyés au conteneur doivent être envoyés au PID 1 du conteneur ou au CMD. Par défaut, lorsque vous effectuez par exemple un docker stop
, un signal de terme sera envoyé au PID 1 du conteneur, qui déclenchera la séquence d'arrêt du conteneur complet - mais si un CMD est présent, il sera parmi les derniers processus à tuer , ce n'est que lorsque tout le reste est en panne et que le conteneur est sur le point de quitter. Si cette variable est 1 ou plus, les signaux sont détournés du PID 1 vers le CMD, ce qui signifie que docker stop
enverra un SIGTERM au CMD à la place, et le conteneur ne déclenchera sa procédure d'arrêt que lorsque le CMD est mort. Notez que seuls Sigterm, Sigquit, Sigint, Sigusr1, Sigusr2, Sigpwr et Sigwinch sont détournés; D'autres signaux sont ignorés ou ne peuvent pas être détournés et sont nécessairement gérés par le PID 1. Sachez que l'utilisation de cette option peut empêcher les CMD interactifs de fonctionner du tout - en d'autres termes, si vous exécutez un CMD interactif dans un terminal, Don 't définis cette variable; Mais cela devrait être bien car dans ce cas, vous avez déjà des moyens interactifs d'arrêter votre CMD. Si un logiciel exécuté dans votre conteneur nécessite Syslog, extraire le tarball syslogd-overlay-noarch.tar.xz
: qui vous donnera une petite émulation syslogd. Les journaux se trouvent dans divers sous-répertoires de /var/log/syslogd
, par exemple, les messages seront trouvés dans le répertoire /var/log/syslogd/messages/
, les derniers journaux disponibles dans le /var/log/syslogd/messages/current