Введение
Paper & Pine превращает задания в очереди в настоящие PDF-программы мероприятий. Перед приостановкой производства нужно завершить текущий трёхстраничный раздаточный материал, оставить следующее задание ожидающим и сохранить готовый утренний PDF. Предоставленный обработчик Python уже умеет завершать текущее задание после запроса остановки, но его входная оболочка сейчас мешает доставить ему этот запрос.
Вы должны знать пути терминала, основы Docker и журналы контейнеров. Небольшой файл Compose и используемое поведение оболочки объясняются перед применением. Программирование на Python и физический принтер не нужны: результат — скачиваемый PDF, содержимое которого можно декодировать.
Используйте Workstation и Print Desk. Независимые тренировочная очередь и очередь поставки позволяют изучить один контролируемый сбой без потери прогресса поставки. Учебный режим останавливается после первой отрисованной страницы до получения запроса остановки, затем добавляет явно заданную паузу 0,5 секунды между оставшимися страницами. Эти паузы делают решение видимым и не служат измерением скорости производства. Выделите около 45 минут с учётом чтения и обычных ошибок; это оценка автора.
Доставьте запрос остановки обработчику
На этом шаге вы увидите неудачную тренировочную остановку, исправите входную оболочку и выполните настоящую поставку тем же обработчиком, который получает запрос остановки.
Начните с настоящей готовой программы
Начните в Print Desk. В Delivery attempt откройте Preview text для Morning welcome handout и выберите Download PDF. Предпросмотр декодируется из сохранённого двухстраничного PDF, а не восстанавливается из исходного текста. Вернитесь через Back to Print Desk. Текущий Community open day handout ожидает; Volunteer notes должен остаться ожидающим после этой передачи.
Стенд разделяет тренировку и поставку; изначально доступны только более ранние раздаточные материалы:

