O Dumb-Init é um supervisor de processo simples e um sistema init projetado para ser executado como PID 1 dentro de ambientes mínimos de contêineres (como o Docker). É implantado como um pequeno binário estaticamente ligado escrito em C.
Os contêineres leves popularizaram a idéia de executar um único processo ou serviço sem sistemas iniciais normais como Systemd ou Sysvinit. No entanto, omitir um sistema init geralmente leva a um manuseio incorreto de processos e sinais e pode resultar em problemas como contêineres que não podem ser graciosamente interrompidos ou com vazamentos de recipientes que deveriam ter sido destruídos.
dumb-init permite que você simplesmente prefie seu comando com dumb-init . Ele atua como PID 1 e imediatamente gera seu comando como um processo infantil, tomando cuidado para lidar adequadamente e avançar sinais à medida que são recebidos.
Normalmente, quando você inicia um contêiner do Docker, o processo que você está executando se torna PID 1, dando -lhe as peculiaridades e responsabilidades que acompanham o sistema init para o contêiner.
Existem dois problemas comuns que apresentam:
Na maioria dos casos, os sinais não serão tratados corretamente.
O kernel Linux aplica manuseio de sinal especial aos processos que são executados como PID 1.
Quando os processos são enviados um sinal em um sistema Linux normal, o kernel verifica primeiro os manipuladores personalizados que o processo se registrou para esse sinal e volta ao comportamento padrão (por exemplo, matando o processo no SIGTERM ).
No entanto, se o processo que recebe o sinal for PID 1, ele receberá um tratamento especial pelo kernel; Se não registrou um manipulador para o sinal, o kernel não voltará ao comportamento padrão e nada acontece. Em outras palavras, se o seu processo não lidar explicitamente a esses sinais, enviá -lo SIGTERM não terá nenhum efeito.
Um exemplo comum são os trabalhos de IC que o docker run my-container script : enviar SIGTERM para o processo docker run normalmente matará o comando docker run , mas deixe o contêiner em execução em segundo plano.
Os processos de zumbis órfãos não são adequadamente colhidos.
Um processo se torna um zumbi quando sai e continua sendo um zumbi até que seus pais chamarem alguma variação do sistema wait() a chamam. Ele permanece na tabela de processos como um processo "extinto". Normalmente, um processo pai chama wait() imediatamente e evita zumbis de vida longa.
Se um pai sair diante de seu filho, a criança está "órfã" e é reprovada sob o PID 1. O sistema init é, portanto, responsável pelo wait() -ing em processos de zumbis órfãos.
Obviamente, a maioria dos processos não wait() em processos aleatórios que se apegam a eles; portanto, os contêineres geralmente terminam com dezenas de zumbis enraizados no PID 1.
dumb-init faz dumb-init é executado como PID 1, agindo como um sistema init simples. Ele lança um processo único e, em seguida, proxies, todos receberam sinais para uma sessão enraizada nesse processo filho.
Como o seu processo real não é mais o PID 1, quando recebe sinais da dumb-init , os manipuladores de sinal padrão serão aplicados e seu processo se comportará como seria de esperar. Se o seu processo morrer, dumb-init também morrerá, tendo o cuidado de limpar quaisquer outros processos que ainda possam permanecer.
No seu modo padrão, dumb-init estabelece uma sessão enraizada na criança e envia sinais para todo o grupo de processos. Isso é útil se você tiver uma criança mal-estar (como um script de concha) que normalmente não sinaliza seus filhos antes de morrer.
Na verdade, isso pode ser útil fora dos contêineres do Docker em supervisores regulares de processo, como Daemontools ou Supervisord, para supervisionar scripts de shell. Normalmente, um sinal como SIGTERM recebido por um shell não é encaminhado para subprocessos; Em vez disso, apenas o processo da concha morre. Com a initulação idiota, você pode apenas escrever scripts de shell com initamento idiota no shebang:
#!/usr/bin/dumb-init /bin/sh
my-web-server & # launch a process in the background
my-other-server # launch another process in the foreground
Normalmente, um SIGTERM enviado à concha mataria a concha, mas deixaria esses processos em execução (tanto o fundo quanto o primeiro plano!). Com a initulação idiota, seus subprocessos receberão os mesmos sinais que seu shell recebe.
Se você deseja que os sinais sejam enviados apenas para o filho direto, você pode executar com o argumento --single-child ou definir a variável de ambiente DUMB_INIT_SETSID=0 ao executar dumb-init . Nesse modo, a initulação idiota é completamente transparente; Você pode até unir múltiplos (como dumb-init dumb-init echo 'oh, hi' ).
O idiota permite reescrever sinais de entrada antes de procurá-los. Isso é útil nos casos em que você tem um supervisor do Docker (como Mesos ou Kubernetes) que sempre envia um sinal padrão (por exemplo, SigMert). Alguns aplicativos exigem um sinal de parada diferente para fazer uma limpeza graciosa.
Por exemplo, para reescrever o sinal sigterm (número 15) para sigquit (número 3), basta adicionar --rewrite 15:3 na linha de comando.
Para soltar um sinal completamente, você pode reescrevê -lo para o número especial 0 .
Ao executar no modo Setsid, não é suficiente encaminhar SIGTSTP / SIGTTIN / SIGTTOU na maioria dos casos, pois se o processo não adicionou um manipulador de sinal personalizado para esses sinais, o kernel não aplicará um comportamento de manuseio de sinal padrão (que suspenderia o processo), pois é um membro de um grupo de processos orfanados. Por esse motivo, definimos reescritas padrão como SIGSTOP desses três sinais. Você pode optar por não participar desse comportamento reescrevendo os sinais de volta aos seus valores originais, se desejar.
Uma ressalva com esse recurso: para sinais de controle de emprego ( SIGTSTP , SIGTTIN , SIGTTOU ), a init de idiota sempre se suspende depois de receber o sinal, mesmo que você o reescreva em outra coisa.
Você tem algumas opções para usar dumb-init :
Muitas distribuições populares do Linux (incluindo o Debian (desde stretch ) e os derivados do Debian, como o Ubuntu (desde bionic )), agora contêm pacotes de invasidade idiota em seus repositórios oficiais.
Nas distribuições baseadas em Debian, você pode executar apt install dumb-init para instalar a initulação idiota, assim como você instalaria qualquer outro pacote.
NOTA: A maioria das versões fornecidas por distro-invasidades não são ligadas estaticamente, ao contrário das versões que fornecemos (consulte as outras opções abaixo). Normalmente, isso é perfeitamente bom, mas significa que essas versões da initulação idiota geralmente não funcionam quando copiadas para outras distritos do Linux, diferentemente das versões vinculadas estaticamente que fornecemos.
Se você possui um servidor APT interno, o upload do .deb para o seu servidor é a maneira recomendada de usar dumb-init . Nos seus dockerfiles, você pode simplesmente apt install dumb-init e ele estará disponível.
Os pacotes do Debian estão disponíveis na guia Github Lankes, ou você pode executar make builddeb .
.deb manualmente (Debian/Ubuntu) Se você não possui um servidor APT interno, pode usar dpkg -i para instalar o pacote .deb . Você pode escolher como colocar o .deb no seu contêiner (montando um diretório ou wget -ing, são algumas opções).
Uma possibilidade é com os seguintes comandos em seu Dockerfile:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.debComo o idiota é lançado como um binário estaticamente ligado, geralmente você pode apenas colocá-lo em suas imagens. Aqui está um exemplo de fazer isso em um Dockerfile:
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-init Embora dumb-init seja escrito inteiramente em C, também fornecemos um pacote Python que compila e instala o binário. Ele pode ser instalado a partir do Pypi usando pip . Você deseja primeiro instalar um compilador C (no Debian/Ubuntu, apt-get install gcc é suficiente) e, em seguida, apenas pip install dumb-init .
A partir de 1.2.0, o pacote no Pypi está disponível como um arquivo de roda pré-construído e não precisa ser compilado em distribuições comuns do Linux.
Uma vez instalado dentro do seu contêiner do Docker, basta prefixar seus comandos com dumb-init (e verifique se você está usando a sintaxe JSON recomendada).
Dentro de um Dockerfile, é uma boa prática usar o Dumb-Init como o ponto de entrada do seu contêiner. Um "EntryPoint" é um comando parcial que é antecipado à sua instrução CMD , tornando-o um ótimo ajuste para a initulação idiota:
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD [ "/my/script" , "--with" , "--args" ] Se você declarar um ponto de entrada em uma imagem base, qualquer imagens que descendem dela não precisará também declarar a initulação idiota. Eles podem simplesmente definir um CMD como sempre.
Para uso único interativo, você pode apenas prendê-lo manualmente:
$ docker run my_container dumb-init python -c 'while True: pass'
Executar esse mesmo comando sem dumb-init resultaria em não conseguir parar o contêiner sem SIGKILL , mas com dumb-init , você pode enviar sinais mais humanos como SIGTERM .
É importante que você use a sintaxe JSON para CMD e ENTRYPOINT . Caso contrário, o Docker chama um shell para executar seu comando, resultando no shell como PID 1 em vez de burro.
Muitas vezes, os contêineres desejam fazer algum trabalho pré-início que não pode ser feito durante o tempo de construção. Por exemplo, convém modelar alguns arquivos de configuração com base em variáveis de ambiente.
A melhor maneira de integrar isso com o idiota é assim:
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
CMD [ "bash" , "-c" , "do-some-pre-start-thing && exec my-server" ]Ao ainda usar o Dumb-Init como o ponto de entrada, você sempre possui um sistema init adequado.
A parte exec do comando bash é importante porque substitui o processo Bash pelo seu servidor, para que o shell exista apenas momentaneamente no início.
Construir o binário burro requer um compilador de trabalho e cabeçalhos e padrões da LIBC para Glibc.
$ make
A init de idiota compilada estaticamente é superior a 700kb devido ao GLIBC, mas Musl agora é uma opção. Em Debian/Ubuntu apt-get install musl-tools para instalar a fonte e os invólucros, então apenas:
$ CC=musl-gcc make
Quando compilado estaticamente com Musl, o tamanho binário é de cerca de 20kb.
Usamos as convenções padrão do Debian para especificar dependências de construção (procure no debian/control ). Uma maneira fácil de começar é o apt-get install build-essential devscripts equivs e, em seguida, sudo mk-build-deps -i --remove para instalar todas as dependências de compilação ausentes automaticamente. Você pode usar make builddeb para criar pacotes de Debian Init-Infin.
Se você preferir uma compilação automatizada do Pacote Debian usando o Docker, basta executar make builddeb-docker . Isso é mais fácil, mas exige que você faça com que o Docker funcionando em sua máquina.