docs(release): organize installation and operator guides by task
This commit is contained in:
+125
-118
@@ -1,10 +1,12 @@
|
||||
# 本地开发依赖
|
||||
|
||||
管理员账号 bootstrap/恢复、有界题包解析服务、不可变发布/下架、审计与回收步骤见
|
||||
[题目维护运维说明](admin-problems.md)。该说明与服务模板用于受控部署准备,不表示已部署生产。
|
||||
开发环境用 Compose 提供数据库和消息队列。单机交付使用独立的受限服务,安装入口见
|
||||
[单机交付说明](release/README.md)。管理员账号、题包导入、发布、下架和审计见
|
||||
[题目维护运维说明](admin-problems.md)。
|
||||
|
||||
`compose.dev.yml` 只提供 PostgreSQL 和 RabbitMQ,端口绑定 localhost。
|
||||
先复制 `.env.example` 为 `.env`,然后在仓库根目录运行:
|
||||
## 启动开发依赖
|
||||
|
||||
将 `.env.example` 复制为 `.env`,在仓库根目录运行:
|
||||
|
||||
```bash
|
||||
make infra-config
|
||||
@@ -12,75 +14,73 @@ make infra-up
|
||||
make infra-down
|
||||
```
|
||||
|
||||
`infra-down` 保留数据卷;不要把示例口令用于正式部署。
|
||||
API 存活检查和前端预览不依赖这些容器,可以先独立启动。
|
||||
该 Compose 不运行判题 Worker 或不可信代码。
|
||||
`compose.dev.yml` 只启动 PostgreSQL 和 RabbitMQ,端口绑定 localhost。
|
||||
`infra-down` 保留数据卷。API 存活检查和前端预览可以先独立启动。
|
||||
不要在这个 Compose 或宿主机上运行学生代码,也不要将示例口令用于正式部署。
|
||||
|
||||
镜像固定具体维护版本(PostgreSQL 18.6、RabbitMQ 4.3.6),正式实验前另锁镜像摘要。
|
||||
配置检查只验证 Compose 结构;成功启动、实际健康与备份恢复需要单独验证。
|
||||
后续受控主机服务和最小权限沙箱启动器配置放在本目录。
|
||||
开发镜像固定 PostgreSQL 18.6 和 RabbitMQ 4.3.6;正式实验还需锁定镜像摘要。
|
||||
配置检查只检查 Compose 结构。服务健康和备份恢复要在实际环境验证。
|
||||
镜像用法见官方 [PostgreSQL](https://hub.docker.com/_/postgres) 和
|
||||
[RabbitMQ](https://hub.docker.com/_/rabbitmq) 说明。
|
||||
|
||||
官方镜像说明:[PostgreSQL](https://hub.docker.com/_/postgres)、
|
||||
[RabbitMQ](https://hub.docker.com/_/rabbitmq)。
|
||||
## 单机 helper 工厂
|
||||
|
||||
### 单机 helper 工厂
|
||||
`kf-sandbox factory --config /etc/kf-judge/runtime.json` 启动 Root 工厂。
|
||||
普通 Worker 连接配置中的 `state_root/factory/control.sock`。
|
||||
安装 `deploy/kf-sandbox-factory.service` 示例前,管理员需核对固定二进制、不可写的
|
||||
GNU 镜像与配置、Worker UID/GID,以及 Root 所有且与监督器 cgroup 分离的任务委派根。
|
||||
工厂读回服务和任务预算,每次编译、测试点运行都经过完整隔离检查。
|
||||
|
||||
`kf-sandbox factory --config /etc/kf-judge/runtime.json` 是 Root 服务入口;
|
||||
普通 Worker 使用配置中 `state_root/factory/control.sock` 的普通用户客户端。
|
||||
`deploy/kf-sandbox-factory.service` 给出有限服务预算示例,安装前由可信管理员
|
||||
核对固定二进制、不可写 GNU 镜像/配置、Worker UID/GID,以及**与服务自身 cgroup
|
||||
分离**的 Root 所有任务委派根。工厂实际读回服务 cgroup 限额,应用/读回任务总预算;
|
||||
每次编译和测试点仍须通过已有完整隔离门控。服务文件本身不证明这些条件成立。
|
||||
工厂无等待队列,只接收有界 run ID。槽位满、重复 ID 和未知字段立即拒绝。
|
||||
每次请求创建全新的单线程 helper,继承已认证连接和工厂生命线。
|
||||
额外控制帧、断连或生命线消失会终止任务树。ID 先进入持久记录,最多 4096 个,
|
||||
跨重启继续拒绝重放。清理失败时保留身份租约并停止分配;恢复只处理已记录的
|
||||
归属和 inode,未知资源保留并拒绝启动。
|
||||
|
||||
工厂只接收有界 run ID,无排队:槽位满、重复 ID 或未知字段立即拒绝。
|
||||
每次申请使用全新单线程 helper,固定继承已认证的连接与工厂生命线;
|
||||
额外控制帧、断开或生命线消失会终止整棵任务树。ID 先写入有限持久记录
|
||||
(最多 4096 个,跨重启拒绝重放);容量用尽需要管理员在所有任务停止、已核对
|
||||
回收后安排新记录周期,不自动删除记录。清理失败保留身份租约并停止新分配;
|
||||
重启只根据已记录归属和 inode 回收,未知/外来内容保留并拒绝启动。
|
||||
### 轮换记录周期
|
||||
|
||||
工厂 `--max-requests` 由可信 Root 启动参数选择,范围1–4096,默认4096;
|
||||
它限制当前记录周期的受理次数,不改变学生预算或已记录 ID 的拒绝规则。
|
||||
`kf-sandbox rotate-factory-cycle --config ROOT_CONFIG --cycle-id ID` 是独立的
|
||||
Root 维护入口:在受限监督器作用域中,核对受保护配置/镜像、取得工厂排他锁,
|
||||
并读回任务、身份、捕获目录和委派 cgroup 为空、控制 socket 已消失后,才把
|
||||
当前请求记录保留到 Root 私有 `factory/cycles/`,fsync文件与目录后清空当前记录。
|
||||
最多保留64个有界周期;历史 ID 在后续启动仍用于拒绝重放。正常启动、重启、
|
||||
寿命结束和容量用尽不会调用轮换或删除任何历史记录。归档完成但当前记录未清空
|
||||
的中断状态拒绝启动,需在停止/排空后显式使用同一个 cycle ID 重试;未知或
|
||||
不完整记录保留并拒绝,不能通过删日志恢复。该入口不启动服务,也不证明隔离。
|
||||
新安装路径下的实际轮换、崩溃恢复、跨周期重放拒绝与资源读回仍需独立 VM 验收。
|
||||
可信 Root 参数 `--max-requests` 设置本周期受理上限,范围 1 到 4096,默认 4096。
|
||||
容量用尽后,先停止任务并核对回收,再由管理员维护记录周期。
|
||||
|
||||
`kf-worker judge-submission` 是受控本地适配入口。常驻 `kf-worker serve --config`
|
||||
使用有界 AMQP 通知和经过服务身份认证的 Coordinator claim;配置与依赖见
|
||||
[判题工程说明](../judge/README.md#单机持久化协调与常驻-worker)。
|
||||
它验证不可变清单摘要,C17/C++20 仅映射到固定 GNU 命令,编译一次;
|
||||
成功产物经完整清理后封印并计算 SHA-256,每点分别申请 helper、stdin 与工作区。
|
||||
期望值逐点读取,留在比较端,不传给工厂/用户文件系统。
|
||||
新增锁定 `sha2 0.10.9` 仅供 Worker 验证输入及编译产物摘要;
|
||||
Sandbox 同版本仅为独立 VM 测试数据生成的开发依赖,Engine 不依赖加密或 OS 库。
|
||||
`kf-sandbox rotate-factory-cycle --config ROOT_CONFIG --cycle-id ID` 在受限监督器作用域
|
||||
中核对配置和镜像、取得工厂排他锁,确认任务、身份、捕获目录、委派 cgroup 均为空,
|
||||
且控制 socket 已消失,才归档请求记录到 Root 私有 `factory/cycles/`。
|
||||
归档文件和目录经 fsync 后清空当前记录,最多保留 64 个周期;历史 ID 继续用于拒绝重放。
|
||||
|
||||
### 单机提交服务的启动与停止
|
||||
正常启动、重启、寿命结束和容量用尽不会自动轮换或删除历史记录。
|
||||
若归档完成后中断,先排空,再显式用同一 cycle ID 重试。未知或不完整记录需要检查,
|
||||
不能删日志续用工厂。轮换入口不启动服务。新安装路径的轮换、崩溃恢复和跨周期拒绝
|
||||
仍需独立 VM 验收。
|
||||
|
||||
1. Backend Alembic 迁移;创建分别仅继承平台/协调器 NOLOGIN 角色的专用登录账号,
|
||||
平台角色只可插入初始评测,执行状态仅由 Coordinator 写入。
|
||||
2. 普通平台服务持有私有工件目录;使用应用服务导入不可变题目和创建测试账号。
|
||||
3. 验证固定 GNU 镜像、Root 配置及工厂服务/任务 cgroup 的真实上限,再启动工厂。
|
||||
4. 独立启动 API、Coordinator、outbox publisher 和普通 Worker;单机 HTTP/AMQP
|
||||
仅数值 loopback,PostgreSQL loopback/Unix socket。任何入口都不自动创建表。
|
||||
5. Coordinator readiness 验证数据库和权限;仍需独立 VM 验证真实执行门控。
|
||||
Root 工厂不可持有平台/MQ/数据库口令,用户运行镜像不含服务文件。
|
||||
6. 先向 Worker 发送 SIGTERM,使当前任务取消/清理,再停止 publisher/API/Coordinator,
|
||||
最后停止工厂和本地依赖。强制死亡后的旧 attempt 必须撤销并取得实际停止证明;
|
||||
`cleanup_failed` 不等于普通终态,先核对保留资源再由管理员恢复工厂/Worker。
|
||||
## 单机提交服务的启动与停止
|
||||
|
||||
平台配置私有 `KF_OJ_ARTIFACT_ROOT`、有界字节/文件容量以及
|
||||
`KF_OJ_JUDGE_ADMISSION`(完整可信 JSON);其工具链、编译/运行上限、总体作业期限、
|
||||
并发/prefetch/retry 必须与 Coordinator/Worker 一致。单机使用并发1、prefetch1,
|
||||
不能仅提高 Worker 数量绕过节点容量。源码最多256KiB,测试点最多256个;
|
||||
stdout/stderr 各不超过 file_bytes,合计不超过 workdir_bytes。
|
||||
`kf-worker judge-submission` 是受控本地适配入口;常驻
|
||||
`kf-worker serve --config` 使用有界 AMQP 通知和认证后的 Coordinator claim。
|
||||
配置见[判题工程说明](../judge/README.md#单机持久化协调与常驻-worker)。
|
||||
|
||||
迁移账号与平台账号分开配置,命令均在 `backend/` 运行:
|
||||
Worker 校验不可变清单摘要,将 C17/C++20 映射到固定 GNU 命令,编译一次。
|
||||
产物经完整清理后封印并计算 SHA-256;每个测试点单独申请 helper、stdin 和工作区。
|
||||
期望输出留在比较端。Worker 用 `sha2 0.10.9` 校验输入与产物;Sandbox 的同版本
|
||||
开发依赖仅生成独立 VM 测试数据,Engine 不依赖加密或 OS 库。
|
||||
|
||||
### 启动顺序
|
||||
|
||||
1. 用独立迁移账号执行 Backend Alembic。平台与协调器登录账号分别继承各自的
|
||||
NOLOGIN 角色;平台只插入初始评测,执行状态由 Coordinator 写入。
|
||||
2. 为平台准备私有工件目录,通过应用服务导入不可变题目并创建测试账号。
|
||||
3. 验证 GNU 镜像、Root 配置、工厂服务和任务 cgroup 的实际限额,启动工厂。
|
||||
4. 独立启动 API、Coordinator、outbox publisher 和普通 Worker。HTTP/AMQP 使用
|
||||
数值 loopback,PostgreSQL 使用 loopback/Unix socket;启动入口不创建表。
|
||||
5. 检查 Coordinator 数据库权限与 readiness,再在独立 VM 验证执行隔离。
|
||||
Root 工厂不持有平台/MQ/数据库口令,用户运行镜像不含服务文件。
|
||||
|
||||
平台配置包括私有 `KF_OJ_ARTIFACT_ROOT`、工件容量和可信
|
||||
`KF_OJ_JUDGE_ADMISSION`。工具链、编译/运行预算、作业期限、并发/prefetch/retry
|
||||
必须与 Coordinator/Worker 一致。单机并发 1、prefetch 1,共享任务身份池。
|
||||
源码最多 256 KiB,测试点最多 256 个;stdout/stderr 各不超过 file_bytes,
|
||||
合计不超过 workdir_bytes。增加 Worker 数量不会扩大节点槽位。
|
||||
|
||||
以下开发命令均在 `backend/` 运行;迁移账号与平台账号分别配置:
|
||||
|
||||
```sh
|
||||
uv run --locked alembic upgrade head # 私有迁移账号,仅执行受控 schema 操作
|
||||
@@ -91,70 +91,77 @@ uv run --locked uvicorn kf_oj.api.app:create_app --factory --host 127.0.0.1 --po
|
||||
uv run --locked python -m kf_oj.infra.messaging.publisher
|
||||
```
|
||||
|
||||
每个账号操作/导入目录都由管理员准备,不从浏览器接受私有路径。导入格式见
|
||||
[题目模块](../backend/src/kf_oj/modules/problems/README.md),不可变版本以 UUID
|
||||
在题库/题面链接和提交请求中传递。更新题目需导入新版本,不覆盖已有提交的题目版本。
|
||||
临时验收使用合成数据和测试口令,实际业务数据与服务配置都保存在仓库外的私有目录。
|
||||
账号和导入目录由管理员准备,浏览器不能传入私有路径。导入格式见
|
||||
[题目模块](../backend/src/kf_oj/modules/problems/README.md)。题库、题面和提交使用
|
||||
不可变版本 UUID;更新需发布新版本。临时验收使用合成数据和测试口令,业务数据与
|
||||
配置放在仓库外。
|
||||
|
||||
取消另需 API 私有配置 `KF_OJ_COORDINATOR_URL=http://127.0.0.1:8100` 与
|
||||
`KF_OJ_COORDINATOR_TOKEN`(Coordinator 的 platform_token)。所有服务令牌私下生成,
|
||||
不可互用或放进前端环境。Coordinator/Worker 私有 JSON 为0600、单链接、有界文件;
|
||||
Root 镜像/配置/二进制由 root 持有,任务委派子树独立于监督器自身 cgroup。
|
||||
运行前实际读回整体节点、服务和任务 memory/swap/pids/CPU/文件/运行期限上限,
|
||||
并通过真实隔离门控;配置解析、readiness、存活探测都不能替代执行验收。
|
||||
取消功能还需 API 配置 `KF_OJ_COORDINATOR_URL=http://127.0.0.1:8100` 和
|
||||
`KF_OJ_COORDINATOR_TOKEN`,后者对应 Coordinator 的 platform_token。
|
||||
独立生成服务令牌,保存在私有配置中。Coordinator/Worker JSON 为 0600、单链接、
|
||||
有界文件;Root 持有镜像、配置和二进制。运行前读回节点、服务和任务的
|
||||
memory/swap/pids/CPU/文件/期限上限。
|
||||
|
||||
前端从 `frontend/` 启动 `pnpm dev`,默认127.0.0.1:5173;平台明确允许相同浏览器
|
||||
Origin,`/api` 同源代理。生产需另行配置 HTTPS、安全 cookie、SPA 回退与 `/api`
|
||||
反向代理,本仓库不自动部署。用户以受控账号登录,在题面选择支持语言、提交源码,
|
||||
随后查看同一 submission UUID 的持久化状态。等待/取消中/系统失败都不等于成功成绩。
|
||||
开发前端在 `frontend/` 运行 `pnpm dev`,默认 127.0.0.1:5173。
|
||||
平台明确允许相同 Origin,`/api` 使用同源代理。生产入口还需 HTTPS、安全 Cookie、
|
||||
SPA 回退和 API 反向代理。登录后在题面提交源码,按同一 submission UUID 查看状态;
|
||||
等待、取消中和系统失败各有独立状态。
|
||||
|
||||
broker 暂停时 API 仍能持久化 queued/outbox;恢复后 publisher confirm 与 Coordinator
|
||||
补偿重新投递。Worker/Coordinator 重启使用原私有配置、工件、日志与 DB,不删除
|
||||
attempt 或强改状态。旧租约不能覆盖新结果,有限基础设施重试最终收敛。
|
||||
容量满时返回429或503;先确认待执行/清理状态,恢复服务或由管理员扩充经过核对的
|
||||
存储容量,不删除仍被提交/题目引用的工件。`kf-artifacts --limit 100` 仅回收满足年龄
|
||||
要求的无引用工件和已标记删除项;它不会通过删除用户源码来腾出容量。
|
||||
恢复 Root 必须核对 journal/inode/实际排空;未知资源保留,管理员处理自己确认的
|
||||
故障来源后再重启。不要递归清空任务目录,也不要把 cleanup_failed 改成已成功。
|
||||
### 停止与故障恢复
|
||||
|
||||
这些入口和顺序不表示生产部署已完成。隔离/超限与用户程序仅在无 NIC、无宿主共享
|
||||
挂载、无业务凭据且整体上限已读回的独立测试 VM 内验收。开发 Compose 仍只提供可信
|
||||
数据库和 broker,不在宿主或业务服务器运行用户代码。workflow 如需启用由指定
|
||||
GitHub 私有仓库承载,并设置有限并发;Gitea 只保存逐阶段工程提交。
|
||||
先向 Worker 发送 SIGTERM,使当前任务取消并清理,再停止 publisher/API/Coordinator,
|
||||
最后停止工厂和本地依赖。强制死亡后的旧 attempt 必须撤销并取得停止证明。
|
||||
`cleanup_failed` 保留未确认资源,管理员检查故障来源后再恢复工厂/Worker。
|
||||
不要递归清空任务目录或强改结果。
|
||||
|
||||
broker 暂停期间,API 保留 queued/outbox;恢复后 publisher confirm 与 Coordinator
|
||||
补偿重新投递。重启 Worker/Coordinator 时保留配置、工件、日志、DB 和 attempt。
|
||||
旧租约不能覆盖新结果,基础设施重试有次数上限。
|
||||
|
||||
容量满时返回 429 或 503。先检查待执行和清理状态,再恢复服务或核对扩容。
|
||||
`kf-artifacts --limit 100` 只回收达到年龄要求的无引用工件和已标记删除项,
|
||||
保留仍被题目或提交引用的源码。Root 恢复还需核对 journal、inode 和实际排空。
|
||||
|
||||
## 四语言节点的受控升级
|
||||
|
||||
新增公开示例 `judge.admission.example.json` 为显式 v1/v2 注册表,
|
||||
`sandbox.managed.runtime.example.json` 为一槽位的共享 Root 配置;原 v1 示例保持原语义。
|
||||
所有 `aaaaaaaa…` 镜像摘要均是无权限占位值,不能当作已验镜像。管理员制作固定镜像后,
|
||||
对 manifest.json 的实际字节计算 SHA-256,逐文件验证版本/字节/权限,并将相同完整身份
|
||||
写入准入注册表与 Root runtime_images。Root 镜像路径仅在私有可信配置中保存。
|
||||
配置解析通过或 check-runtime 可见仍不允许跳过实际隔离门控。
|
||||
`judge.admission.example.json` 是显式 v1/v2 注册表,
|
||||
`sandbox.managed.runtime.example.json` 使用一槽位共享 Root 配置。
|
||||
原 v1 示例语义保留。示例中的 `aaaaaaaa…` 是无权限占位摘要,必须换成固定镜像
|
||||
manifest.json 的实际 SHA-256,并逐文件验证版本、字节和权限。
|
||||
准入注册表与 Root runtime_images 使用同一完整身份;Root 镜像路径仅放入私有配置。
|
||||
|
||||
1. 停止创建新提交/消费投递,向所有普通 Worker 发 SIGTERM;保留 DB、清单、工件和 journal。
|
||||
2. 取得工厂实际 stopped/cleanup_complete 证明,读回空任务 cgroup 和 owned 资源;
|
||||
未排空或存在未知资源时停止升级,保留证据,不递归清空任务目录。
|
||||
3. 使用独立迁移账号执行 Backend Alembic upgrade;新语言数据存在时旧迁移降级会拒绝,
|
||||
不删除数据以实现回退。平台与 Coordinator 继续使用各自最小权限角色,Worker 无 DB 账号。
|
||||
4. 安装固定版本的 Backend/Coordinator/Worker/Sandbox;同时更新三个普通服务的同一
|
||||
`judge_admission`/`admission` 注册表,Root 配置单独由管理员持有。旧消费者不混用新协议。
|
||||
5. 为整体节点、控制面各服务、工厂和任务设置有限 CPU/memory/swap/pids/文件/期限,
|
||||
读回内核值。节点至少预留一份512MiB编译任务以及独立控制面;Java运行示例总预算384MiB,
|
||||
Xmx不能代替整体预算。并发1/prefetch1,共享UID池,不因第二Worker存在而扩大节点槽位。
|
||||
6. 在独立无NIC、无宿主共享挂载、无业务凭据 VM 用合成题目验证四语言、故障与实际排空,
|
||||
再恢复工厂/Coordinator/API/publisher/Worker。生产启用需单独安排,仓库不自动部署。
|
||||
1. 停止创建提交和消费投递,向普通 Worker 发 SIGTERM,保留 DB、清单、工件和 journal。
|
||||
2. 确认工厂 stopped/cleanup_complete,读回空任务 cgroup 和 owned 资源。
|
||||
发现未知资源或未排空时停止升级并保留证据。
|
||||
3. 用独立账号执行 Backend Alembic upgrade。含新语言数据时旧迁移拒绝降级;
|
||||
保留这些数据。平台与 Coordinator 使用各自最小权限角色,Worker 无 DB 账号。
|
||||
4. 安装固定版本 Backend/Coordinator/Worker/Sandbox,同时更新普通服务的同一
|
||||
`judge_admission`/`admission`。Root 配置单独保管,旧消费者不混用新协议。
|
||||
5. 设置并读回节点、控制面、工厂和任务的 CPU/memory/swap/pids/文件/期限上限。
|
||||
至少预留一份 512 MiB 编译任务和独立控制面;Java 运行示例总预算 384 MiB,
|
||||
Xmx 只是堆上限。并发 1、prefetch 1,使用共享 UID 池。
|
||||
6. 在断网、无宿主共享挂载、无业务凭据的受限独立 VM 验证四语言、故障和排空,
|
||||
再恢复服务。生产启用单独安排。
|
||||
|
||||
请求不在线安装 Python/Java 依赖,不运行 pip/uvx,不接收自定义解释器参数/JAR/agents。
|
||||
Worker 私有配置可设置 `control_timeout_ms`(1000–5000,缺省2000)作为 Coordinator
|
||||
单次控制请求的期限。资源受限节点应依据实际领取/续租/停止耗时选择,并核对租约和
|
||||
停止恢复;这不改变学生阶段/作业期限、重试次数或工件下载的固定2秒期限,连接仍限1秒。
|
||||
Python main.py 仅标准库;Java Main.java 使用默认包 Main,最多256个有界编译class。
|
||||
题目语言与预算以公开版本配置为准,旧题目和草稿保留,Rust继续明确不支持。
|
||||
新版检查阶段CE/运行RE/真实限额与SystemError的区别见 [判题说明](../judge/README.md#python--java-与明确协议版本)。
|
||||
Python `main.py` 仅支持标准库。Java `Main.java` 使用默认包 Main,最多 256 个
|
||||
有界编译 class。请求不在线安装依赖,不运行 pip/uvx,不接收解释器参数、JAR 或 agents。
|
||||
题目版本决定语言和预算,旧题目、草稿和报告保留;Rust 暂不支持。
|
||||
CE、RE、真实超限与 SystemError 的区别见
|
||||
[判题说明](../judge/README.md#python--java-与明确协议版本)。
|
||||
|
||||
停止/故障恢复沿原单机流程:停止Worker使任务全树排空,旧租约/report拒绝后恢复;
|
||||
publisher/broker中断保留outbox,有限重试而非清空记录。恢复旧软件前应先排空v2任务,
|
||||
保留不可变v2版本和报告供兼容消费者读取;不能把旧配置静默转换为宽松新能力。
|
||||
这些公开配置不含业务令牌。数据库/AMQP/服务令牌分别在仓库外私下生成,Root不持有它们。
|
||||
需要CI时只在GitHub私有仓库 `KiriAky107/KF-OnlineJudge` 运行并设置有限并发,Gitea保存源码。
|
||||
Worker 的 `control_timeout_ms` 可设为 1000 到 5000,缺省 2000。
|
||||
按实际领取、续租和停止耗时选择,并核对租约与恢复流程。
|
||||
该值不改变学生阶段/作业期限、重试次数、工件下载的固定 2 秒期限或 1 秒连接期限。
|
||||
|
||||
回退旧软件前先排空 v2 任务,保留 v2 不可变版本和报告供兼容消费者读取。
|
||||
恢复时沿用停止与故障流程,保持 outbox 和有限重试。
|
||||
公开示例不含业务令牌;数据库、AMQP 和服务令牌分别在仓库外生成,Root 不持有它们。
|
||||
|
||||
## 验收与 CI
|
||||
|
||||
所有学生源码、隔离、超限和故障测试都在无 NIC、无宿主共享挂载、无业务凭据且
|
||||
整体上限已读回的独立 VM 运行。配置解析、readiness 和存活探测不能替代执行验收。
|
||||
仓库不自动部署生产。
|
||||
|
||||
需要 workflow 时,在 GitHub 私有仓库 `KiriAky107/KF-OnlineJudge` 设置有限并发执行。
|
||||
Gitea 保存工程提交,不运行 workflow。
|
||||
|
||||
+119
-92
@@ -1,107 +1,134 @@
|
||||
# Single-node delivery contract
|
||||
# Single-node delivery
|
||||
|
||||
`contract.v1.json` describes the first pre-release installation interface. It is
|
||||
separate from judging v1/v2 wire formats and never rewrites historical reports.
|
||||
The reference target is Arch/CachyOS Linux x86_64-v3, glibc 2.44, systemd and cgroup
|
||||
v2. Images and service libraries are bound to explicit file/catalog fingerprints;
|
||||
support for other distributions requires a separately verified delivery profile.
|
||||
The single-node profile packages the application, four judging runtimes and the
|
||||
installation tools for offline use. `contract.v1.json` defines the installation
|
||||
interface. Judging v1/v2 reports keep their existing formats.
|
||||
|
||||
The target has at least 4 GiB RAM and 12 GiB installation/data capacity. Judging
|
||||
and problem-import concurrency remain one each. Configured service and execution
|
||||
budgets, effective kernel limits and free capacity must also pass preflight; total
|
||||
RAM alone does not prove enough capacity or verified execution isolation.
|
||||
Preflight requires at least 12 GiB of filesystem capacity and derives required
|
||||
free bytes from the verified application, protected runtime-image copies and
|
||||
separate GNU legacy copy, plus 4 GiB of mutable-data/operation reserve. It does
|
||||
not assume that a nominal 12-GiB filesystem exposes 12 GiB free after metadata.
|
||||
Full installation and recovery acceptance is in progress. Validate a release on
|
||||
an independent test node before using it for production.
|
||||
|
||||
Release payloads are directory bundles with a bounded manifest: at most 50,000
|
||||
single-link regular files, 256 MiB per file, 4 GiB total and 8 MiB metadata. The
|
||||
manifest SHA-256 is supplied separately by the operator. Links, extra members,
|
||||
private paths and unexpected file changes are refused. Built payloads and real
|
||||
configuration never belong in Git. Validation is read-only and does not claim
|
||||
that any service has been installed, started or verified:
|
||||
## Requirements
|
||||
|
||||
| Item | Required profile |
|
||||
| --- | --- |
|
||||
| OS | Arch/CachyOS, Linux x86_64-v3, glibc 2.44, systemd 262, cgroup v2 |
|
||||
| Memory | At least 4 GiB, with enough free capacity for the configured services and tasks |
|
||||
| Storage | At least 12 GiB filesystem capacity; preflight calculates the required free space |
|
||||
| Concurrency | One judging job and one problem import |
|
||||
| Dependencies | PostgreSQL 18.6 and the fixed broker runtime, prepared from verified offline materials |
|
||||
|
||||
Other distributions need a separately verified profile. Preflight checks the
|
||||
effective kernel limits and available capacity as well as nominal RAM and disk
|
||||
size. Its free-space calculation includes the application, protected runtime
|
||||
images, the separate GNU legacy image and 4 GiB for mutable data and operations.
|
||||
|
||||
## Verify a bundle
|
||||
|
||||
Use a separately trusted checker and the manifest digest supplied with the release:
|
||||
|
||||
```sh
|
||||
python3.13 -m scripts.release contract deploy/release/contract.v1.json
|
||||
python3.13 -m scripts.release verify /path/to/bundle --manifest-sha256 SHA256_FROM_RECEIPT
|
||||
```
|
||||
|
||||
Application versions live below `/opt/kf-onlinejudge/releases`; `current` selects
|
||||
an explicitly installed version. Private operator/service configuration lives in
|
||||
`/etc/kf-onlinejudge` and `/etc/kf-judge`, mutable application data under
|
||||
`/var/lib/kf-onlinejudge`, and protected Sandbox state/images under
|
||||
`/var/lib/kf-judge`. The delegated task cgroup is separate from the Root factory's
|
||||
own supervision cgroup. Application processes do not initialize tables on start;
|
||||
Backend Alembic remains the only migration entry point.
|
||||
All application services and the separate delegated task subtree live beneath
|
||||
`kfonlinejudge.slice`: 3840 MiB memory, no swap, 384 processes/threads and two CPU
|
||||
equivalents. The remaining physical RAM is reserved for the kernel/minimal OS.
|
||||
The task subtree is `/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks`, a sibling
|
||||
of the Root factory supervision unit under that same total node budget. Its
|
||||
installation template does not prove the effective limits; actual service and
|
||||
task limits are read back in the independent acceptance environment.
|
||||
A bundle contains at most 50,000 single-link regular files, 256 MiB per file,
|
||||
4 GiB in total and 8 MiB of metadata. Verification rejects links, extra members,
|
||||
private paths and changed bytes. Keep bundles and real configuration outside Git.
|
||||
|
||||
Database login identities are separate for migration, API/outbox, Coordinator,
|
||||
problem import and local identity operations. Rust Worker and Root factory do not
|
||||
hold database credentials. Root factory has no MQ/platform/SSH/GitHub identity;
|
||||
privileged execution remains inside Sandbox. Public and admin browser traffic
|
||||
share the HTTPS site; execution-control interfaces remain internal.
|
||||
Verification is read-only. Installation, running services and execution isolation
|
||||
each need their own acceptance evidence.
|
||||
|
||||
Upgrade and restore must retain immutable problem versions, submitted source,
|
||||
audit and original judging reports. A consistent backup cut has a zero confirmed
|
||||
data-loss target; restoring from already prepared release/backup materials has a
|
||||
30-minute recovery target, including service health and actual four-language
|
||||
verification. These are acceptance targets, not results inferred from this file.
|
||||
Installation/service/restore evidence is required before this pre-release can be
|
||||
treated as an accepted delivery; the contract itself does not deploy production.
|
||||
## Directories and authority
|
||||
|
||||
The node input for `generate-configuration` has exactly `schema_version: 1`,
|
||||
`origin` (one HTTPS origin), `worker_id` (canonical nonzero UUID), and
|
||||
`measurement_nonce` (null or a fresh operator-generated SHA-256-sized nonce).
|
||||
It is a 0600 single-link file. Endpoint input is a separate 0600 JSON object
|
||||
with exactly `schema_version: 1`, `database` and `amqp`: database keys are
|
||||
`api`, `coordinator`, `import`, `migration`, `identity`; broker keys are `outbox`
|
||||
and `worker`. Login names and numeric loopback/socket locations are validated by
|
||||
the tool. Every endpoint requires its own operator-supplied private credential;
|
||||
there is no working example password or shared default. Import uses the reachable
|
||||
`/run/postgresql` Unix socket; all other control connections use numeric loopback.
|
||||
| Location | Contents |
|
||||
| --- | --- |
|
||||
| `/opt/kf-onlinejudge/releases` | Installed application versions |
|
||||
| `current` | Explicitly selected application version |
|
||||
| `/etc/kf-onlinejudge` and `/etc/kf-judge` | Private operator and service configuration |
|
||||
| `/var/lib/kf-onlinejudge` | Mutable application data |
|
||||
| `/var/lib/kf-judge` | Protected Sandbox state and runtime images |
|
||||
|
||||
Generated admission uses the actual verified catalogs for C17/C++20, CPython
|
||||
3.13.16 and OpenJDK 21.0.12.1+1-LTS and the unchanged public per-language budgets.
|
||||
The protected GNU legacy image is a separate directory: it retains the original
|
||||
v1 header and identical verified GNU executable/library bytes, while the three
|
||||
v2 catalog identities remain untouched. Shared Worker/task UID/GID pool and
|
||||
execution/prefetch/import slots remain one. No canonical judging report or
|
||||
historical runtime identity is converted by the installer.
|
||||
Backend Alembic is the only schema migration entry point. Database identities are
|
||||
separate for migration, API/outbox, Coordinator, problem import and local account
|
||||
operations. Rust Worker has no database identity; the Root factory has no
|
||||
database, MQ, platform, SSH or GitHub credentials. Sandbox owns privileged
|
||||
execution. Public and admin pages share the HTTPS origin; control interfaces stay
|
||||
internal.
|
||||
|
||||
Comparability defaults to unavailable (`measurement_epoch=null`). A trusted
|
||||
operator may explicitly use `--attest-measurement` only after actual node/service
|
||||
limits and the intended execution environment have been verified. The attestation
|
||||
binds a fresh nonce, worker, source, exact images, budgets, CPU/microcode, kernel
|
||||
and node scheduling snapshot. `check-configuration` refuses an old binding when
|
||||
those values change; it does not manufacture replacement metrics for old reports.
|
||||
Before any service resume after such a change, drain execution, generate/install
|
||||
a new complete private group with a new nonce and independently revalidate the
|
||||
environment. Raw configuration checks are not performance or isolation evidence.
|
||||
Configuration batches have independent hash-bound `cfg-...` identities; files
|
||||
and receipts contain both the source release and batch identifier. A fresh
|
||||
nonce/identity can therefore be installed for the same source without replacing
|
||||
an old configuration. Preparation never selects the new batch automatically.
|
||||
`activate` requires an explicit `--configuration-id`, a fully validated group,
|
||||
all application units inactive, stopped control ports and no retained task
|
||||
resources. Root journals and all old batches remain intact. It binds the code
|
||||
pointer and private batch in `/etc/kf-onlinejudge/active.json`; `check-active`
|
||||
refuses inconsistent pointers, stale hardware bindings or changed bytes.
|
||||
Interruption between the two atomic pointer writes requires operator review;
|
||||
no service starts or claims readiness during that mismatch. A stopped stale
|
||||
batch can be retired without accepting it for execution under a changed kernel.
|
||||
Application services and the task subtree share `kfonlinejudge.slice`, capped at
|
||||
3840 MiB, zero swap, 384 processes/threads and two CPU equivalents. The remaining
|
||||
physical memory is reserved for the kernel and minimal OS. Tasks use
|
||||
`/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks`, separate from the Root
|
||||
supervisor's cgroup. Read back service and task limits on the target node.
|
||||
|
||||
The installation entry points are available with `python3.13 -m scripts.release
|
||||
--help`. Use them in order: verify/preflight, install-files, generate-configuration,
|
||||
install-configuration, explicit DB identity provision, controlled Backend Alembic
|
||||
and account CLI operations, then activate and the separately verified bounded
|
||||
service lifecycle. These operations never contact a package index and do not
|
||||
install onto a business host on their own. OS dependencies (systemd 262, matching
|
||||
glibc ABI bytes, PostgreSQL 18.6 and the fixed broker runtime) must already be
|
||||
present from separately verified offline materials.
|
||||
## Prepare configuration
|
||||
|
||||
Supply two 0600 single-link JSON files to `generate-configuration`:
|
||||
|
||||
| Input | Fields |
|
||||
| --- | --- |
|
||||
| Node | `schema_version: 1`, `origin`, `worker_id`, `measurement_nonce` |
|
||||
| Endpoints | `schema_version: 1`, `database`, `amqp` |
|
||||
|
||||
`origin` is one HTTPS origin. `worker_id` is a canonical nonzero UUID.
|
||||
`measurement_nonce` is null or a fresh operator-generated SHA-256-sized nonce.
|
||||
The database object has `api`, `coordinator`, `import`, `migration` and `identity`
|
||||
keys; the broker object has `outbox` and `worker` keys. Supply a different private
|
||||
credential for every endpoint. Import uses `/run/postgresql`; other control
|
||||
connections use numeric loopback. The tool checks login names and locations.
|
||||
|
||||
The generated admission profile binds C17/C++20, CPython 3.13.16 and OpenJDK
|
||||
21.0.12.1+1-LTS to verified catalogs and the existing per-language budgets. The
|
||||
GNU legacy image retains its v1 header and verified executable/library bytes in
|
||||
a separate directory. Worker, task identities and execution/prefetch/import
|
||||
slots remain one. Historical reports and runtime identities remain unchanged.
|
||||
|
||||
## Select a version
|
||||
|
||||
Configuration batches have content-bound `cfg-...` identities. Preparation keeps
|
||||
earlier batches and does not select a new one. `activate` requires an explicit
|
||||
`--configuration-id`, a fully validated batch, inactive application units,
|
||||
stopped control ports and no retained task resources.
|
||||
|
||||
Selection writes the code pointer and `/etc/kf-onlinejudge/active.json`. A crash
|
||||
between those atomic writes can leave a mismatch; stop and inspect it before
|
||||
starting services. `check-active` rejects inconsistent pointers, changed bytes
|
||||
and stale hardware bindings. Root journals and earlier batches remain intact.
|
||||
A stopped stale batch can be retired without accepting it under a changed kernel.
|
||||
|
||||
## Performance comparability
|
||||
|
||||
Comparability defaults to unavailable (`measurement_epoch=null`). Use
|
||||
`--attest-measurement` after verifying the node's limits and execution environment.
|
||||
It binds a fresh nonce, worker, source, images, budgets, CPU/microcode, kernel and
|
||||
node scheduling snapshot. `check-configuration` rejects a changed binding.
|
||||
|
||||
After an environment change, drain execution, install a complete batch with a new
|
||||
nonce and revalidate before resuming. Earlier reports retain their original
|
||||
measurements. Configuration validation alone does not prove comparable performance
|
||||
or execution isolation.
|
||||
|
||||
## Installation order
|
||||
|
||||
Use `python3.13 -m scripts.release
|
||||
--help` for the command interface. Prepare the OS dependencies and dedicated data
|
||||
volumes, then follow this order:
|
||||
|
||||
1. Verify the bundle and run preflight.
|
||||
2. Install files and runtime images.
|
||||
3. Generate and install a private configuration batch.
|
||||
4. Activate that batch while services and tasks are stopped.
|
||||
5. Install the journal profile and selected dependency snapshots.
|
||||
6. Initialize PostgreSQL, provision DB identities, run Backend Alembic and create
|
||||
application accounts; stop the initialization database before installing units.
|
||||
7. Install ordinary units, the Root factory and the HTTPS snapshot/TLS material.
|
||||
8. Start the bounded service lifecycle and verify readiness and actual execution.
|
||||
|
||||
The tools use offline materials and do not contact a package index.
|
||||
|
||||
See the [operator command reference](../../scripts/release/README.md) for service,
|
||||
HTTPS, backup and recovery operations. Upgrades and restores must preserve
|
||||
immutable problem versions, submitted source, audit and original judging reports.
|
||||
A confirmed consistent backup cut has a zero data-loss target. Recovery from
|
||||
prepared release and backup materials has a 30-minute target, including health
|
||||
and four-language verification. Both targets require measured acceptance results.
|
||||
|
||||
Reference in New Issue
Block a user