Files
OJ2/docker/judge/README.md
T
xuyueandClaude Opus 5 8b4d8899f9
Deploy / deploy (push) Waiting to run
feat(判题机): 自建镜像升级工具链,语言收敛到 C / C++ / Python
上游 QingdaoU/JudgeServer 停更在 2024-04(registry 上的 latest 和 1.6.1 是同一份
镜像,编译器停在 gcc-13),没有新版可拉,所以自己重编。docker/judge/ 是只改工具链
的 Dockerfile 分叉,server/ 和 Judger/ 从上游固定 commit b28aa56 拉,一行没动。

镜像 oj2-judge-2(不在任何 registry 上:本机 build.sh --save → scp → docker load)
- gcc/g++ 13 → 14.2,Python 3.12 → 3.13.5,都是 trixie 默认
- Go / JDK / Node 整套删掉:前端的题目语言复选框从来只给 C / C++ / Python / SQL,
  12 万条提交里 Java 44 条、Golang 15、JavaScript 3,全是很早以前的
- 体积 1.1GB → 433MB;默认走清华源,构建 12 分钟 → 40 秒(--no-mirror 换回官方)
- deploy.sh 加一道自检:镜像不在本机就中止,并打印该跑的三条命令

C 的编译参数加三个 -Wno-error(implicit-function-declaration / int-conversion /
incompatible-pointer-types):gcc-14 把它们从 warning 提成了 error,而 -w 压不住。

语言值统一成 Python(迁移 0019 / 0020)
- 0019:Python3(104527 条提交)与 Python2(3 条)并成 Python,一并改掉 937 道题的
  languages、75 个 template 键、15 个 ast_rules 键、257 条 answers、1235 个用户的
  成就指标 _languages(languages_used 重算,总和 1928 → 1925,少的 3 个是同时用过
  两种 Python 的人)
- 0020:把 Java / JavaScript / Golang 从 84 道题的可选语言里摘掉 —— 不摘的话那些题
  的语言下拉还能选 Java,提交必 SYSTEM_ERROR
- 契约新增 normalizeLanguage() 别名表,判题侧一律走 judgeConfigFor():旧客户端
  localStorage 里的 Python3、迁移前排进队列的任务都还能判;协作的语言归一也走它,
  否则上线那一刻学生页面里的 Python3 会静默落到 C
- 回滚要连数据一起回,只滚代码会让所有 Python 提交变 SYSTEM_ERROR

实跑
- 判题冒烟 docker/judge/smoke.ts 13 条全过:三种语言、六种状态码、gcc 宽松度
- 拿备份里的真实代码逐文件比对新旧镜像的编译结果,0 差异:C 提交 1951 份
  (1725 过 / 226 CE)、C++ 882 份、Python 2000 份、20 篇 C 教程的 93 个代码块。
  不加那三个 -Wno-error 的话,C 有 26 份会从能过变成 CE
- 迁移在灌了 12.4 万行真实数据的一次性库里跑过:0 残留、没有题目被清空;
  dev 库用真正的执行器跑通
- check:ast 56 个 target 全过,前后端 typecheck 均 0

顺带记下一个升级之前就有的坑(现在随 Go 一起消失,写在 README 里):GOCACHE 指向
容器的 tmpfs,判题机重启后第一次 Go 提交是冷构建,Go 1.22 要 5.6 秒 CPU、超过 3 秒
的编译预算,于是重启后第一个交 Go 的学生必吃一次 CE,后面的人缓存热了又都正常。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 06:24:05 -06:00