Столбец Practice attempt — отдельная копия учебной очереди, а не вторая машина. У каждого столбца свои результаты и состояние обработчика. Текущий PDF становится доступен для скачивания только после окончательного формирования всего документа. Внутренняя отрисовка страницы 1 ещё не создаёт полный PDF-файл.
Посмотрите, почему тренировочная остановка не удаётся
Переключитесь в Workstation, начиная в /home/labex/project/print-signal. cd возвращает туда, а cat читает файл:
cd /home/labex/project/print-signal
cat compose.trial.yaml
Compose группирует настройки в services.worker. Подготовленный образ содержит обработчик и библиотеки PDF. user уже соответствует фактическим числовым ID вашей учётной записи, чтобы результат оставался редактируемым; не заменяйте эти значения. /input только для чтения содержит исходные задания, /work — записываемое состояние и результат этой попытки, а /entry.sh — входной скрипт только для чтения. :ro означает «только для чтения». entrypoint: ["/bin/sh", "/entry.sh"] запускает эту оболочку напрямую как процесс контейнера 1, называемый PID 1. Контейнер разделяет ядро VM, но имеет собственное представление процессов.
stop_grace_period: 10s даёт приложению конечное десятисекундное окно завершения. Docker сначала отправляет SIGTERM, обычный запрос завершения. После истечения срока он может отправить SIGKILL, принудительно завершающий процессы и не позволяющий обработать сигнал для окончания работы. Отправка сигнала PID 1 не пересылает его автоматически каждому потомку.
Существующий тренировочный скрипт запускает Python фоновым потомком с &, затем wait ожидает его. Оболочка остаётся PID 1. Прочитайте предоставленный скрипт:
cat trial-entry.sh
В оболочке нет обработчика пересылки. PID 1 контейнера имеет особое поведение сигналов по умолчанию, поэтому эта оболочка может оставаться живой, пока Python так и не получит запрос TERM.
Запустите только тренировочный проект. -f выбирает файл, -p группирует ресурсы, up -d запускает его в фоне, а последние параметры используют подготовленные образы без скачивания и сборки:
docker compose -f compose.trial.yaml -p print-trial up -d --pull never --no-build
Переключитесь в Print Desk, обновите страницу и дождитесь, пока Practice attempt покажет Training pause after page 1; waiting for stop request. Обработчик действительно отрисовал страницу 1 и намеренно ждёт. Узкого временного окна нет.
Переключитесь в Workstation. docker exec выполняет команду в существующем контейнере. Linux предоставляет сведения о процессах через /proc; /proc/1/comm внутри этого контейнера именует команду его PID 1. Это наблюдение только для чтения должно показать sh:
docker exec print-trial cat /proc/1/comm
stop запрашивает остановку сервиса с настроенным сроком ожидания:
docker compose -f compose.trial.yaml -p print-trial stop worker
Сама команда stop может завершиться с кодом 0 даже при принудительном уничтожении обработчика: это означает выполнение операции остановки Docker, а не завершение программы мероприятия. docker inspect читает сохранённый контейнер. Шаблон в одинарных кавычках выбирает код завершения обработчика:
docker inspect print-trial --format '{{.State.ExitCode}}'
docker logs print-trial
Ожидайте код 137 и отсутствие событий обработчика stop-received или completed. В этой контролируемой попытке срок истёк и Docker принудительно остановил процесс; само по себе 137 не является универсальным диагнозом каждой ошибки контейнера. В журнале по одному событию JSON на строку: event именует наблюдение, job определяет программу мероприятия, а page фиксирует фактический прогресс отрисовки.
Переключитесь в Print Desk и обновите страницу. У тренировки нет окончательного текущего PDF, а более ранний материал и нетронутая очередь поставки остаются доступны.
Замените оболочку обработчиком
Переключитесь в Workstation. Редактируйте только entry.sh поставки, сохранив trial-entry.sh, входные программы мероприятий, существующие PDF и остальные настройки в целости. exec оболочки заменяет текущий процесс оболочки указанной программой. Поэтому Python становится PID 1, и его предоставленный обработчик TERM получает запрос Docker.
Этот буквальный here-document записывает полный короткий скрипт. > заменяет entry.sh; маркер SH в кавычках предотвращает подстановки оболочки до закрывающей строки. Файлу не нужен бит исполняемости, поскольку настроенная /bin/sh читает его:
cat > entry.sh <<'SH'
#!/bin/sh
exec python3 -B /app/worker.py
SH
cat entry.sh
Запустите независимый проект поставки, чей десятисекундный срок уже достаточен для этого трёхстраничного материала:
docker compose -p print-delivery up -d --pull never --no-build
Переключитесь в Print Desk, обновите страницу и дождитесь сообщения учебной паузы Delivery attempt. Вернитесь в Workstation и снова прочитайте PID 1:
docker exec print-delivery cat /proc/1/comm
Ожидайте python3 вместо ожидающей оболочки. Запросите обычную остановку:
docker compose -p print-delivery stop worker
docker inspect print-delivery --format '{{.State.ExitCode}}'
docker logs print-delivery
Ожидайте код завершения 0. Тот же обработчик записывает stop-received, оставшиеся события page-completed, completed для текущей программы и worker-stopped. Новый процесс, который позднее заполняет недостающий PDF, не доказал бы завершение задания этим обработчиком.
Изучите переданный файл и сохранённую очередь
Переключитесь в Print Desk, обновите страницу и откройте Preview text текущего материала в Delivery attempt. Должны присутствовать все три декодированные страницы, заголовки разделов, ID программы и номера страниц. Скачайте настоящий PDF и откройте его. Вернитесь к стенду: Morning welcome handout должен сохранить исходные две страницы, а Volunteer notes — оставаться ожидающим без созданного PDF. Сохраните штатно остановленный контейнер print-delivery для свидетельств его собственного состояния.
Если ошиблись в имени, прочитайте ошибку и используйте указанное имя контейнера; например, docker logs print-workr не найдёт эту поставку, а показанная docker logs print-delivery прочитает её. Не создавайте подменный PDF вручную. Проверки читают фактические сведения об остановке в вашем учебном окружении и декодируют каждую страницу, затем воспроизводят сохранённый входной скрипт в изолированном тестовом окружении программы мероприятия без изменения вашего текущего прогресса.
Восстановите неудачную поставку
Если попытка поставки не удалась, сохраните остановленный контейнер достаточно долго для чтения журналов и кода завершения. Исправляйте только входной скрипт или настройку срока. Предоставленное действие восстановления отказывает для работающего или успешно завершённого контейнера; для неудачного остановленного учебного обработчика оно возвращает в очередь только текущее задание и сохраняет неудачные артефакты в delivery/attempt-history/. Более ранний PDF, исходные входные данные и последующее ожидающее задание сохраняются:
python3 /opt/labex/dk05/source/retry.py
После этого явного восстановления удалите только контейнеры неудачного проекта поставки через docker compose -p print-delivery down, затем повторите показанную последовательность запуска поставки, ожидания паузы и остановки. Ни один исходный PDF не создаётся заново; это восстановление никогда не запускается вкладкой или оценочной проверкой. Не сбрасывайте VM и не меняйте JSON очереди для сокрытия неудачной попытки.
Резюме
Вы увидели, как ожидающая оболочка с PID 1 может скрыть запрос остановки от дочернего процесса, затем использовали exec, чтобы предоставленный обработчик получал TERM напрямую. Исходный обработчик поставки завершил трёхстраничный PDF, штатно завершился, сохранил утренний документ и оставил следующее задание ожидающим.



