Ein klassisches Problem: Python läuft als PID 1 im Container, und bei SSH-Timouts (insbesondere mit ProxyCommand/Bastion) bleiben Enkelprozesse an PID 1 hängen, der sie jedoch nicht adoptiert — daraus entstehen Zombiprozesse. Ich behebe das mit zwei Schichten: Ich setze tini als init im Image selbst (schützt jeden docker run), und aktiviere init: true in docker-compose.yml als explizites Signal.
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 als PID 1: adoptiert verwaiste Enkel (Hauptquelle von Zombis — ssh mit ProxyCommand/Bastion, getötet durch ConnectTimeout/SSH_TIMEOUT_SEC, lässt den Kinderssh-Prozess an init hängen)
# und leitet Signale korrekt an den Python-Server weiter.
ENTRYPOINT ["/usr/bin/tini", "-g", "--", "python3", "/app/server.py", "--host", "0.0.0.0", "--config", "/config/config.yaml"]
restart: always
# Falls der ENTRYPOINT im Image überschrieben wurde: docker-init (tini)
# greift die Zombis von ssh/ProxyCommand bei Timeouten auf.
init: true
ports:
Was geändert wurde und warum:
Dockerfile: Ich installiere den Pakettiniund umhüllen die Kommandozeile mittini -g -- python3 …. Nun ist PID 1tini, der:- verwaiste Enkelprozesse reapernt (Hauptquelle von Zombis —
sshmitProxyCommand/Bastion, getötet durchConnectTimeout/SSH_TIMEOUT_SEC, lässt den Kinderssh-Prozess an init hängen); SIGTERM/SIGINTkorrekt an die gesamte Prozessgruppe weiterleitet (Flag-g), sodassdocker stopnicht 10 Sekunden hängt und keine ssh-„Schwänze“ zurücklässt.
- verwaiste Enkelprozesse reapernt (Hauptquelle von Zombis —
docker-compose.yml:init: true— zusätzliche Sicherungsschicht, falls jemand lokalentrypoint:überschreibt (dann verschwindettiniaus dem Image, aberdocker-initnimmt trotzdem PID 1 ein).
Anwendung:
docker compose build --no-cache ansible-status
docker compose up -d
Überprüfen, ob keine Zombis mehr entstehen (im Container sollten keine Zeilen mit Z in STAT auftreten):
docker compose exec ansible-status sh -c 'ps -e -o pid,ppid,stat,comm | awk "NR==1 || /Z/"'
Wenn man noch genauer sein möchte — kann man in ssh_check_for_target zusätzlich die Optionen -o ControlMaster=no -o ControlPath=none und start_new_session=True in subprocess.run hinzufügen, damit ssh keine langlebigen Multiplexoren erzeugt und bei TimeoutExpired die gesamte Prozessgruppe abgeschaltet werden kann, und nicht nur der Haupt-ssh. Aber das ist eine Verbesserung des Verhaltens — die eigentliche Ursache der Zombis, nämlich das Fehlen von init, ist bereits behoben.