送达停止信号

DockerBeginner
立即练习

介绍

Paper & Pine 将队列中的活动节目单制作成真实 PDF。暂停生产前,必须完成当前三页讲义,让下一个任务保持待处理,并保留已完成的上午 PDF。提供的 Python worker 已经知道如何在收到停止请求后完成当前任务,但其入口 shell 目前阻止了请求送达。

你应了解终端路径、基本 Docker 和容器日志。这里使用的简短 Compose 文件和 shell 行为都会在使用前解释。无需 Python 编程或实体打印机:成果是可下载、可解码的 PDF。

使用 Workstation 和 Print Desk。练习队列与交付队列彼此独立,让你能观察一次受控失败而不丢失交付进度。训练模式在渲染第一页后暂停,等待停止请求,然后在剩余页面之间加入已说明的 0.5 秒暂停。这些暂停用于展示判断过程,不是生产速度基准。包含阅读和普通错误在内,预计约 45 分钟;这是作者估计。

将停止请求送达 worker

本步骤观察一次失败的练习停止操作,修复入口 shell,并让接收停止请求的同一个 worker 完成真实交付。

从已完成的真实节目单开始

从 Print Desk 开始。在 Delivery attempt 下,打开 Morning welcome handout 的 Preview text,并选择 Download PDF。预览由保存的两页 PDF 解码得到,不是从源文本重建的。使用 Back to Print Desk 返回。当前 Community open day handout 处于待处理状态;本次交接后 Volunteer notes 必须仍为待处理。

服务台区分练习与交付,初始时只有较早的讲义可用:

制作当前讲义前的 Print Desk

Practice attempt 列是训练队列的独立副本,不是第二台机器。每列有自己的输出和 worker 状态。当前 PDF 只有在整个文档最终生成后才可下载。内部已渲染第 1 页,并不代表已有完整 PDF 文件。

观察练习停止为何失败

切换到 Workstation,从 /home/labex/project/print-signal 开始。cd 返回该目录,cat 读取文件:

cd /home/labex/project/print-signal
cat compose.trial.yaml

Compose 将设置归在 services.worker 下。已准备的镜像包含 worker 和 PDF 库。user 已匹配你实际账号的数字 ID,因此输出仍可编辑;不要替换这些值。只读的 /input 包含原始任务,/work 是本次尝试可写的状态/输出位置,/entry.sh 是只读入口脚本。:ro 表示只读。entrypoint: ["/bin/sh", "/entry.sh"] 将 shell 直接启动为容器进程 1,称为 PID 1。容器共享 VM 内核,但有自己的进程视图。

stop_grace_period: 10s 给应用有限的十秒关闭窗口。Docker 先发送 SIGTERM,即正常的终止请求。宽限期结束后,它可以发送 SIGKILL,强制结束进程;进程无法处理此信号来完成工作。向 PID 1 发信号,不会自动转发给每个子进程。

现有练习入口用 & 将 Python 启动为后台子进程,再用 wait 等待它。shell 仍是 PID 1。阅读所提供的脚本:

cat trial-entry.sh

shell 没有转发处理器。容器 PID 1 有特殊的默认信号行为,因此 shell 可能保持存活,而 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。worker 已真实渲染第 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

即使 worker 被强制终止,stop 命令自身仍可能以 0 退出:它表示 Docker 处理了停止操作,不表示节目单已完成。docker inspect 读取保留的容器。单引号中的模板选择 worker 退出代码:

docker inspect print-trial --format '{{.State.ExitCode}}'
docker logs print-trial

预期退出代码为 137,且没有 worker 的 stop-received 或 completed 事件。在这个受控练习中,宽限期已到,Docker 强制停止;单凭 137 不能对所有容器故障作出通用诊断。日志每行是一个 JSON 事件:event 命名观察,job 标识节目单,page 记录实际渲染进度。

切换到 Print Desk 并刷新。练习没有最终生成当前 PDF,而较早讲义和未触碰的交付队列仍可用。

用 worker 替换 shell

切换到 Workstation。只编辑交付的 entry.sh,保持 trial-entry.sh、节目单输入、现有 PDF 和其他设置不变。shell 的 exec 用指定程序替换当前 shell 进程。因此 Python 成为 PID 1,其已有的 TERM 处理器会收到 Docker 请求。

以下按字面保存的 here-document 写出完整短脚本。> 替换 entry.sh;加引号的 SH 标记在结束行之前阻止 shell 展开。文件无需可执行位,因为配置的 /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,而非等待中的 shell。请求正常停止:

docker compose -p print-delivery stop worker
docker inspect print-delivery --format '{{.State.ExitCode}}'
docker logs print-delivery

预期退出代码为 0。同一个 worker 记录 stop-received、剩余的 page-completed 事件、当前节目单的 completed,以及 worker-stopped。后来启动新进程补齐缺失 PDF,不能证明这个 worker 完成了手头任务。

检查交付文件与保留队列

切换到 Print Desk,刷新,在 Delivery attempt 下打开当前讲义的 Preview text。应包含全部三页解码内容、各节标题、节目单 ID 和页码。下载真实 PDF 并打开。返回服务台:Morning welcome handout 必须仍保留原始两页,Volunteer notes 必须保持待处理且没有生成 PDF。保留正常停止的 print-delivery 容器,供原生证据检查使用。

如果名称拼错,阅读错误并使用指定容器名;例如 docker logs print-workr 找不到本次交付,而所示 docker logs print-delivery 能读取它。不要手工创建替代 PDF。检查读取实时停止事实并解码每一页,然后在隔离节目单 fixture 中重放保存的入口,不改变实时进度。

恢复失败的交付

如果交付尝试失败,先保留停止的容器,读取日志和退出状态。仅修正入口或宽限期配置。提供的恢复操作会拒绝正在运行或已成功完成的容器;对于失败且已停止的课程 worker,它只将当前任务重新排队,并将失败产物保留在 delivery/attempt-history/。它保留较早 PDF、原始输入及后续待处理任务:

python3 /opt/labex/dk05/source/retry.py

显式恢复后,用 docker compose -p print-delivery down 仅移除失败交付项目的容器,再重复所示交付启动、等待暂停和停止流程。不会重新生成源 PDF,标签页或评分检查也绝不会运行此恢复。不要重置 VM 或更改队列 JSON 来掩盖失败尝试。

总结

你观察到保持等待的 PID 1 shell 如何使子进程收不到停止请求,随后使用 exec 让提供的 worker 直接接收 TERM。原交付 worker 完成三页 PDF,正常退出,保留上午文档,并让下一任务保持待处理。