Índice
CMD
s6-overlay é um conjunto de scripts e utilitários fácil de instalar (basta extrair um tarball ou dois!) Que permite usar imagens Docker existentes enquanto usa s6 como pid 1 para seu contêiner e supervisor de processo para seus serviços.
Crie o seguinte Dockerfile e experimente:
# 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
Se você estiver migrando de uma versão anterior do s6-overlay ( v2 ) para a nova versão ( v3 ), pode ser necessário fazer algumas alterações em seus serviços ou na maneira como você usa o s6-overlay para que tudo funcione perfeitamente. Este documento tenta ser preciso sobre como a v3 funciona, mas temos uma página separada listando as principais diferenças e coisas que você provavelmente notará. Por favor, leia se você estiver nesta situação!
O projeto tem os seguintes objetivos:
cont-init.d
), finalização ( cont-finish.d
) e seus próprios serviços com dependências entre elesPID 1
adequadas6
e s6-portable-utils
. Eles incluem utilitários práticos e combináveis que tornam nossas vidas muito, muito mais fáceis.logutil-service
que usa s6-log
nos bastidores.USER
do Docker, para executar toda a sua árvore de processos como um usuário específico. Não é compatível com todos os recursos, detalhes na seção de notas. Um dos mantras frequentemente repetidos do Docker é “um processo por contêiner”, mas discordamos. Não há nada de inerentemente ruim em executar vários processos em um contêiner. A nossa política é mais abstrata, "uma coisa por contêiner" - um contêiner deve fazer uma coisa, como "executar um serviço de chat" ou "executar o gitlab". Isso pode envolver vários processos, o que é bom.
A outra razão pela qual os autores de imagens evitam os supervisores de processo é que eles acreditam que um supervisor de processo deve reiniciar os serviços com falha, o que significa que o contêiner do Docker nunca morrerá.
Isso efetivamente quebra o ecossistema Docker - a maioria das imagens executa um processo que será encerrado quando houver um erro. Ao sair em caso de erro, você permite que o administrador do sistema lide com as falhas da maneira que preferir. Se sua imagem nunca sair, agora você precisa de algum método alternativo de recuperação de erros e notificação de falhas.
Nossa política é que se “a coisa” falhar, então o contêiner também deverá falhar. Fazemos isso determinando quais processos podem ser reiniciados e quais devem desativar o contêiner. Por exemplo, se cron
ou syslog
falhar, seu contêiner provavelmente poderá reiniciá-lo sem nenhum efeito negativo, mas se ejabberd
falhar, o contêiner deverá sair para que o administrador do sistema possa agir.
Nossa interpretação de "The Docker Way" é assim:
e nosso sistema init foi projetado para fazer exatamente isso. Suas imagens se comportarão como outras imagens do Docker e se ajustarão ao ecossistema de imagens existente.
Consulte "Escrever um script de finalização opcional" na seção Uso para obter detalhes sobre como interromper "a coisa".
Nosso init de sobreposição é devidamente personalizado para ser executado adequadamente em ambientes em contêineres. Esta seção explica brevemente como os estágios funcionam, mas se você quiser saber como um sistema init completo deve funcionar, você pode ler este artigo: Como executar o s6-svscan como processo 1
/etc/cont-init.d
./etc/s6-overlay/s6-rc.d
, seguindo as dependências/etc/services.d
) para um diretório temporário e faça com que s6 os inicie (e supervisione)./etc/cont-finish.d
.TERM
. De qualquer forma, não deve haver nenhum processo restante.KILL
. Então o contêiner sai. s6-overlay vem como um conjunto de tarballs que você pode extrair para sua imagem. Os tarballs necessários são uma função da imagem que você usa; a maioria das pessoas precisará dos dois primeiros, e os outros são extras que você pode usar conforme sua conveniência.
s6-overlay-noarch.tar.xz
: este tarball contém os scripts que implementam a sobreposição. Chamamos-lhe "noarch" porque é independente da arquitectura: contém apenas scripts e outros ficheiros de texto. Todos que desejam executar o s6-overlay precisam extrair esse tarball.s6-overlay-x86_64.tar.xz
: substitua x86_64
pela arquitetura do seu sistema. Este tarball contém todos os binários necessários do ecossistema s6, todos vinculados estaticamente e fora do caminho dos binários da sua imagem. A menos que você tenha certeza de que sua imagem já vem com todos os pacotes que fornecem os binários usados na sobreposição, você precisa extrair este tarball.s6-overlay-symlinks-noarch.tar.xz
: este tarball contém links simbólicos para os scripts s6-overlay para que sejam acessíveis via /usr/bin
. Normalmente não é necessário, todos os scripts são acessíveis através da variável de ambiente PATH, mas se você tiver scripts de usuário antigos contendo shebangs como #!/usr/bin/with-contenv
, a instalação desses links simbólicos os fará funcionar.s6-overlay-symlinks-arch.tar.xz
: este tarball contém links simbólicos para os binários do ecossistema s6 fornecido pelo segundo tarball, para torná-los acessíveis via /usr/bin
. Normalmente não é necessário, mas se você tiver scripts de usuário antigos contendo shebangs como #!/usr/bin/execlineb
, a instalação desses links simbólicos os fará funcionar.syslogd-overlay-noarch.tar.xz
: este tarball contém definições para um serviço syslogd
. Se você estiver executando daemons que não podem fazer login no stderr para aproveitar as vantagens da infraestrutura de log s6, mas codificar o uso do antigo mecanismo syslog()
, você pode extrair esse tarball e seu contêiner executará uma emulação leve de um daemon syslogd
, portanto, seus logs do syslog serão capturados e armazenados em disco.Para instalar esses tarballs, adicione linhas ao seu Dockerfile que correspondam à funcionalidade que você deseja instalar. Por exemplo, a maioria das pessoas usaria o seguinte:
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
Certifique-se de preservar as permissões do arquivo ao extrair (ou seja, usar a opção -p
para tar
).
O projeto é distribuído como um conjunto de arquivos .tar.xz padrão, que você extrai na raiz da sua imagem. (Você precisa do pacote xz-utils para tar
entender os arquivos .tar.xz
; ele está disponível em todas as distribuições, mas nem sempre nas imagens de contêiner padrão, então você pode precisar apt install xz-utils
ou apk add xz
, ou equivalente, antes de poder expandir os arquivos.)
Depois, defina seu ENTRYPOINT
como /init
.
No momento, recomendamos usar a diretiva ADD
do Docker em vez de executar wget
ou curl
em uma diretiva RUN
- o Docker é capaz de lidar com a URL https quando você usa ADD
, enquanto sua imagem base pode não ser capaz de usar https, ou pode até não ter wget
ou curl
instalado.
A partir daí, você tem algumas opções:
CMD
da sua imagem.CMD
Usar CMD
é uma maneira conveniente de aproveitar as vantagens da sobreposição. Seu CMD
pode ser fornecido em tempo de construção no Dockerfile ou em tempo de execução na linha de comando, de qualquer forma, tudo bem. Será executado como um processo normal no ambiente configurado por s6; quando falhar ou sair, o contêiner será encerrado de forma limpa e sairá. Você pode executar programas interativos desta maneira: somente o CMD receberá seu comando interativo, os processos de suporte não serão afetados.
Por exemplo:
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 $
A outra maneira de usar um contêiner com sobreposição s6 é tornar seus serviços supervisionados. Você pode supervisionar qualquer número de serviços; geralmente são apenas serviços de suporte para o daemon principal que você executa como um CMD, mas se é isso que você deseja, nada impede que você tenha um CMD vazio e execute seu daemon principal também como um serviço supervisionado. Nesse caso, o daemon será reiniciado por s6 sempre que sair; o contêiner só irá parar quando você solicitar, por meio de um comando docker stop
ou de dentro do contêiner com o comando /run/s6/basedir/bin/halt
.
Existem duas maneiras de fazer um serviço supervisionado. A maneira antiga, que ainda é suportada, é criar um diretório de serviço "s6 puro". Crie um diretório com o nome do seu serviço em /etc/services.d
e coloque nele um arquivo executável de run
; este é o arquivo no qual você colocará a execução do processo de longa duração. Para obter detalhes sobre supervisão de diretórios de serviço e como você pode configurar como o s6 lida com seu daemon, você pode dar uma olhada na documentação do servicedir. Um exemplo simples seria assim:
/etc/services.d/myapp/run
:
#!/command/execlineb -P
nginx -g "daemon off;"
A nova maneira é criar um diretório de definição de origem s6-rc no diretório /etc/s6-overlay/s6-rc.d
e adicionar o nome desse diretório ao pacote user
, ou seja, criar um arquivo vazio com o mesmo nome no diretório /etc/s6-overlay/s6-rc.d/user/contents.d
. O formato de um diretório de definição de origem é descrito nesta página. Observe que você pode definir longruns , ou seja, daemons que serão supervisionados por s6 assim como no método /etc/services.d
, mas também oneshots , ou seja, programas que serão executados uma vez e encerrados. Seu serviço principal é provavelmente longrun , não oneshot : você provavelmente precisará de um daemon para permanecer por aqui.
A vantagem deste novo formato é que ele permite definir dependências entre serviços: se B depender de A , então A iniciará primeiro, então B iniciará quando A estiver pronto, e quando o contêiner for avisado para sair, B irá parar primeiro, depois A. Se você tem uma arquitetura complexa onde vários processos dependem uns dos outros, ou simplesmente onde você tem que misturar oneshots e longruns em uma ordem precisa, isso pode ser para você.
O exemplo acima poderia ser reescrito desta forma:
/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
: arquivo vazio. (Isso adiciona myapp
ao conjunto de serviços que o s6-rc iniciará na inicialização do contêiner.)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
: arquivo vazio. (Isso diz ao s6-rc para iniciar myapp
apenas quando todos os serviços básicos estiverem prontos: evita condições de corrida.)
Nós encorajamos você a mudar para o novo formato, mas se você não precisar de seus benefícios, você pode ficar com os diretórios de serviço regulares em /etc/services.d
, ele funcionará da mesma forma.
Se você executar seu serviço principal como um CMD, não terá nada a fazer: quando seu CMD sair ou quando você executar docker stop
, o contêiner sairá naturalmente com o mesmo código de saída do seu serviço. (Esteja ciente, no entanto, que no caso docker stop
, seu serviço receberá um SIGTERM; nesse caso, o código de saída dependerá inteiramente de como seu serviço o trata - ele pode interceptá-lo e sair de 0, interceptá-lo e sair de outra coisa , ou não interceptá-lo e deixar o shell sair de seu próprio código - normalmente 130.)
Se você executar seu serviço principal como um serviço supervisionado, no entanto, as coisas serão diferentes e você precisará informar ao contêiner com qual código sair ao enviar um comando docker stop
. Para fazer isso, você precisa escrever um script finish
:
/etc/services.d
, você precisará de um script executável /etc/services.d/myapp/finish
./etc/s6-overlay/s6-rc.d/myapp/finish
contendo seu script (o arquivo pode ou não ser executável). Este script finish
será executado quando o serviço for encerrado e receberá dois argumentos:
No script finish
, você precisa escrever o código de saída do contêiner desejado no arquivo /run/s6-linux-init-container-results/exitcode
- e é isso.
Por exemplo, o script finish
para o serviço myapp
acima poderia ser algo assim:
#! /bin/sh
if test " $1 " -eq 256 ; then
e= $(( 128 + $2 ))
else
e= " $1 "
fi
echo " $e " > /run/s6-linux-init-container-results/exitcode
Quando você envia um comando docker stop
para o seu contêiner, o serviço myapp
será eliminado e este script será executado; ele escreverá o código de saída de myapp
(se myapp
capturar o sinal TERM) ou 130 (se myapp
não capturar o sinal TERM) no arquivo especial /run/s6-linux-init-container-results/exitcode
, que irá será lido por s6-overlay no final do procedimento de encerramento do contêiner, e seu contêiner sairá com esse valor.
Esta seção descreve uma funcionalidade das versões do s6-overlay anteriores à v3. fix-attrs ainda é suportado na v3, mas está obsoleto por vários motivos: um deles é que geralmente não é uma boa política alterar a propriedade dinamicamente quando isso pode ser feito estaticamente. Outro motivo é que ele não funciona com contêineres USER. Em vez de fix-attrs, agora recomendamos que você cuide da propriedade e das permissões nas montagens de host offline, antes de executar o contêiner . Isso deve ser feito no seu Dockerfile, quando você tiver todas as informações necessárias.
Dito isto, aqui está o que escrevemos para versões anteriores e que ainda é aplicável hoje (mas pare de depender disso):
Às vezes é interessante corrigir a propriedade e as permissões antes de prosseguir porque, por exemplo, você montou/mapeou uma pasta host dentro do seu contêiner. Nossa sobreposição fornece uma maneira de resolver esse problema usando arquivos em /etc/fix-attrs.d
. Este é o formato padrão seguido pelos arquivos fix-attrs:
path recurse account fmode dmode
path
: caminho do arquivo ou diretório.recurse
: (Definido como true
ou false
) Se uma pasta for encontrada, recurse por todos os arquivos e pastas que ela contém.account
: conta de destino. É possível usar como padrão o fallback uid:gid
se a conta não for encontrada. Por exemplo, nobody,32768:32768
tentaria usar a conta nobody
primeiro e, em seguida, recorreria ao uid 32768
. Se, por exemplo, a conta daemon
for UID=2
e GID=2
, estes são os valores possíveis para o campo 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
: modo de arquivo de destino. Por exemplo, 0644
.dmode
: Modo diretório/pasta de destino. Por exemplo, 0755
.Aqui você tem alguns exemplos de trabalho:
/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
Aqui está a maneira antiga de fazer isso:
Depois de corrigir os atributos (através de /etc/fix-attrs.d/
) e antes de iniciar os serviços fornecidos pelo usuário (através de s6-rc ou /etc/services.d
) nossa sobreposição executará todos os scripts encontrados em /etc/cont-init.d
, por exemplo:
/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
Esta forma ainda é suportada. No entanto, agora existe uma maneira mais genérica e eficiente de fazer isso: escrever suas tarefas de inicialização e finalização oneshot como serviços s6-rc, adicionando diretórios de definição de serviço em /etc/s6-overlay/s6-rc.d
, tornando-os parte do pacote user
(para que sejam realmente iniciados quando o contêiner for inicializado) e tornando-os dependentes do pacote base
(para que sejam iniciados somente após base
).
Todas as informações sobre o s6-rc podem ser encontradas aqui.
Quando o contêiner é iniciado, as operações são realizadas nesta ordem:
/etc/fix-attrs.d
./etc/cont-init.d
são executados sequencialmente.user
são iniciados pelo s6-rc, em uma ordem definida pelas dependências. Os serviços podem ser oneshots (tarefas de inicialização) ou longruns (daemons que serão executados durante toda a vida útil do contêiner). Se os serviços dependerem de base
, é garantido que começarão neste ponto e não antes; caso contrário, podem ter sido iniciados mais cedo, o que pode causar condições de corrida - por isso é recomendável sempre fazê-los depender de base
./etc/services.d
são iniciados.user2
com a dependência correta são iniciados. (A maioria das pessoas não precisa usar isso; se você não tiver certeza, siga o pacote user
.)Quando o contêiner é interrompido, seja porque o administrador enviou um comando de parada ou porque o CMD foi encerrado, as operações são executadas na ordem inversa:
user2
com a dependência correta são interrompidos./etc/services.d
são interrompidos.down
no diretório de definição de origem é executado; é assim que o s6-rc pode realizar tarefas de finalização./etc/cont-finish.d
são executados sequencialmente. O objetivo do pacote user2
é permitir que os serviços do usuário declarados nele sejam iniciados após os /etc/services.d
; mas para fazer isso, todo serviço em user2
precisa declarar uma dependência de legacy-services
. Em outras palavras, para que um serviço foobar
comece tarde, você precisa:
/etc/s6-overlay/s6-rc.d/foobar
como qualquer outro serviço s6-rc./etc/s6-overlay/s6-rc.d/foobar/dependencies.d/legacy-services
/etc/s6-overlay/s6-rc.d/user2/contents.d/foobar
. Isso garantirá que foobar
será iniciado depois de tudo em /etc/services.d
.
Por padrão, os serviços criados em /etc/services.d
serão reiniciados automaticamente. Se um serviço derrubar o contêiner, você provavelmente deverá executá-lo como um CMD; mas se preferir executá-lo como um serviço supervisionado, você precisará escrever um script finish
, que será executado quando o serviço estiver inativo; para fazer o contêiner parar, o comando /run/s6/basedir/bin/halt
deve ser invocado. Aqui está um exemplo de script de conclusão:
/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
A primeira linha do script grava 0
no arquivo /run/s6-linux-init-container-results/exitcode
. A segunda linha interrompe o contêiner. Quando você interrompe o contêiner por meio do comando /run/s6/basedir/bin/halt
executado de dentro do contêiner, /run/s6-linux-init-container-results/exitcode
é lido e seu conteúdo é usado como código de saída para o comando docker run
que iniciou o contêiner. Se o arquivo não existir ou se o contêiner for interrompido com docker stop
ou outro motivo, o código de saída será padronizado como 0.
É possível realizar operações mais avançadas em um script final. Por exemplo, aqui está um script que só desativa o serviço quando ele sai diferente de zero:
/etc/services.d/myapp/finish
:
#!/command/execlineb -S1
if { eltest ${1} -ne 0 -a ${1} -ne 256 }
/run/s6/basedir/bin/halt
Observe que, em geral, os scripts finais só devem ser usados para limpezas locais após a morte de um daemon. Se um serviço for tão importante que o contêiner precise parar quando morrer, realmente recomendamos executá-lo como CMD.
Cada serviço pode ter seu registrador dedicado. Um logger é um serviço s6 que lê automaticamente o stdout do seu serviço e registra os dados em um arquivo girado automaticamente no local desejado. Observe que os daemons geralmente registram em stderr, não em stdout, então você provavelmente deve iniciar o script de execução do seu serviço com exec 2>&1
no shell ou com fdmove -c 2 1
em execline, para capturar stderr .
s6-overlay fornece um utilitário chamado logutil-service
que é um wrapper do programa s6-log
. Este ajudante faz o seguinte:
S6_LOGGING_SCRIPT
nobody
(padrão 65534:65534
se não existir) s6-log será executado para sempre, lendo os dados do seu serviço e gravando-os no diretório especificado em logutil-service
.
Observe:
s6-setuidgid
nobody
nobody
. Você pode criar pastas de log em scripts cont-init.d
ou como oneshots s6-rc. Aqui está um exemplo de um serviço logado myapp
implementado da maneira antiga:
/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
E aqui está o mesmo serviço, myapp, implementado no s6-rc.
/etc/s6-overlay/s6-rc.d/myapp-log-prepare/dependencies.d/base
: arquivo vazio
/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
(Início da seção detalhada.)
Portanto, os arquivos up
e down
são especiais: não são scripts shell, mas linhas de comando únicas interpretadas pelo execlineb. Você não deveria se preocupar com execline; você deve apenas lembrar que um arquivo up
contém uma única linha de comando. Então se você precisa de um script com diversas instruções, veja como fazer:
up
. Aqui está como você normalmente procederia para escrever o arquivo up
para 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
: (precisa ser executável)
#! /bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp
A localização do script real é arbitrária, só precisa corresponder ao que você está escrevendo no arquivo up
.
Mas aqui acontece que o script é simples o suficiente para caber inteiramente no arquivo up
sem torná-lo muito complexo ou difícil de entender. Portanto, optamos por incluí-lo como exemplo para mostrar que há mais que você pode fazer com os arquivos up
, se desejar. Você pode ler a documentação completa da linguagem execline aqui.
(Final da seção detalhada, clique no triângulo acima novamente para recolher.)
/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base
: arquivo vazio
/etc/s6-overlay/s6-rc.d/myapp-log/dependencies.d/myapp-log-prepare
: arquivo vazio
/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
: arquivo vazio
São muitos arquivos! Um resumo do que tudo isso significa é:
myapp | myapp-log
O pipeline myapp | myapp-log
recebe um nome, myapp-pipeline
, e esse nome é declarado como parte do pacote user
, portanto, será iniciado quando o contêiner for iniciado.myapp-log-prepare
, myapp-log
e myapp
dependem do pacote base
, o que significa que eles só serão iniciados quando o sistema estiver realmente pronto para iniciá-los. Ele realmente realiza as mesmas coisas que o método /etc/cont-init.d
mais /etc/services.d
, mas é muito mais limpo por baixo e pode lidar com gráficos de dependência muito mais complexos, portanto, sempre que você tiver a oportunidade, recomendamos você se familiariza com a maneira s6-rc de declarar seus serviços e seus registradores. A sintaxe completa de um diretório de definição de serviço, incluindo a declaração se seu serviço é de execução longa ou única, a declaração de pipelines, a adição de tempos limite específicos do serviço, se necessário, etc., pode ser encontrada aqui.
Quando se trata de executar um serviço, não importa se é um serviço ou um logger, uma boa prática é eliminar privilégios antes de executá-lo. s6
já inclui utilitários para fazer exatamente esse tipo de coisa:
Em execline
:
#!/command/execlineb -P
s6-setuidgid daemon
myservice
Em sh
:
#! /bin/sh
exec s6-setuidgid daemon myservice
Se você quiser saber mais sobre esses utilitários, dê uma olhada em: s6-setuidgid
, s6-envuidgid
e s6-applyuidgid
.
Se você deseja que seu script personalizado tenha ambientes de contêiner disponíveis: você pode usar o auxiliar with-contenv
, que enviará todos eles para seu ambiente de execução, por exemplo:
/etc/cont-init.d/01-contenv-example
:
#! /command/with-contenv sh
env
Este script produzirá o conteúdo do seu ambiente de contêiner.
Versões recentes do Docker permitem a execução de contêineres com um sistema de arquivos raiz somente leitura. Se o seu contêiner estiver nesse caso, você deve definir S6_READ_ONLY_ROOT=1
para informar ao s6-overlay que ele não deve tentar gravar em determinadas áreas - em vez disso, ele executará cópias em um tmpfs montado em /run
.
Observe que s6-overlay assume que:
/run
existe e é gravável. Caso contrário, ele tentará montar um tmpfs lá./var/run
é um link simbólico para /run
, para compatibilidade com versões anteriores. Se não for, isso acontecerá. Em geral, suas configurações padrão do docker já devem fornecer um tmpfs adequado em /run
.
É possível de alguma forma ajustar o comportamento do s6-overlay fornecendo um conjunto já predefinido de variáveis de ambiente para o contexto de execução:
PATH
(default = /command:/usr/bin:/bin
): este é o PATH padrão que todos os serviços do container, incluindo o CMD, terão. Defina esta variável se você tiver muitos serviços que dependem de binários armazenados em outro diretório, por exemplo, /usr/sbin
. Observe que /command
, /usr/bin
e /bin
sempre serão adicionados a esse caminho se ainda não estiverem naquele que você forneceu.S6_KEEP_ENV
(padrão = 0): se definido, o ambiente não será redefinido e toda a árvore de supervisão verá o conjunto original de variáveis de ambiente. Ele muda with-contenv
para um nop.S6_LOGGING
(padrão = 0):0
: Envia tudo para stdout/stderr.1
: Usa um logger catch-all
e persiste tudo nele, está localizado em /var/log/s6-uncaught-logs
. Qualquer coisa executada como um CMD
ainda será enviada para stdout/stderr.2
: Usa um logger catch-all
e persiste tudo nele, incluindo a saída de CMD
. Absolutamente nada é gravado em stdout/stderr.S6_CATCHALL_USER
(padrão = root): se definido, e se S6_LOGGING
for 1 ou 2, então o registrador pega-tudo é executado como este usuário, que deve ser definido em /etc/passwd
da sua imagem. Cada separação de privilégios ajuda um pouco na segurança.S6_BEHAVIOUR_IF_STAGE2_FAILS
(padrão = 0): determina o que o contêiner deve fazer se um dos scripts de serviço falhar. Isso inclui:fix-attrs
/etc/cont-init.d
ou do novo estilo s6-rc falhar/etc/services.d
de estilo antigo ou s6-rc longrun de estilo novo estiver marcado como aguardando notificação de prontidão e não ficar pronto no tempo alocado (consulte S6_CMD_WAIT_FOR_SERVICES_MAXTIME
abaixo). Os valores válidos para S6_BEHAVIOUR_IF_STAGE2_FAILS
são os seguintes:0
: Continue silenciosamente mesmo se um script falhar.1
: Continue, mas avise com uma mensagem de erro irritante.2
: Pare o contêiner.S6_KILL_FINISH_MAXTIME
(padrão = 5000): Quanto tempo (em milissegundos) o sistema deve esperar, no momento do desligamento, para que um script em /etc/cont-finish.d
termine naturalmente. Após esse período, o script receberá um SIGKILL. Tenha em mente que os scripts em /etc/cont.finish.d
são executados sequencialmente e a sequência de desligamento irá potencialmente aguardar S6_KILL_FINISH_MAXTIME
milissegundos para cada script.S6_SERVICES_READYTIME
(padrão = 50): Com serviços declarados em /etc/services.d
, há uma condição de corrida inevitável entre o momento em que os serviços são iniciados e o momento em que sua prontidão pode ser testada. Para evitar essa corrida, dormimos um pouco, por padrão 50 milissegundos, antes de testar a prontidão. Se sua máquina estiver lenta ou muito ocupada, você poderá receber erros parecidos com s6-svwait: fatal: unable to s6_svstatus_read: No such file or directory
. Nesse caso, deve-se aumentar o tempo de sleep, declarando-o (em milissegundos) na variável S6_SERVICES_READYTIME
. Observe que isso se aplica apenas /etc/services.d
; s6-rc é imune à condição de corrida.S6_SERVICES_GRACETIME
(padrão = 3000): Quanto tempo (em milissegundos) s6
deve esperar, no momento do encerramento, para que os serviços declarados em /etc/services.d
morram antes de prosseguir com o restante do encerramento.S6_KILL_GRACETIME
(padrão = 3000): Quanto tempo (em milissegundos) s6
deve esperar, no final do procedimento de desligamento, quando todos os processos tiverem recebido um sinal TERM, para que morram antes de enviar um sinal KILL
para ter certeza de que estão mortos .S6_LOGGING_SCRIPT
(default = "n20 s1000000 T"): Este ambiente decide o que registrar e como, por padrão, cada linha será anexada com ISO8601, girada quando o arquivo de registro atual atingir 1 MB e arquivado, no máximo, com 20 arquivos.S6_CMD_ARG0
(padrão = não definido): o valor deste env var será anexado a qualquer argumento CMD
passado pelo docker. Use-o se você estiver migrando uma imagem existente para s6-overlay e quiser torná-la uma substituição imediata: definir esta variável com o valor de um ENTRYPOINT usado anteriormente ajudará na transição.S6_CMD_USE_TERMINAL
(padrão = 0): defina esse valor como 1 se você tiver um CMD que precisa de um terminal para sua saída (normalmente quando você está executando seu contêiner com docker run -it
) e tiver definido S6_LOGGING
com um valor diferente de zero. Esta configuração fará com que seu CMD seja realmente enviado para o seu terminal; a desvantagem é que sua saída não será registrada. Por padrão (quando esta variável é 0 ou não definida), o stdout e o stderr do seu CMD são registrados quando S6_LOGGING
é diferente de zero, o que significa que eles vão para um canal mesmo se você o estiver executando em um terminal interativo.S6_FIX_ATTRS_HIDDEN
(padrão = 0): Controla como os scripts fix-attrs.d
processam arquivos e diretórios.0
: Arquivos e diretórios ocultos são excluídos.1
: Todos os arquivos e diretórios são processados.S6_CMD_WAIT_FOR_SERVICES
(padrão = 0): Por padrão, quando o contêiner é iniciado, os serviços em /etc/services.d
serão iniciados e a execução continuará iniciando o pacote user2
e o CMD, se algum deles estiver definido. Se S6_CMD_WAIT_FOR_SERVICES
não for diferente de zero, no entanto, a sequência inicial do contêiner aguardará até que os Serviços em /etc/services.d
estejam prontos antes de prosseguir com o restante da sequência. Observe que isso só é significativo se os Serviços em /etc/services.d
notificarem sua prontidão para S6.S6_CMD_WAIT_FOR_SERVICES_MAXTIME
(default = 0, ou seja, infinito): o tempo máximo (em milissegundos) que os serviços podem levar para criar antes de se procedir à execução da CMD. Defina essa variável como um valor positivo se você tiver serviços que possam bloquear indefinidamente e você prefere que o contêiner falhe, se não tudo, tudo estiver após um determinado tempo. Observe que esse valor também inclui o tempo que configura a inicialização do contêiner herdado ( /etc/cont-init.d
) e os serviços ( /etc/services.d
); portanto, leve isso em consideração ao calcular um valor adequado. Nas versões do S6-overlay até 3.1.6.2, o padrão era de 5000 (cinco segundos), mas causou falhas mais indesejadas de contêineres do que os problemas resolvidos, então agora não há tempo limite por padrão: S6-overlay esperará o tempo que está necessário para que todos os serviços sejam levantados.S6_READ_ONLY_ROOT
(default = 0): Ao executar em um contêiner cujo sistema de arquivos raiz é somente leitura, defina esse env para 1 para informar o estágio 2 do init que deve copiar scripts de inicialização fornecidos pelo usuário de /etc
para /run/s6/etc
antes Ele tenta alterar as permissões, etc. Consulte o sistema de arquivos raiz somente leitura para obter mais informações.S6_SYNC_DISKS
(default = 0): defina este Env como 1 para informar o estágio INIT 3 que ele deve tentar sincronizar sistemas de arquivos antes de interromper o contêiner. Nota: Isso provavelmente sincronizará todos os sistemas de arquivos no host.S6_STAGE2_HOOK
(default = nenhum): Se essa variável existir, seu conteúdo será interpretado como um trecho de shell que será executado no estágio inicial 2, antes que os serviços sejam iniciados. Isso pode ser usado, por exemplo, para corrigir dinamicamente o banco de dados de serviço em tempo de execução logo antes de ser compilado e executado. O valor errado pode impedir que seu contêiner execute ou ameaça sua segurança; portanto, use isso apenas se você souber exatamente o que está fazendo. Em caso de dúvida, deixe essa variável indefinida.S6_VERBOSITY
(padrão = 2): controla a verbosidade do S6-RC e potencialmente outras ferramentas, no tempo de partida e parada do contêiner. O padrão, 2, é normalmente detalhado: listará as operações de início e parada do serviço. Você pode deixar o contêiner mais silencioso diminuindo esse número: 1 imprimirá apenas avisos e erros e 0 imprimirá apenas erros. Você também pode tornar o contêiner mais detalhado, ou seja, as informações de rastreamento de impressão e depuração, aumentando esse número até 5, mas a saída se tornará rapidamente muito barulhenta e a maioria das pessoas não deve precisar disso.S6_CMD_RECEIVE_SIGNALS
(padrão = 0): decide se os sinais enviados ao contêiner devem ser enviados para o PID 1 do contêiner ou para o CMD. Por padrão, quando você executa, por exemplo, uma docker stop
, um sinal de termo será enviado ao PID 1 do contêiner, que acionará a sequência de desligamento completa do contêiner - mas se um CMD estiver presente, estará entre os últimos processos a serem mortos , somente quando todo o resto está abaixado e o contêiner está prestes a sair. Se essa variável for 1 ou mais, os sinais serão desviados do PID 1 para o CMD, o que significa que o docker stop
enviará um sigmert para o CMD, e o contêiner apenas desencadeará seu procedimento de desligamento quando o CMD estiver morto. Observe que apenas o Sigterm, Sigquit, Sigint, Sigusr1, Sigusr2, Sigpwr e Sigwinch são desviados; Outros sinais são ignorados ou não podem ser desviados e são necessariamente tratados pelo PID 1. Esteja ciente de que o uso dessa opção pode impedir que os CMDs interativos funcionem - em outras palavras, se você estiver executando um CMD interativo em um terminal, não Defina essa variável; Mas isso deve ficar bem, pois neste caso você já tem maneiras interativas de interromper seu CMD. Se o software em execução em seu contêiner exigir syslog, extraia o syslogd-overlay-noarch.tar.xz
tarball: isso lhe dará uma pequena emulação de syslogd. Os logs serão encontrados em vários subdiretos de /var/log/syslogd
, por exemplo, as mensagens serão encontradas no diretório /var/log/syslogd/messages/
/var/log/syslogd/messages/current