# KECE OJ 评测机节点配置与扩容实操教程 本文档面向管理员与运维同学,演示如何在 KECE 分池架构下**新增一台评测机**、**在同一台机器扩容某个语言池**、以及日常**平滑下线维护与排错**。 --- ## 一、1 分钟搞懂新评测机是怎么工作的 1. **无需公网 IP、无需开放入站端口、无需同步题库目录**: - 评测机是 **主动拉取式(Pull Worker)**。只要新机器能通过内网访问主后端的控制面地址(如 `http://<后端内网IP>:8080/api/v1`),就能自动领单、按需下载测试点(本地 SHA-256 缓存,下载一次永久复用)并回传判题结果。 - **切记不要填公网业务域名**:公网网关出于安全隔离已将 `/api/v1/internal/judgers/*` 全部拦截为 `404`,评测机必须走内网或专用 VPN 地址。 2. **一进程一专池,宿主机总预算防打满**: - 每个 `judger` 实例只负责一种专池:`python`、`cpp`、`java`、`mysql` 或 `arkts`。 - 同一台物理机可以同时运行多个专池实例,由后台的 **「宿主机分组(Host Group)」** 限制这台机器允许同时跑的总并发任务数(`maxSlots`),防止考试高峰期多池并发把机器内存打爆。 | 专池标识 (`JUDGE_POOL`) | 运行时规格 (`JUDGE_RUNTIME_SPEC`) | 机器上需要提前准备的依赖 | 推荐单实例槽位 (`JUDGE_LOCAL_SLOTS`) | | :---: | :---: | :--- | :---: | | `python` | `python-v1` | Docker + `docker pull python:3.12-slim` | `2` ~ `4` | | `cpp` | `cpp-v1` | Docker + `docker pull gcc:13` | `2` ~ `4` | | `java` | `java-v1` | Docker + `docker pull eclipse-temurin:17-jdk` | `2` | | `arkts` | `arkts-v1` | Docker + `kece-sandbox-arkts:latest` 镜像 | `2` | | `mysql` | `mysql-v1` | **不需要 Docker Socket**,只需一个独立的判题 MySQL 8.0 实例(如 `3307` 端口) | `2` ~ `4` | --- ## 二、前置环境准备(在新评测机上执行) ### 1. 加入内网并确认能连通主后端 确保新机器与主后端处于同一内网或 VPN 虚拟局域网中,并测试连通性: ```bash # 测试能否访问主后端集群摘要接口(返回 code:200 即连通正常) curl -s http://<后端内网IP>:8080/api/v1/system/judgers/summary ``` ### 2. 按你要跑的池准备依赖 - **如果这台机器跑代码池(`python` / `cpp` / `java` / `arkts`)**:安装 Docker 并拉取对应镜像(跑哪个池就拉哪个,不用全拉): ```bash docker pull python:3.12-slim docker pull gcc:13 docker pull eclipse-temurin:17-jdk # 若需要跑 arkts 池,在仓库根目录构建镜像(或通过 docker save / docker load 导入): # docker build -t kece-sandbox-arkts:latest -f judger/Dockerfile.arkts judger ``` - **如果这台机器跑数据库池(`mysql`)**:启动一个专门给判题建删临时库用的隔离 MySQL 8.0 容器(**严禁填写生产业务库**): ```bash docker run -d --name kece-judge-mysql --restart unless-stopped \ -p 127.0.0.1:3307:3306 -e MYSQL_ROOT_PASSWORD=judge123 \ mysql:8.0 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci ``` --- ## 三、方式 A:通过管理后台网页手动接入(最直观,4 步完成) 适合随时给某台机器新增一个专池节点,全程可视化。 ### 第 1 步:在网页后台创建宿主机组并登记节点 1. 使用管理员账号登录 OJ 管理后台,进入 **评测与内容 -> 评测机管理(`/admin/judgers`)**。 2. **创建宿主机预算组**(如果这台物理机是第一次接入): - 点击「新建宿主机组」,例如名称填 `worker-node-01`,最大并发槽位填 `6`(根据该机器 CPU 核数填写,一般 4 核填 `6~8`,8 核填 `10~12`)。 3. **注册评测节点**: - 点击「登记评测节点」,填写表单: - **节点名称**:如 `worker-01-cpp`(全局唯一即可) - **评测专池**:选择你要跑的池,例如 `cpp` - **宿主机分组**:选择刚才创建的 `worker-node-01` - **最大并发槽位**:填 `2` - **节点探测地址(Base URL)**:**留空即可**(留空即使用免证书的主动心跳自检模式) - 点击确认后,页面会弹出 **一次性接入码(`Enrollment Code`)**(有效期 **10 分钟**,关闭后不再明文显示),复制该接入码。 --- ### 第 2 步:在评测机上配置并启动 Worker(任选 systemd 或 Docker Compose) #### 选法 1:Linux `systemd` 原生托管(推荐,最轻量稳定) 1. **准备二进制与服务模板**(首次在该机器操作一次即可,后续同机加新池无需重复): ```bash sudo install -d -m 0755 /opt/kece/judger sudo install -d -m 0700 /etc/kece/judger sudo cp ./judger /opt/kece/judger/judger sudo chmod 0755 /opt/kece/judger/judger # 写入通用多实例 systemd 模板(参考 deploy/judger/kece-judger@.service.example) sudo tee /etc/systemd/system/kece-judger@.service >/dev/null <<'EOF' [Unit] Description=KECE Judge Pool Worker Instance %i Wants=network-online.target After=network-online.target docker.service [Service] Type=simple User=root WorkingDirectory=/opt/kece/judger StateDirectory=kece-judger/%i Environment=JUDGE_CREDENTIAL_FILE=/var/lib/kece-judger/%i/credential.json Environment=JUDGE_ARTIFACT_CACHE_DIR=/var/lib/kece-judger/%i/cache EnvironmentFile=/etc/kece/judger/%i.env ExecStart=/opt/kece/judger/judger Restart=always RestartSec=5s TimeoutStopSec=40s LimitNOFILE=65536 LimitNPROC=4096 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload ``` 2. **写入专池实例配置文件**(例如实例名叫 `cpp-1`,对应 `/etc/kece/judger/cpp-1.env`): ```bash sudo install -d -m 0755 /var/lib/kece-judger/cpp-1/work sudo tee /etc/kece/judger/cpp-1.env >/dev/null <<'EOF' JUDGE_POOL=cpp JUDGE_RUNTIME_SPEC=cpp-v1 JUDGE_RUNTIME_VERSION=prod-1.0 JUDGE_CONTROL_URL=http://<后端内网IP>:8080/api/v1 JUDGE_ENROLLMENT_CODE=粘贴刚才网页复制的一次性接入码 JUDGE_LOCAL_SLOTS=2 WORKSPACE_DIR=/var/lib/kece-judger/cpp-1/work JUDGE_HEARTBEAT_INTERVAL_SECONDS=5 EOF sudo chmod 0600 /etc/kece/judger/cpp-1.env ``` > 若配置的是 **`mysql` 专池**(如 `/etc/kece/judger/mysql-1.env`),把 `JUDGE_POOL=mysql`、`JUDGE_RUNTIME_SPEC=mysql-v1`,并额外加上独立库连接信息: > ```ini > JUDGE_MYSQL_HOST=127.0.0.1 > JUDGE_MYSQL_PORT=3307 > JUDGE_MYSQL_USER=root > JUDGE_MYSQL_PASSWORD=judge123 > ``` 3. **启动并设为开机自启**: ```bash sudo systemctl enable --now kece-judger@cpp-1 sudo journalctl -u kece-judger@cpp-1 -n 20 --no-pager ``` 看到日志打印 `[worker] joined as node ...` 即说明首次凭据交换成功!长期凭据已自动保存在 `/var/lib/kece-judger/cpp-1/credential.json`,此时可随手删掉 `.env` 里的 `JUDGE_ENROLLMENT_CODE=` 那一行(以后机器重启会自动读取 `credential.json` 续接会话)。 #### 选法 2:Docker Compose 容器化启动 如果不想装 `systemd` 服务,可直接使用仓库 `deploy/judger/` 目录下的模板: ```bash # 1. 复制对应池的环境变量模板并填入 JUDGE_CONTROL_URL 与 JUDGE_ENROLLMENT_CODE cp deploy/judger/node.python.env.example python-1.env # 2A. 启动代码池节点(python / cpp / java / arkts) docker compose -f deploy/judger/compose.pool.yml --env-file python-1.env -p kece-judger-py1 up -d # 2B. 启动 MySQL 专池节点(专用无 docker.sock 模板) cp deploy/judger/node.mysql.env.example mysql-1.env docker compose -f deploy/judger/compose.mysql.yml --env-file mysql-1.env -p kece-judger-sql1 up -d ``` --- ### 第 3 步:回到网页后台「自检」并「启用」 1. 刷新 `/admin/judgers` 页面,可以看到刚启动的节点已有最新心跳时间,健康状态为 **`UNVERIFIED(待自检)`**,管理状态为 **`DISABLED(未启用)`**。 2. 点击该节点右侧的 **「发起自检」** 按钮: - 节点会在下一次心跳(约 1~5 秒内)自动执行沙箱/判题库探针检查; - 健康状态变为绿色的 **`HEALTHY(健康)`**。 3. 点击 **「启用(Enable)」** 按钮: - 节点状态变为 **`ENABLED`**,立即开始分担线上对应专池判题流量! --- ## 四、方式 B:使用 `deploy/sync_judger.py` 一键全自动部署/扩容 如果已经在新 Linux 机器上配置好 SSH 访问与 Docker,可以直接运行自动化脚本完成 **编译上传 -> 安装 systemd -> 创建宿主机组 -> 注册节点 -> 换取凭据 -> 自动自检 -> 自动启用**: ```powershell python deploy/sync_judger.py ` --host <目标评测机IP> ` --user ` --password "" ` --control-url "http://<后端内网IP>:8080/api/v1" ` --host-group "<宿主机预算组名称>" ` --node-prefix "<节点前缀>" ` --pools "cpp,python" ` --slots 2 ` --skip-backfill ``` --- ## 五、日常运维与排错速查 ### 1. 怎么安全停机维护而不弄丢正在判的题?(优雅排空) 1. 在后台 `/admin/judgers` 找到要停机的节点,将状态切换为 **`DRAINING(排空中)`**。 2. 节点会立刻停止接新题,并把手头正在跑的题判完回传。 3. 观察页面上该节点的 `Running(运行中)` 变为 `0` 后,再去机器上停止服务: ```bash sudo systemctl stop kece-judger@cpp-1 ``` 4. 维护完成后执行 `sudo systemctl start kece-judger@cpp-1`,在后台把状态从 `DRAINING` 改回 `ENABLED` 即可。 ### 2. 怎么在同一台机器上再开第 2 个同语言实例或调大并发? - **调大并发(优先推荐)**:无需多开进程!先在 `/etc/kece/judger/cpp-1.env` 把 `JUDGE_LOCAL_SLOTS=4` 改大并 `sudo systemctl restart kece-judger@cpp-1`,然后在网页后台把宿主机组和该节点的 `maxSlots` 一并调大即可生效。 ### 3. 常见报错原因与解决 | 现象 / 报错日志 | 原因 | 解决办法 | | :--- | :--- | :--- | | 日志报 `HTTP 404`(请求 `/internal/judgers/join`) | `JUDGE_CONTROL_URL` 误填成了公网业务域名 | 改为内网控制面地址 `http://<后端内网IP>:8080/api/v1` | | 日志报 `HTTP 401/403`(首次启动) | 一次性接入码已超过 10 分钟过期,或已被用过 | 在网页后台点该节点的「重置接入码」,把新码填入 `.env`,删除 `/var/lib/kece-judger/<实例>/credential.json` 后重启服务 | | 日志报 `HTTP 422 (code=INVALID_RUNTIME)` | `JUDGE_RUNTIME_SPEC` 与池名不匹配 | 确保 `JUDGE_RUNTIME_SPEC` 严格等于 `<池名>-v1`(如 `cpp-v1`、`python-v1`、`java-v1`、`mysql-v1`、`arkts-v1`) | | 后台点「发起自检」后状态变成 `FAILED` | 该机器上没拉取对应 Docker 镜像,或 `mysql` 池连不上 `3307` 判题库 | 点开节点详情查看失败原因,执行 `docker pull <对应镜像>` 或检查 `kece-judge-mysql` 容器后再点一次「发起自检」 | | 提交老题目提示 `409 题目尚未发布用例清单` | 该题缺少 `.in/.out` 测试文件 | 在管理后台进入该题目编辑页,重新上传完整的测试用例压缩包保存即可自动发布清单 |