
sleep
é um substituto para os processos mais complexos, é claro.
Este Dockerfile (como você pode ver usando o formulário exec para que haja apenas um processo em execução e nenhum filho de bash
):
FROM busybox
CMD ["/bin/sleep", "100000"]
cria um contêiner ininterrupto:
docker build -t kill-sleep .
docker run --rm --name kill-sleep kill-sleep
Quando tento parar:
time docker stop kill-sleep
kill-sleep
real 0m10.449s
user 0m0.021s
sys 0m0.027s
o comando expira em 10 segundos antes que o contêiner seja eliminado.
O problema não é que ele sleep
não processa sinais, porque se eu executar no host:
sleep 100000
# in another shell
ps faxww | grep sleep
kill -TERM 31333 # the PID
o processo para imediatamente.
O problema pode ter a ver com o fato de estar rodando como PID 1 no contêiner, mas ainda não vi uma documentação de referência para isso.
Responder1
Quando você corre docker stop ...
,algumas coisas vão acontecer:
docker
envia umSIGTERM
para o processo principal do contêiner. O processo é capaz de mascarar/ignorar aSIGTERM
, e se o fizer (ou manuseá-lo sem encerrar) "nada" acontecerá.- Após um tempo limite (padrão 10 segundos),
docker
envia umSIGKILL
para o processo principal. Este sinal não pode ser mascarado por um processo e, portanto, morre imediatamente, sem oportunidade de executar um procedimento de desligamento.
Idealmente, os processos executados docker
responderão SIGTERM
em tempo hábil, cuidando de qualquer limpeza antes de encerrar.
Se você sabe que o processo não tem nenhuma manutenção para realizar (por exemplo: sleep
) ou não responderá adequadamente a SIGTERM
, você pode especificar um tempo limite mais curto (ou mais longo) com o -t
sinalizador:
-t, --time=10 Seconds to wait for stop before killing it
Por exemplo, no seu caso, você pode querer executar docker stop -t 0 ${CONTAINER}
.
A razão pela qual este comportamento do sinal é diferente é devido ao sleep
funcionamento com PID = 1.
Normalmente (por exemplo: executando com PID! = 1), qualquer sinal de que o processo não lida explicitamente leva ao encerramento do processo - tente enviar um sleep
a SIGUSR1
.
No entanto, ao executar com PID = 1, os sinais não tratados são ignorados, caso contrário, você acabaria com um kernel panic:
Kernel panic - not syncing: Attempted to kill init!
Você pode enviar um sinal para o contêiner do docker usando ferramentas do docker, por exemplo:
docker kill -s TERM kill-sleep
Como podemos ver, isso não tem o efeito desejado, mas sim:
docker kill -s KILL kill-sleep
Um experimento
Dockerfile
FROM busybox
COPY run.sh /run.sh
RUN chmod +x /run.sh
CMD "/run.sh"
correr.sh
#!/bin/sh
echo "sleeping"
sleep 100000
Agora, corra
docker build -t kill-sleep .
docker run --rm --name kill-sleep kill-sleep
E isso em um terminal diferente:
docker stop kill-sleep
Observamos o mesmo atraso/tempo limite de 10 segundos.
Uma solução
Agora vamos lidar com o SIGTERM
. Background e wait
ing for sleep
são devidos à forma como um shell POSIX lida com sinais (vejaessepara mais).
correr.sh
#!/bin/sh
die_func() {
echo "oh no"
sleep 2
exit 1
}
trap die_func TERM
echo "sleeping"
sleep 100000 &
wait
Execute os comandos novamente e veremos o que procuramos!
$ time docker stop kill-sleep
kill-sleep
real 0m2.515s
user 0m0.008s
sys 0m0.044s
Responder2
Mais algumas opções:
- adicione a
--init
opção ao comando de execução do contêiner. Dessa forma, sleep não é PID 1 e init faz a coisa certa em TERM. - adicione o
--stop-signal=KILL
ao comando de execução do contêiner. Entretanto, usar KILL como uma operação normal é geralmente desencorajado.