Judge Configuration.zh CN - houyhlouis/LLMOJ GitHub Wiki
本页介绍评测机配置文件 /opt/LLMOJ/config/judge.yaml(如果安装在其他目录,请换成你的目录)中的每项设置、评测槽数量的计算方法、沙箱、systemd 服务,以及如何修改已有安装的评测槽数量。
评测机读取 LIBREOJ_JUDGE_CONFIG_FILE 指定的文件,没有该文件就无法启动。大多数设置没有内置默认值:下面的示例值来自 config-example.yaml,安装器可能写入其他值。示例中的 key 和 sandbox.rootfsId 只是占位符,不能直接使用。
| 设置 | 示例 | 含义 |
|---|---|---|
serverUrl |
http://api.libreoj.test/ |
站点地址,不带 /api。与站点在同一台服务器的评测机由安装器写入 http://127.0.0.1:2002,远程评测机写入站点的 HTTPS 地址 |
downloadEndpointOverride |
null |
替换测试数据下载地址中的协议、主机和端口,保留路径和查询参数。同一服务器上的评测机使用 127.0.0.1 上的 Nginx;远程评测机通常不设置,但必须能访问站点的下载地址 |
key |
40 个 base64 字符 | 该评测机独有的密钥。文件所有者为 root、权限为 0600;不要把密钥写在命令行、截图或公开日志中 |
rpcTimeout |
20000 |
等待站点响应的毫秒数。超时后评测机会重启,并取消正在进行的工作。安装器写入 30000
|
downloadTimeout |
20000 |
站点开始响应后,一次下载可用的毫秒数 |
downloadRetry |
3 |
每次下载总共尝试的次数:3 表示首次尝试加最多两次重试 |
maxConcurrentDownloads |
10 |
同时进行的下载数(超过 20 会警告)。安装器写入评测槽数量,最多 4 |
| 设置 | 示例 | 含义 |
|---|---|---|
dataStore |
/root/judge/data |
已下载测试数据的缓存。安装器写入 <root>/data/judge/testdata。它的大小没有限制,请关注磁盘空间 |
binaryCacheStore |
/root/judge/cache |
编译结果的缓存,评测机启动时清空。安装器写入 <root>/data/judge/cache。不要指向保存其他内容的目录 |
binaryCacheMaxSize |
536870912(512 MiB) |
编译缓存的大小(字节)。必须至少等于站点的 limit.outputSize,否则评测机连接后会停止 |
| 设置 | 示例 | 含义 |
|---|---|---|
taskWorkingDirectories |
三个路径 | 每个评测槽一个目录,互不相同。安装器写入 <root>/data/judge/work/1 到 N,每个都是单独的 tmpfs,评测机启动时清空。不要与缓存共用 |
maxConcurrentTasks |
3 |
评测槽数量,由编译、运行测试点和本机 AI 检查共用。安装器使它等于工作目录数;实际取两者中较小的值 |
taskConsumingThreads |
2 |
评测机同时从站点领取的提交数(超过 3 会警告),每份提交可以有很多测试点。安装器写入评测槽数的一半,范围 1~3。它不是 CPU 数 |
memoryBudget |
未设置 | 所有正在运行的工作的沙箱合计可预留的内存(MiB)。默认为服务器内存的 85% 减 512 MiB,至少 256。工作在其声明的内存限制装得下时才开始,因此多道内存限制很大的题目不会耗尽服务器内存。与其他服务共用服务器时可调低 |
aiExecutionSlots |
未设置 | 本机 AI 检查同时可用的评测槽数。默认为除一个之外的全部槽(只有一个槽时为 1),保证评测始终有槽可用 |
这些设置只用于与站点在同一台服务器上的评测机;远程评测机将它们留空,即关闭 AI 任务。
| 设置 | 含义 |
|---|---|
aiRunnerSocket |
后端发送 AI 检查所用的本机 socket:<root>/data/judge/ai-runner.sock,所有者为 root 和 libreoj 组,权限 0660。不要通过 TCP 公开 |
aiGeneratedDirectory |
与后端共用的 AI 结果私有目录:<root>/data/ai-generated,必须是真实目录(不能是链接),所有者为 root 和 libreoj 组,权限 0750
|
aiRunnerGid |
libreoj 组的 ID。开启 AI 任务时必须为正数;远程评测机为 0
|
| 设置 | 示例 | 含义 |
|---|---|---|
sandbox.rootfs |
/opt/rootfs-ng |
沙箱的文件系统(rootfs)。安装器写入 <root>/runtime/sandbox-rootfs。不要修改其中文件的所有者 |
sandbox.rootfsId |
64 位十六进制数 | 沙箱镜像的 SHA-256 ID,必须与 ID 文件完全一致,否则评测机无法启动 |
sandbox.rootfsIdFile |
未设置 | ID 文件,默认为 <sandbox.rootfs>/etc/libreoj-rootfs-id。不可修改的镜像可以把它放在别处 |
sandbox.hostname |
null |
沙箱内的主机名;安装器写入 libreoj-judge
|
sandbox.immutable |
未设置 | 用于不可修改的镜像:设为 true 时,工作路径改为 /tmp/libreoj,而不是镜像内的目录。它本身不会让镜像变成只读 |
sandbox.resourceMode |
未设置 |
default(安装器写入)用 cgroup 统计程序的 CPU 时间;arbiter 改用程序的用户态 CPU 时间,并额外限制地址空间。只影响提交的程序,会改变评测结果的计算方式 |
sandbox.syscallFilter |
未设置 | 默认 true:拒绝评测程序用不到的系统调用,见下文。只在需要确认过滤器是否影响某种语言时设为 false
|
sandbox.user |
sandbox |
程序在沙箱中运行的用户(不是评测服务本身的运行用户) |
sandbox.environments |
见右 | 沙箱中程序的环境变量,值为文本。示例设置 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin、HOME=/sandbox 和 LC_ALL=en_US.UTF-8。评测机自身的变量(如 UV_THREADPOOL_SIZE)不能写在这里 |
cpuAffinity.compiler、cpuAffinity.userProgram、cpuAffinity.interactor 和 cpuAffinity.checker 分别列出各类程序可以使用的逻辑 CPU(例如 [0, 1])。安装器为四者都写入它为评测选择的 CPU。它们不会为每个评测槽分配独占的 CPU,并且必须在 systemd 允许该服务使用的 CPU 范围内。不设置时只受 systemd 的限制。
评测机连接时,站点会发送一些限制:limit.compilerMessage、limit.outputSize、limit.dataDisplay、limit.dataDisplayForSubmitAnswer 和 limit.stderrDisplay。它们不是 judge.yaml 中的设置;每道题的时间和内存限制同样来自站点。
-
LIBREOJ_JUDGE_CONFIG_FILE:配置文件,<root>/config/judge.yaml。 -
LIBREOJ_JUDGE_LOG_LEVEL:默认info。只在排查问题时使用verbose或debug,并且不要公开这些日志,其中包含带签名的下载地址。 -
UV_THREADPOOL_SIZE:至少为评测槽数的两倍加二(最多 1024);安装器写入max(4, 2 × 槽数 + 2)。每个槽可能同时等待两个程序(交互题),另外两个线程处理文件。修改后需要重启评测机。 -
NODE_OPTIONS=--max-old-space-size=512:评测机自身的内存,不是提交程序的内存限制。 -
HYHOJ_AI_SAMPLE_INPUTS_DIR:AI 样例输入的私有目录,<root>/data/ai-sample-inputs。必须与后端相同,并且不能公开访问。
- 评测机同时最多从站点领取
taskConsumingThreads份提交。 - 编译、运行测试点和本机 AI 检查共用评测槽。一个测试点预留程序的内存限制加上交互器的内存限制(如果校验器的更大则用校验器的),一次编译预留 512 MiB,其他工作预留 256 MiB;工作按到达顺序,在评测槽和所需内存都空闲时开始。需要的内存超过全部预算的工作单独运行。
- 本机 AI 检查最多使用
aiExecutionSlots个槽,不会阻塞排在它后面的评测。 - 下载有单独的队列,大小为
maxConcurrentDownloads。 - AI 任务另外最多同时运行三个 AI 流程,最多接受八个请求;这些不是额外的评测槽,也不能配置。
提高 taskConsumingThreads 或 AI 并发不会增加评测能力:只有评测槽(以及它们背后的 CPU 和内存)才会。
一个评测槽同一时间运行一次编译或一个测试点。一个评测服务运行它所有的评测槽。默认的评测槽数量(也是允许的最大值)为:
评测机可用的 CPU 数减一,与其可用内存(MiB)除以 256,两者取较小值,且不超过 511
与站点在同一台服务器上的评测机和远程评测机都使用这一规则。只有一个 CPU 或内存不足 256 MiB 的服务器不能评测。
“可用的 CPU 数”会考虑 CPU 亲和性、cgroup CPU 集合和 CPU 配额,而不仅是 nproc;“可用内存”取服务器内存与 cgroup memory.max 中的较小值。评测机使用的 CPU 从允许列表的末尾选取,列表不必从 0 开始,也不必连续。
| CPU 数 | 评测槽数(内存足够时) |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 8 | 7 |
| 16 | 15 |
| 32 | 31 |
例如有 8 个 CPU(0–7)和 8 GiB 内存时,评测机使用 CPU 1–7、7 个评测槽、同时领取 3 份提交、4 个下载、工作目录 1–7 和 16 个线程。只有 512 MiB 内存时,它使用 CPU 6 和 7 上的 2 个评测槽和 6 个线程。由于工作会等待内存,除非大部分题目都有很大的内存限制,否则不需要减少评测槽。
每个评测槽的 512 MiB tmpfs 是它最多能容纳的大小,而不是预留的内存。编译器、程序、页缓存、AI 和数据库共用服务器内存,TasksMax、cgroup 和 CPU 配额也仍然有效。lscpu -e=CPU,CORE,SOCKET,ONLINE 可以显示哪些逻辑 CPU 共用一个物理核心;评测槽不会独占核心。
安装器和扩缩容命令使用同一计算方法:judge_capacity.py。
所有编译器、程序、校验器和交互器都在沙箱中运行:
- 以无特权的
sandbox.user身份运行,无法获得特权(no_new_privs); - 使用独立的挂载、PID、网络、UTS 和 IPC 命名空间,文件系统只读,只能访问自己任务的目录;
- 受 cgroup v2 对内存、进程数和 CPU 时间的限制。
此外,seccomp 过滤器会拒绝评测程序用不到的系统调用(sandbox.syscallFilter,默认开启):
- 创建或进入命名空间、挂载和更改根目录;
- ptrace 和读取其他进程的内存;
- 密钥环、io_uring、bpf、性能事件和 userfaultfd;
- 内核模块、重启、交换区以及设置系统时间或主机名。
这些调用返回“不允许的操作”,通过 32 位系统调用表(-m32 程序和 int 0x80)调用时也一样;x32 调用同样失败。clone3 返回“未实现”,使 C 库改用 clone,过滤器可以检查后者的参数。如果过滤器无法安装,任何程序都不会运行。
检查本评测机(通过评测机自身的代码编译并运行每种语言,并用 64 位和 32 位程序测试过滤器):
sudo bash /opt/LLMOJ/deploy/sandbox/self-test.sh每项检查输出一行,耗时几秒到一分钟。它使用自己的临时目录,从不使用评测机密钥,因此可以在评测机工作时运行,但会与评测争用 CPU。--only cpp,java 只检查部分语言。每当评测机或沙箱有改动时,升级器都会在切换之前用新版本运行它。
沙箱不能做到的事。 沙箱中的程序与服务器共用内核,因此内核漏洞仍可能让程序逃逸。过滤器减少了程序能接触到的内核功能,但不能证明完全隔离,而且评测机的控制进程以 root 运行。对于公开的比赛:
- 在独立的机器上运行评测机,不要放在网页服务器上;
- 及时更新内核;
- 不要在远程评测机上存放站点的凭据(数据库、MinIO、AI 密钥),它们只需要评测机密钥。
安装器和扩缩容命令为评测服务写入以下设置。请检查它们写入的文件,以及可能覆盖这些设置的 drop-in。
| 设置 | 值 |
|---|---|
After、Requires、Wants
|
与站点在同一服务器上的评测机在 libreoj-backend.service 之后启动,并依赖它。远程评测机等待网络就绪(network-online.target) |
PartOf、WantedBy
|
服务及其挂载在站点服务器上属于 libreoj.target,在远程评测机上属于开机启动的 libreoj-judge.target
|
RequiresMountsFor |
所有工作目录,因此服务只在它们的 tmpfs 挂载完成后启动 |
WorkingDirectory、ExecStart
|
<root>/apps/judge,运行 <node> -r @swc-node/register index.mjs
|
Environment |
NODE_ENV=production、PATH,以及评测机的环境变量中的变量 |
Delegate、DelegateSubgroup
|
cpu memory pids 和 supervisor(需要 systemd 254 或更高版本):由沙箱自己管理 cgroup。不要删除 |
OOMPolicy |
continue:某个程序内存不足不会停止整个服务 |
AllowedCPUs |
cpuAffinity 中的所有 CPU |
TasksMax |
总共 2048 个进程和线程 |
Restart、RestartSec
|
失败 3 秒后重启 |
TimeoutStopSec |
30 秒。停止服务不会等待排队的工作:请先排空评测机 |
UMask、LimitCORE
|
0022,不生成 core 文件 |
StandardOutput、StandardError
|
journal |
没有设置 User 和 Group,因此服务以 root 运行;sandbox.user 是沙箱内的另一个用户。CPUAccounting、MemoryAccounting、TasksAccounting、MemoryMax、CPUQuota 等限制也没有设置,由 systemd 默认值或上级 slice 决定。查看实际生效的值:
sudo systemctl show libreoj-judge.service -p CPUAccounting -p MemoryAccounting -p TasksAccounting -p LimitCORE -p TasksMax每个工作目录有自己的挂载单元(名称由 systemd-escape --path --suffix=mount 生成):一个 tmpfs,选项为 size=512m,mode=0755,nodev,nosuid。
增加或减少 CPU、内存后,请使用扩缩容命令修改评测槽数量。不要用不同的 --judge-slots 重新运行安装器,不要修改安装记录,也不要删除 judge.yaml 或密钥来强行修改。
该命令适用于安装器安装的评测机,要求使用标准的工作目录和服务。仅网页的服务器没有可调整的评测机;请改为添加远程评测机。--mode auto(默认)会判断这是完整站点还是远程评测机;--mode all 或 --mode remote 只用于确认是否为你预期的那一种。如果服务自己设置了 MemoryMax 或 CPUQuota、有影响它的 drop-in,或属于其他安装目录,命令会拒绝修改:请先人工检查。
resize-judge.sh 输出英文,resize-judge.zh-CN.sh 输出中文,其他方面完全相同。
sudo bash /opt/LLMOJ/deploy/resize-judge.zh-CN.sh --prefix /opt/LLMOJ --plan
# 指定数量(必须符合 CPU 和内存):
sudo bash /opt/LLMOJ/deploy/resize-judge.zh-CN.sh --prefix /opt/LLMOJ --slots 6 --plan--plan 不做任何修改,只显示新旧评测槽数、CPU、内存和线程数。
选择空闲时段。停止新的提交、重测、测试运行和 AI 任务,等待所有人排队中和运行中的工作完成。如果其他评测机可以接手队列,也可以在 评测机状态 页面 排空 本评测机,等待显示 已排空;但本机 AI 任务仍需完成,因为排空不包括它们。CPU 空闲或自己的 AI 任务列表为空,都不能证明没有工作在运行。--drained 表示你确认已完成这一步,命令本身不会排空任何东西。
不带参数运行时,命令会显示容量和排空要求,并询问评测槽数量。输入数字(或直接回车使用默认值)即表示确认已排空,并立即执行修改,不会再次确认。 如果仍有工作在运行,请按 Ctrl+C 退出。
sudo bash /opt/LLMOJ/deploy/resize-judge.zh-CN.sh --prefix /opt/LLMOJ或者不提问直接执行:
sudo bash /opt/LLMOJ/deploy/resize-judge.zh-CN.sh --prefix /opt/LLMOJ --slots 6 --apply --drained该命令会:
- 把
judge.yaml、评测服务及其状态备份到<prefix>/backups/judge-resize/<UTC 时间>/,并输出该目录; - 只停止评测机;
- 更新评测槽、工作目录、CPU、并发、线程、挂载和服务,保留密钥、站点地址、沙箱镜像和 AI 设置;
- 检查服务并运行真实的沙箱测试;
- 如果评测机之前在运行,则重新启动它(并检查其 CPU 和线程);之前已停止的评测机保持停止。
如果检查失败,它会恢复备份。如果恢复也失败,评测机保持停止,命令会告诉你备份所在的位置。请妥善保管备份:其中包含评测机密钥。
确认服务正在运行、站点上显示评测机在线,然后用常用语言提交普通题和交互题;如果使用本机 AI,再运行一个小的 AI 任务。
sudo systemctl is-active libreoj-judge.service
sudo systemctl show libreoj-judge.service -p AllowedCPUs -p EffectiveCPUs -p TasksMax要撤销修改,先停止新的工作并再次排空,然后传入命令输出的备份目录:
sudo bash /opt/LLMOJ/deploy/resize-judge.zh-CN.sh --prefix /opt/LLMOJ --rollback /path/to/backup --drained这会恢复评测机的配置、服务及其之前的状态。它不会改动题目或数据库,也不会撤销期间已评测的提交。之后请再次检查评测机。
另见:远程评测机 · 分布式评测。来源:config.ts、configure-judge.py、executionCapacity.ts、taskQueue.ts、aiRunner.ts。