Einführung
Paper & Pine verwandelt eingereihte Veranstaltungsprogramme in echte PDFs. Vor einer Produktionspause muss es das aktuelle dreiseitige Handout fertigstellen, den nächsten Auftrag ausstehend lassen und das fertige Morgen-PDF erhalten. Der bereitgestellte Python-Worker weiß bereits, wie er nach einer Stoppanforderung seinen aktuellen Auftrag beendet, aber seine Einstiegsshell verhindert derzeit, dass diese Anforderung ihn erreicht.
Du solltest Terminalpfade, grundlegendes Docker und Container-Logs kennen. Die kleine Compose-Datei und das hier genutzte Shell-Verhalten werden vor ihrer Verwendung erklärt. Python-Programmierung und ein physischer Drucker sind unnötig: Das Produkt ist ein herunterladbares, dekodierbares PDF.
Nutze Workstation und Print Desk. Unabhängige Übungs- und Lieferwarteschlangen erlauben dir, einen kontrollierten Fehler ohne Verlust des Lieferfortschritts zu untersuchen. Der Trainingsmodus pausiert nach der ersten gerenderten Seite bis zu einer Stoppanforderung und fügt danach zwischen verbleibenden Seiten eine offengelegte Pause von 0,5 Sekunden ein. Diese Pausen machen die Entscheidung sichtbar, sind aber kein Produktionsgeschwindigkeits-Benchmark. Plane etwa 45 Minuten einschließlich Lesen und gewöhnlicher Fehler ein; dies ist eine Schätzung des Autors.
Die Stoppanforderung an den Worker übermitteln
In diesem Schritt beobachtest du einen fehlgeschlagenen Übungsstopp, reparierst die Einstiegsshell und stellst eine echte Lieferung in demselben Worker fertig, der die Stoppanforderung empfängt.
Mit einem echten fertigen Programmheft beginnen
Beginne in Print Desk. Öffne unter Delivery attempt für Morning welcome handout Preview text und wähle Download PDF. Die Vorschau wird aus diesem gespeicherten zweiseitigen PDF dekodiert, nicht aus Quelltext rekonstruiert. Kehre mit Back to Print Desk zurück. Das aktuelle Community open day handout ist ausstehend; Volunteer notes muss nach dieser Übergabe ausstehend bleiben.
Der Schalter trennt Übung und Lieferung und beginnt mit ausschließlich den früheren verfügbaren Handouts:

