Problema clássico: o Python está rodando como PID 1 dentro do container, e durante timeouts do SSH (especialmente com ProxyCommand/bastion), os filhos ficam pendurados no PID 1, que não os espera — daí os zumbis. Solução em dois níveis: instalo o tini como init dentro do próprio imagem (protege qualquer docker run), e ativo init: true no compose como sinal explícito.
RUN apt-get update \\
&& apt-get install -y --no-install-recommends openssh-client \\
&& apt-get install -y --no-install-recommends openssh-client tini \\
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [\"python3\", \"/app/server.py\", \"--host\", \"0.0.0.0\", \"--config\", \"/config/config.yaml\"]
# tini como PID 1: recolhe processos orfãos (principal fonte de zumbis — SSH com ProxyCommand/bastion, terminado por ConnectTimeout/SSH_TIMEOUT_SEC, deixa o ssh filho pendurado no init);
# e encaminha corretamente sinais para o servidor Python.
ENTRYPOINT [\"/usr/bin/tini\", \"-g\", \"--\", \"python3\", \"/app/server.py\", \"--host\", \"0.0.0.0\", \"--config\", \"/config/config.yaml\"]
restart: always
# Caso o ENTRYPOINT na imagem tenha sido sobrescrito localmente: docker-init (tini) ainda assumirá o PID 1 e capturará zumbis de ssh/ProxyCommand em timeouts.
init: true
ports:
O que mudou e por quê:
-
Dockerfile: Instalo o pacotetinie envolvo a execução comtini -g -- python3 …. Agora, o PID 1 é otini, que:- reapára processos orfãos (principal causa de zumbis —
sshcomProxyCommand/bastion, terminado porConnectTimeout/SSH_TIMEOUT_SEC, deixa o ssh filho pendurado no init); - encaminha corretamente
SIGTERM/SIGINTpara toda a grupo de processos (flag-g), para quedocker stopnão fique travado por 10 segundos e não deixe resíduos de ssh.
- reapára processos orfãos (principal causa de zumbis —
-
docker-compose.yml:init: true— camada adicional de segurança, no caso de alguém sobrescrever localmenteentrypoint:(então o tini da imagem desaparece, mas o docker-init ainda assumirá o PID 1).
Como aplicar:
docker compose build --no-cache ansible-status
docker compose up -d
Verificar que os zumbis não estão mais sendo gerados (dentro do container, não deve haver linhas com Z no campo STAT):
docker compose exec ansible-status sh -c 'ps -e -o pid,ppid,stat,comm | awk "NR==1 || /Z/"'
Se quiser ainda mais cuidado — é possível adicionar, em um passo separado, nas opções de ssh_check_for_target, os parâmetros -o ControlMaster=no -o ControlPath=none e start_new_session=True no subprocess.run, para que o ssh não crie multiplexadores longevos e, em caso de TimeoutExpired, possamos matar toda a group de processos, e não apenas o processo principal ssh. Mas isso é uma melhoria no comportamento, e a causa raiz dos zumbis — ausência de init — já foi resolvida.