Domjudge配置指南 & 校赛踩坑记录
条评论前言
最近要准备一次月赛,本来没准备启用Domjudge的,但是截止前一天忽然多了好多人。做了一下简单的压力测试,原来的学校OJ至少会爆炸20分钟…因此选用了腾讯云按量付费+Domjudge的方案进行配置。
但是这个坑,可以说不是一般的多啊…这里简单列列安装过程和坑点,希望能帮到后来者QAQ
不过由于作者已经把服务器释放掉了(按量付费的,买了高配,再烧就没钱了),所以缺少一些插图。
部署
数据库
这里我采用的是Docker版的部署方法。
参考链接:
- 计算机 · DOMjudge Docker 配置 - 知乎
- cn-xcpc-docs/docker.md at master · cn-xcpc-tools/cn-xcpc-docs
- 关于搭建 domjudge 还有其他一些 ICPC Tool 之类的事 | 赤红幻想
选用一台干净的Linux服务器,本人使用的是Centos7.8 64位。
首先部署数据库:
1 | docker run -d -it --name dj-mariadb -e MYSQL_ROOT_PASSWORD=ROOT998244353 -e MYSQL_USER=domjudge -e CONTAINER_TIMEZONE=Asia/Shanghai -e MYSQL_PASSWORD=DOM998244353 -e MYSQL_DATABASE=domjudge -p 13306:3306 mariadb --max-connections=1000 --max-allowed-packet=102400000 --innodb-log-file-size=202400000 |
重点:
- MySQL的root密码被设置为:
ROOT998244353
,如有需要请自己修改 - MySQL的domjudge密码被设置为:
DOM998244353
,如有需要请自己修改 - 设置了
--max-connections
为1000
,保证能够顺利进行数据库连接 - 设置了
--max-allowed-packet
为102400000
,特别注意,底层单位为B
,而不是KB
。此外,貌似你设置的值大于$1GB$,就会按照$1GB$来计算。因此我直接在原来的基础(第一次安装时记错了单位,以为底层是KB)上敲多了两个零。此处的值主要取决于你最大的测试数据点,一般选为最大测试点的两倍。 - 设置了
--innodb-log-file-size
为202400000
,单位同上。此处的值同样主要取决于你最大的测试数据点,一般选为最大测试点的十倍。此外,该变量与上面的不同,需要重启数据库才能更改,因此如果你使用docker部署数据库,请务必在启用时就做好参数的指定。
Domjudge Server
而后,部署domjudge server:
1 | docker run --link dj-mariadb:mariadb -d -it -e MYSQL_HOST=mariadb -e MYSQL_USER=domjudge -e MYSQL_DATABASE=domjudge -e CONTAINER_TIMEZONE=Asia/Shanghai -e MYSQL_PASSWORD=DOM998244353 -e MYSQL_ROOT_PASSWORD=ROOT998244353 -p 80:80 --name domserver domjudge/domserver:7.3.3 |
重点:
- 这里讲domjudge-server的访问端口直接设为了主机的
80
端口,如果使用云服务商请注意是否开放安全组。 - 这里虽然指定了时区为
Asia/Shanghai
,但实测没什么用,还是欧洲中部时区,不过不影响正常比赛。 - 使用
link
连接前面部署好的数据库,需要注意数据库密码一一对应。 - 指定了
domjudge
版本为7.3.3
,下面的judgehost
最好选用相同版本,否则容易出现未知BUG。
此时就可以通过80
端口进行访问了。我们先停止部署,查看一下对应的API KEY
和ADMIN SECRET
:
Domjudge
后台管理员的初始密码:
1 | docker exec -it domserver cat /opt/domjudge/domserver/etc/initial_admin_password.secret |
Domjudge
的API KEY
,配置judgehost
需要:
1 | docker exec -it domserver cat /opt/domjudge/domserver/etc/restapi.secret |
Judgehost
保存好上面的API KEY
。在开始部署judgehost
前,你还需要修改一下grub
:
请编辑/etc/default/grub
,修改:
1 | GRUB_CMDLINE_LINUX_DEFAULT="quiet cgroup_enable=memory swapaccount=1" |
如果GRUB_CMDLINE_LINUX_DEFAULT
有初始内容,可以将这段内容粘贴到原本的内容最后。
而后,开始配置judgehost
:
1 | docker run -d -it --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name judgehost-0 --link domserver:domserver --hostname judgedaemon-0 -e DAEMON_ID=0 -e JUDGEDAEMON_PASSWORD=UCgqHXW1WajmF9AG -e CONTAINER_TIMEZONE=Asia/Shanghai domjudge/judgehost:7.3.3 |
judgehost
需要以特权模式进行,请记得加上参数--privileged
UCgqHXW1WajmF9AG
为我的API KEY
,每个人的KEY
不同,请更改该项参数后再执行命令。- 指定了
judgehost
版本为7.3.3
,上面的domjudge
最好选用相同版本,否则容易出现未知BUG。 - 在此之前你需要修改
grub
,别忘了。
如果你需要部署多台judgehost
,请保证:
--hostname
各不相同,如judgedaemon-1
,judgedaemon-2
;DAEMON_ID
各不相同,如DAEMON_ID=1
,DAEMON_ID=2
;(此处为CPU核心,如果核数较少就不要设了)--name
各不相同,如judgehost-1
,judgehost-2
。
如果你需要配置某台judgehost
不运行某类程序(某种语言,某道题的提交等…),请在后台设置restriction
。
此时你可以使用自带的账号进行测试。
你或许需要去Users
里面Set
一下dummy
的密码,然后再用这个账号登陆。
交一发HelloWorld!
,如果顺利,C/C++
都是没问题的。
但是Java/Python
会Runtime Error
,原因是Java Not Found / Python Not Found
不难推测是Docker
里面有点问题,使用以下命令进入某台评测机:
1 | docker exec -it <ID> /bash/bin |
发现judgehost
里面使用chroot
进行隔离,命令在chroot /chroot/domjudge/
下进行,于是:
1 | chroot /chroot/domjudge/ |
在这一步可以发现,返回结果都是Not Found
,于是考虑在里面更新环境。默认源安装太慢,考虑换源。
注意,尽管默认源安装较慢,但是换源之后gpg
证书会提示找不到公钥!而如果你要导入证书,需要gnupg
,但是judgehost
里是不带这个的。
因此,你需要先:
1 | apt update |
忍受一会龟速后,你可以导入新的公钥了,这时我们先换源,而后执行apt update
。
如果服务器在海外当我没说。
由于我们换的是chroot
里面的源,你最好在主机内,使用类似下面的命令:
1 | docker cp ./sources.list <ID>:chroot/domjudge/etc/apt/sources.list |
将你准备好的sources.list
传入docker
内,执行:
1 | apt update |
此时,不出意外,会报错:
1 | GPG error: The following signatures couldn't be verified because the public key is not available |
使用以下命令,将出现的不能被验证的公钥导入apt
内:
1 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys <XXXXXXXXX> |
此时,再执行apt update
,就能完美更新源了,接下来我们安装Python
环境和Java
环境。
特别注意,请不要使用openjdk-8-jdk
,版本太老旧了,安装之后仍然会报错无法评测java
程序。
如果你安装了,会报错:
1 | Error: A JNI error has occurred, please check your installation and try again |
按照网上的说法,这一般是由于java
和javac
的版本不一致造成的问题,但是,如果你安装了这个老旧的版本,你会发现,虽然版本都是1.8.0_231
,但后台依然会报错。
我后续安装的是openjdk-13-jdk
,源更换的是腾讯云的Ubuntu
源。
此外,你还需要特别注意在docker
里安装openjdk
的坑,尤其是还在chroot
下。
参考链接:
- 在CHROOT环境中工作 - 尝试安装JDK时出错 - VoidCC
- Ubuntu 18.04 JDK install failing because of weird dependency issues · Issue #64
- #863199 - error creating symbolic link ‘/usr/share/man/man1/rmid.1.gz.dpkg-tmp - Debian Bug report logs
- Unable to install openJdk8 on the container · Issue #182 · puckel/docker-airflow
解决方案:
在chroot
里,运行:
1 | mount -t proc none /proc |
而后,顺利的话,就可以直接安装openjdk
了。
Python
环境安装没什么坑点,直接apt install
就好了。
至于其他的配置,如针对某项语言设置时限倍数,直接在后台摸索就可以了。
配置比赛
主要分为几类:配置题目数据(以及spj
),外部导入队伍和登录账号,配置比赛信息。
后台体验卡:DOMjudge - Online Demo
题目数据
Domjudge的后台只能单点的修改数据,我们填入基本的题目信息后,在Problem Achieve
里面把题目下载到本地,此时题目是一个压缩包,解压之后,把内部填成这个样子:
1 | │ domjudge-problem.ini |
简单解析:
problem.pdf
,为题面,可以选择html/pdf/忘了
三种格式,在比赛后台可以上传,也可以直接在这里上传。domjudge-problem.ini
,存放该题的评测细节,如果是传统题,里面只有时限,大概长这样:1
timelimit='1'
problem.yaml
,存放该题的评测细节,如果是传统题,里面应该只有题目名字和内存限制(单位MB):1
2
3
4# Problem exported by DOMjudge on 2021-12-11T13:38:42+01:00
name: 'Hard & Crazy Problem'
limits:
memory: 512/data/secret
,存放评测数据,用于后台评测,选手无法获得这些数据。特别注意,数据需要有相同的文件名,并且输入数据后缀为
.in
,输出数据后缀为.ans
。可以使用
Renamer
一类的工具批量改名。/data/sample
,如果有这个文件夹,里面的数据将在选手的Problem Set
里显示,并允许下载。数据格式要求与
secret
一样。
然后我们重新打包成zip
,上传到domjudge
,在编辑题目的地方,最下面有个import
,将压缩包选中并上传就可以了。如果你这一步出现了ERROR 2006 (HY000):MySQL server has gone away
或者500
错误的话,请检查你的数据库max-connections
,--max-allowed-packet
,innodb-log-file-size
参数是否合理。
一个简单检查的方式是,在jury
界面,有个config cheker
,点击进入后domjudge
会进行一些简单的判断。
或直接在地址栏输入http://[Server IP/Other]/jury/config/check
,进入检查界面,看看参数设置是否合理。
拓展链接:4.6 检查确保一切就绪 - DOMjudge 中文文档
如此,你应该顺利的上传了对应的文件,看起来一切正常。
那么,有spj
的题该怎么上传?
以下是一份有(更准确的,是复用了以前的)spj
的题的目录结构:
1 | │ domjudge-problem.ini |
其中,problem.yaml
有所更改。具体为:增加了validation: custom
一行:
不使用 SPJ 不需要定义 validation: 'custom'
,也不需要 output_validators
1 | # Problem exported by DOMjudge on 2021-12-11T13:38:57+01:00 |
·
而后,在/output_validators/validate/
里放入checker.cpp
(自己所编写的spj
)即可。
所使用的testlib.h
需要修改一下,貌似是某个魔改版?
我所参考的题包和testlib.h
来源于:
特别注意的是,如果你是复用SPJ
,请参考官方文档修改返回值:Output validator - Problem Archive
A validator program is required to report its judgment by exiting with specific exit codes:
- If the output is a correct output for the input file (i.e., the submission that produced the output is to be Accepted), the validator exits with exit code 42.
- If the output is incorrect (i.e., the submission that produced the output is to be judged as Wrong Answer), the validator exits with exit code 43.
修改完毕后,按照上面的目录格式填入checker.cpp
,以及修改problem.yaml
,其他保持不变,打包上传即可。
如果使用Polygon
出题,貌似有一个可以转换的工具,之前没有注意到没去测试,见:
导入队伍和登录账号
在Domjudge
里,队伍和账号是分开的。我的需求是,利用Domjudge
举办单人赛,因此每个队伍只有一个人。此外,比赛只面向校内,因此不配置国家,学校等信息。由于相当于月考,也不配置气球和打印了…
官方文档:Contest Control System Requirements - ICPC-Contest Control Standard
参考链接:
简要过程:准备两个文件,team.tsv
和account.tsv
,注:tsv
代表用tab
进行分隔。
一个简单的方案是,在Excel
中将数据处理好,然后粘贴到notepad++
里,得到的就是以tab
分隔的数据。
对于team.tsv
,第一行总是teams[tab]1
。然后第二行开始,如下:
- 第一个是【唯一队伍编号】,你可以从某个数字任意开始,但是不能够与以往的数据重复;
- 第二个是【ICPC ID】,说实话这个我也不用配置,不过顺势配上去了;
- 第三个是【队伍类型】,选择$3$,代表
Participants
; - 第四个是【队伍名称】,将会显示在榜单上。后面接上四个
[tab]
,代表这些数据设空。这样就能做完一个最基础的team.tsv
了。
在account.tsv
中,你需要构造一个账号,对应上【唯一队伍编号】。
下面列出的匹配方式搬运自上面的北航春训 DOMjudge 配置坑点 - Chielo’s Blog。
accounts.tsv
导入的时候也会自动去挂队伍ID,但方式是跳过username
前面的非数字字符、再跳过0
以后,以剩下的数字字符作为对应的队伍ID(剩下的不是数字就不挂队伍了)。对应的源码:
1 $teamid = preg_replace('/^[^0-9]*0*([0-9]+)$/', '\1', $line[2]);
更具体的,我所构造的account.tsv
如下:
简单解释,第一行固定为accounts[tab]1/
。从第二行开始:
- 第一个为【账号类型】,这里固定为
[team]
; - 第二个是【名称】,在管理员界面点入队伍再点入对应用户可以看到对应的真实姓名是谁,在导出榜单时无用;
- 第三个是【登陆账号】,匹配方式上面已经提到;
- 第四个是登陆密码,我在
Excel
里使用randbetween
生成的。
由此就能完成team.tsv
和account.tsv
,先导入team
,再导入account
即可。
发放账号
为了防止有心人通过非正当手段查看代码,以及减轻压力(其实用邮箱也行,但许多人平时并没有查看邮箱的习惯),账号采用【问卷星平台的对外查询】的方式发放。
类似这样,整理好一个表格后,上传到问卷星的【表单】,选择【导入Excel文件】,然后【配置对外查询】。
我所配置的是,你需要输入正确的姓名,学号,手机号,才能够查到对应的信息。
配置比赛信息
配置比赛信息非常的简单,只需要打开后台按着操作就可以了…
需要注意的是,建议对Java
程序的评测,内存开到512M
以上,在一些题目中,Java
的代码非常容易因为空间不够RE
,并会暂停judgehost
评测java
,需要手动调整后再继续评测。
赛后
导出代码
可以使用:LaiJunBin/domjudge-code-download-tool: Download domjudge contest source code tool.
在.env
里配置对应信息后按照指示操作即可。
代码查重
虽然样式丑了一点,但是功能相当强大,个人一般使用jplag
查重。
导出ghost文件
需要自行修改一下result_map
,添加对OLE
的处理。其他按照代码中所说即可。
- Install requests.
- Run the DOMjudge server.
- Change the constants below.
- python3 DOMjudge-to-CFgym.py > contest.dat
批量数字签名 & 发放证书
可以使用:PDF Signer – Digital Signature Software
先利用PS内的变量功能+证书模板,生成带名字的证书。
然后选用特定的签名图片和数字证书进行签名即可。
其他
对于比赛平台,你可以使用一些手段进行压力测试,防止开局就崩了(
简单的压测可以使用:phper-hejing/insane: HTTP负载测试,压力测试,专为API设计的测试工具。
关于滚榜,打印等比较进阶的内容后续再研究(或许会再更的!)