105 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 判题沙箱镜像
判题机跑的是 [QingdaoU/JudgeServer](https://github.com/QingdaoU/JudgeServer)。
**上游已经停更**master 最后一次提交是 2024-04-05`b28aa56`,即 tag `v1.6.1`),
registry 上的 `oj-image/judge:latest``:1.6.1` 是同一份镜像(config digest 都是
`221bf4c0e730`)。所以想换新编译器,只能自己构建。
这里放的是**只改工具链的分叉**`server/`Flask + `_judger` 那套判题逻辑)和
`Judger/`libjudger.so 沙箱内核)一行都没动,构建时从上游那个固定 commit 拉。
## 工具链
2026-09 从上游镜像升级,同时把用不上的三种语言整套砍掉:
| | 上游 1.6.1 | 现在 |
|---|---|---|
| gcc / g++ | 13 | **14.2**trixie 默认) |
| Python | 3.12 | **3.13.5**trixie 默认) |
| Go | 1.22 | **删掉** |
| Node | 20.x | **删掉** |
| JDK | temurin-21 | **删掉** |
| base | debian:trixie-slim2024 年的) | debian:trixie-slim(当前) |
| 镜像体积 | 1.1 GB | **433 MB** |
砍语言的依据:前端的题目语言复选框从来只给 Python / C / C++ / SQL
生产库 12 万条提交里 Java 44 条、Golang 15 条、JavaScript 3 条,全是很早以前的。
契约 `judgeLanguageSchema` 里那几个键留着(渲染历史提交要用),只是判题机不再认。
要恢复某种语言:Dockerfile 里加回包和 `update-alternatives`,同时改
`apps/api/src/judge/languages.ts`,两边缺一个都是静默失败。
## 镜像源
默认走清华源(`Dockerfile` 顶部三个 ARG)。官方源在这边实测 **197 KB/s**,清华
**3.9 MB/s**,整个构建从 12 分钟掉到 1 分钟以内。
`build.sh --no-mirror` 换回官方源。debian 那两个只能用 http —— 改 sources 发生在
装 ca-certificates 之前,base 镜像里没有 CA 根证书,https 一律
`certificate verify failed`(这个坑踩过)。
## 构建与分发
```bash
docker/judge/build.sh --save # 本机构建 + 导出 dist/oj2-judge-2.tar
scp dist/oj2-judge-2.tar root@服务器:/root/OJDeploy/
ssh root@服务器 'docker load -i /root/OJDeploy/oj2-judge-2.tar'
# 机房那台同样来一遍 —— 两个站点各有各的判题沙箱
```
之后正常 `docker/deploy.sh` 即可。
⚠️ **这个名字不在任何 registry 上。** 服务器上忘了 `docker load`compose 会去
pull 然后报找不到镜像(好在是响亮地失败,不是静默降级)。
⚠️ **改工具链就把末尾的序号 +1**(下一版叫 `oj2-judge-3``build.sh` 里的 `IMAGE`
compose 里三处,一起改)。`docker compose up -d` 不带 `--pull`,名字没变会静默用机器上
的旧镜像。官方镜像算第 1 版,所以我们自己重编的从 `-2` 起。
回滚:把三个 compose 的 image 改回
`registry.cn-hongkong.aliyuncs.com/oj-image/judge:1.6.1`,重新 `up -d`。别在服务器上
`docker image prune` 把那份旧镜像清掉。
## 和 `languages.ts` 的关系
**编译和运行命令不在镜像里**,在 `apps/api/src/judge/languages.ts`。镜像只负责把
`/usr/bin/gcc``/usr/bin/python3``/usr/bin/go``/usr/bin/node``/usr/bin/java`
这些绝对路径挂到正确的版本上(Dockerfile 末尾的 `update-alternatives`)。
gcc-14 把隐式函数声明、int↔指针互赋、不兼容指针类型从 warning 提成了 error`-w`
压不住。`languages.ts` 里的 `cLooseErrors` 三个 `-Wno-error=` 就是为此加的 ——
实测 1951 份历史 C 提交和 20 篇 C 教程的 93 个代码块,加了之后与 gcc-13 逐个文件
结果完全一致;不加的话有一批会从能过变成 CE。
## 这次升级是怎么验的
不写测试,全是实跑。除了 `smoke.ts` 的 13 条,还拿**生产库备份里的真实代码**逐个
文件对比了新旧镜像的编译结果(脚本是一次性的,结论记在这里):
| 语料 | 份数 | 老镜像 (gcc-13 / py3.12) | 新镜像 (gcc-14 / py3.13) | 差异 |
|---|---|---|---|---|
| 历史 C 提交(共 19262,随机抽样) | 1951 | 1725 过 / 226 CE | 一模一样 | **0** |
| 历史 C++ 提交(全量) | 882 | 603 过 / 279 CE | 一模一样 | **0** |
| 历史 Python 提交(共 104527,随机抽样) | 2000 | 1833 过 / 167 CE | 一模一样 | **0** |
| 20 篇 C 教程里含 `int main` 的代码块 | 93 | 93 全过 | 一模一样 | **0** |
**不加 `cLooseErrors` 那三个开关的话,1951 份 C 提交里有 26 份会从「能过」变成 CE**
(按比例算全库约 260 条),全是忘了 `#include <string.h>` 之类的隐式函数声明。
教程那 93 块本身写得规范,加不加都全过。
## 换镜像后怎么验
```bash
bun docker/judge/smoke.ts # 六种语言 + 六种状态码 + gcc 宽松度
```
它直接打判题机的 `/judge`,不需要起后端、不需要库里有题。用的 `languageConfigs`
就是线上那份,所以配置和镜像对不上会当场暴露。
**Go 那条坑记一下**(升级时发现的,升级之前就有):`GOCACHE` 指向容器的 tmpfs
`/tmp`,判题机重启后第一次 Go 提交是冷构建,Go 1.22 要 5.6 秒 CPU、超过 3 秒的编译
预算 —— 重启后第一个交 Go 的学生必吃一次 CE,后面的人缓存热了又都正常。Go 现在整个
删掉了,但**以后加回任何需要编译缓存的语言,记得把编译预算放宽**。
判题机装好之后,后台「判题机列表」应该能看到它上线(心跳走
`POST /api/judge-server/heartbeat`5 秒一次)。