Die Spalte Practice attempt ist eine getrennte Kopie der Trainingswarteschlange, kein zweiter Rechner. Jede Spalte hat eigene Ausgabe und Worker-Zustand. Ein aktuelles PDF wird erst nach Fertigstellung des ganzen Dokuments herunterladbar. Das interne Rendern von Seite 1 erzeugt noch keine vollständige PDF-Datei.
Beobachten, warum ein Übungsstopp fehlschlägt
Wechsle zu Workstation, das in /home/labex/project/print-signal startet. cd führt dorthin zurück und cat liest eine Datei:
cd /home/labex/project/print-signal
cat compose.trial.yaml
Compose gruppiert Einstellungen unter services.worker. Sein vorbereitetes Image enthält Worker und PDF-Bibliotheken. user entspricht bereits deinen tatsächlichen numerischen Konto-IDs, sodass Ausgabe bearbeitbar bleibt; ersetze diese Werte nicht. Das schreibgeschützte /input enthält Originalaufträge, /work ist der beschreibbare Zustands-/Ausgabeort dieses Versuchs, und /entry.sh ist sein schreibgeschütztes Einstiegsskript. :ro bedeutet schreibgeschützt. entrypoint: ["/bin/sh", "/entry.sh"] startet diese Shell direkt als Containerprozess 1, genannt PID 1. Ein Container teilt den VM-Kernel, hat aber eine eigene Prozesssicht.
stop_grace_period: 10s gibt der Anwendung ein begrenztes zehnsekündiges Zeitfenster zum Beenden. Docker sendet zuerst SIGTERM, die normale Beendigungsanforderung. Nach Fristablauf kann es SIGKILL senden, das die Prozesse zwangsweise beendet und nicht behandelt werden kann, um Arbeit fertigzustellen. Ein Signal an PID 1 wird nicht automatisch an jedes Kind weitergeleitet.
Der vorhandene Übungseinstieg startet Python mit & als Hintergrundkind, dann wartet wait darauf. Die Shell bleibt PID 1. Lies dieses bereitgestellte Skript:
cat trial-entry.sh
Die Shell hat keinen Weiterleitungs-Handler. PID 1 eines Containers hat ein besonderes Standardsignalverhalten, sodass diese Shell weiterleben kann, während Python die TERM-Anforderung nie erhält.
Starte nur das Übungsprojekt. -f wählt seine Datei, -p gruppiert seine Ressourcen, up -d startet im Hintergrund, und die letzten Optionen nutzen vorbereitete Images ohne Pulls oder Builds:
docker compose -f compose.trial.yaml -p print-trial up -d --pull never --no-build
Wechsle zu Print Desk, lade neu und warte, bis Practice attempt Training pause after page 1; waiting for stop request zeigt. Der Worker hat Seite 1 tatsächlich gerendert und wartet absichtlich. Es gibt kein enges Zeitfenster.
Wechsle zu Workstation. docker exec führt einen Befehl in einem vorhandenen Container aus. Linux stellt Prozessinformationen unter /proc bereit; /proc/1/comm nennt in diesem Container seinen PID-1-Befehl. Diese schreibgeschützte Beobachtung sollte sh zeigen:
docker exec print-trial cat /proc/1/comm
stop fordert diesen Dienst mit seiner konfigurierten Frist zum Stoppen auf:
docker compose -f compose.trial.yaml -p print-trial stop worker
Der Stoppbefehl selbst kann mit 0 enden, obwohl der Worker zwangsweise beendet wurde: Das bedeutet, dass Docker den Stopp bearbeitet hat, nicht dass das Programmheft fertig wurde. docker inspect liest den aufbewahrten Container. Die Vorlage in einfachen Anführungszeichen wählt seinen Worker-Exit-Code:
docker inspect print-trial --format '{{.State.ExitCode}}'
docker logs print-trial
Erwarte Exit-Code 137 und kein Worker-Ereignis stop-received oder completed. In diesem kontrollierten Versuch lief die Frist ab und Docker erzwang den Stopp; 137 allein ist keine allgemeine Diagnose jedes Containerfehlers. Das Log enthält ein JSON-Ereignis pro Zeile: event benennt die Beobachtung, job identifiziert das Programmheft und page dokumentiert tatsächlichen Renderfortschritt.
Wechsle zu Print Desk und lade neu. Die Übung hat kein fertiges aktuelles PDF, während ihr früheres Handout und die unberührte Lieferwarteschlange verfügbar bleiben.
Die Shell durch den Worker ersetzen
Wechsle zu Workstation. Bearbeite nur den Liefereinstieg entry.sh und erhalte trial-entry.sh, Programmeingaben, vorhandene PDFs und andere Einstellungen. Shell-exec ersetzt den aktuellen Shell-Prozess durch das benannte Programm. Python wird dadurch PID 1, und sein bereitgestellter TERM-Handler empfängt Dockers Anforderung.
Dieses wörtliche Here-Dokument schreibt das vollständige kurze Skript. > ersetzt entry.sh; die in Anführungszeichen stehende Markierung SH verhindert Shell-Ersetzungen bis zur Schlusszeile. Die Datei benötigt kein Ausführungsbit, weil das konfigurierte /bin/sh sie liest:
cat > entry.sh <<'SH'
#!/bin/sh
exec python3 -B /app/worker.py
SH
cat entry.sh
Starte das unabhängige Lieferprojekt, dessen zehnsekündige Frist für dieses dreiseitige Handout bereits ausreicht:
docker compose -p print-delivery up -d --pull never --no-build
Wechsle zu Print Desk, lade neu und warte auf die Trainingspausenmeldung unter Delivery attempt. Kehre zu Workstation zurück und lies PID 1 erneut:
docker exec print-delivery cat /proc/1/comm
Erwarte python3 statt der wartenden Shell. Fordere einen normalen Stopp an:
docker compose -p print-delivery stop worker
docker inspect print-delivery --format '{{.State.ExitCode}}'
docker logs print-delivery
Erwarte Exit-Code 0. Derselbe Worker protokolliert stop-received, verbleibende page-completed-Ereignisse, completed für das aktuelle Programmheft und worker-stopped. Ein neuer Prozess, der später das fehlende PDF ergänzt, würde nicht beweisen, dass dieser Worker seinen Auftrag beendet hat.
Die gelieferte Datei und erhaltene Warteschlange untersuchen
Wechsle zu Print Desk, lade neu und öffne Preview text für das aktuelle Handout unter Delivery attempt. Alle drei dekodierten Seiten mit Abschnittsüberschriften, Programm-ID und Seitennummern sollten vorhanden sein. Lade das tatsächliche PDF herunter und öffne es. Kehre zum Schalter zurück: Morning welcome handout muss weiterhin seine ursprünglichen zwei Seiten haben, und Volunteer notes muss ohne erzeugtes PDF ausstehend bleiben. Behalte den normal gestoppten Container print-delivery für seine nativen Inspektionsnachweise.
Ist ein Name falsch geschrieben, lies den Fehler und nutze den angegebenen Containernamen; beispielsweise findet docker logs print-workr diese Lieferung nicht, während das gezeigte docker logs print-delivery sie liest. Erstelle kein Ersatz-PDF von Hand. Prüfungen lesen die Stoppnachweise aus deiner tatsächlichen Lab-Umgebung und dekodieren jede Seite; danach wiederholen sie deinen gespeicherten Einstieg in einer isolierten Programm-Testumgebung, ohne den tatsächlichen Fortschritt zu verändern.
Einen erfolglosen Lieferversuch wiederherstellen
Wenn dein Lieferversuch fehlschlägt, behalte den gestoppten Container lange genug, um Logs und Exit-Status zu lesen. Korrigiere nur Einstiegs- oder Fristkonfiguration. Die bereitgestellte Wiederherstellungsaktion verweigert einen laufenden oder erfolgreich beendeten Container; bei einem erfolglosen gestoppten Kurs-Worker stellt sie nur den aktuellen Auftrag zurück in die Warteschlange und bewahrt fehlgeschlagene Artefakte in delivery/attempt-history/ auf. Sie erhält das frühere PDF, Originaleingaben und den späteren ausstehenden Auftrag:
python3 /opt/labex/dk05/source/retry.py
Entferne nach dieser expliziten Wiederherstellung nur die Container des fehlgeschlagenen Lieferprojekts mit docker compose -p print-delivery down und wiederhole die gezeigte Folge aus Lieferstart, Warten auf die Pause und Stopp. Kein Quell-PDF wird neu erzeugt, und diese Wiederherstellung wird niemals durch einen Tab oder eine Bewertung ausgeführt. Setze die VM nicht zurück und ändere kein Warteschlangen-JSON, um einen fehlgeschlagenen Versuch zu verbergen.
Zusammenfassung
Du hast gesehen, wie eine wartende PID-1-Shell eine Stoppanforderung vor ihrem Kindprozess verbergen kann, und dann exec genutzt, damit der bereitgestellte Worker TERM direkt empfängt. Der ursprüngliche Liefer-Worker stellte sein dreiseitiges PDF fertig, endete normal, erhielt das Morgendokument und ließ den nächsten Auftrag ausstehend.



