简单记录一下这次部署 DOMjudge 的过程,以防止自己忘记了部署步骤。

这次是在学校服务器上部署的 DOMjudge,因为我不太会使用 Linux 系统,在部署过程中遇到了很多问题。最后采用 docker-compose 终于是成功将 DOMjudge 给部署成功了。

首先,学校服务器的系统版本如下:

服务器是CentOS的

之前部署失败的经历就不再赘述了,只记录用 docker-compose 部署成功的过程,为了给新手带来更好的部署体验,本文将尽可能详细地描述部署细节。

0. 系统准备

  • 注意:DOMjudge评测系统暂不支持 Windows 操作系统,请在 Linux 操作系统上部署,我的系统版本是Centos 7.9,若你的是 ubuntu,则将指令中的 yum 换成 apt 即可。

(1) 设置 cgroups (Linux Control Groups)

若你的系统未安装 vim ,请先安装 vim

1
yum -y install vim*

编辑 /etc/default/grub 文件 (需要 sudo 权限)

1
sudo vim /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT 项目后面添加以下内容(若没有该项可以自己添加):

1
GRUB_CMDLINE_LINUX_DEFAULT="quiet cgroup_enable=memory swapaccount=1 isolcpus=2 systemd.unified_cgroup_hierarchy=0"

修改后重新加载一下 grub 配置文件(没有该指令就跳过):

1
sudo update-grub

重启系统:

1
sudo reboot

等系统重启完成后就行了。

1. 安装 docker 和 docker-compose

安装这两个软件请自行去官网部署文档安装,这里不再赘述。请 务必 参照 docker 官方部署文档安装 docker 和 docker-compose!

接下来,我们在 /etc/docker-compose/ 目录下设置 docker-compose 项目,使得之后在此文件夹里的 docker-compose 项目都可以通过系统服务来进行管理

1
sudo mkdir -p /etc/docker-compose

接下来设置一下系统服务,我们创建 /etc/systemd/system/docker-compose@.service 文件

1
sudo mkdir -p /etc/systemd/system/docker-compose@.service

并用 vim 给该文件中添加以下内容:(同样要注意需要 sudo 权限)

(ps:如果你是没用过 vim 的新手,你需要知道的是,vim 需要按 i 才会切换成编辑模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=%i service deployed with docker compose
Requires=docker.service
After=docker.service

[Service]
user=root
Type=simple
WorkingDirectory=/etc/docker-compose/%i
ExecStart=/usr/local/bin/docker compose up --remove-orphans

[Install]
WantedBy=multi-user.target

Esc + : + w + q 保存后,接下来我们需要重新载入系统服务配置文件

1
sudo systemctl daemon-reload

到这一步,我们的准备工作完成了。

2. 部署DOMjudge

(1) 先 cd/etc/docker-compose/ 目录下,创建三个文件

分别是:

  • database.secret
  • docker-compose.yml
  • judgehost.secret

用 vim 编辑器分别给上面的文件填入如下内容:

1
2
MYSQL_ROOT_PASSWORD=<YOUR PASSWORD>
MYSQL_PASSWORD=<YOUR PASSWORD>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
version: "3.9"

services:
dj-mariadb:
container_name: dj-mariadb
image: mariadb:latest
restart: unless-stopped
ports:
- "13306:3306"
volumes:
- ./database:/var/lib/mysql
env_file: database.secret
environment:
- MYSQL_USER=domjudge
- MYSQL_DATABASE=domjudge
- CONTAINER_TIMEZONE=Asia/Shanghai
command: --max-connections=1024 --max-allowed-packet=1G --innodb-log-file-size=512M
healthcheck:
test: mysqladmin ping -h localhost -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
start_period: 10s
interval: 5s
timeout: 1s
retries: 5

domserver:
container_name: domserver
image: domjudge/domserver:latest
restart: unless-stopped
ports:
- "80:80"
links:
- 'dj-mariadb:mariadb'
depends_on:
dj-mariadb: { condition: service_healthy }
env_file: database.secret
environment:
- MYSQL_HOST=mariadb
- MYSQL_USER=domjudge
- MYSQL_DATABASE=domjudge
- CONTAINER_TIMEZONE=Asia/Shanghai

judgehost:
image: 'domjudge/judgehost:latest'
links:
- 'domserver:domserver'
depends_on:
domserver: { condition: service_healthy }
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
env_file: judgehost.secret
environment:
- CONTAINER_TIMEZONE=Asia/Shanghai
deploy:
mode: replicated
replicas: 2
endpoint_mode: dnsrr
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 5
1
JUDGEDAEMON_PASSWORD=<GET THIS FROM TERMINAL>

(2) 初始化 DOMjudge 系统和数据库

首先自己设置一个数据库密码,替换掉 database.secret 文件中的 <YOUR PASSWORD> 内容;

然后只运行数据库和后端系统,得以获得判题机 judgehost 的 API key

1
sudo docker-compose up -d dj-mariadb domserver

输入如下指令获取 judgehost 的 API key

1
sudo docker exec -it domserver cat /opt/domjudge/domserver/etc/restapi.secret

将获取到的 API key 替换 judgehost.secret 文件中的 <GET THIS FROM TERMINAL> 内容

这里注意一下,倘若你发现数据库无法初始化成功,应该是由于 mariadb 版本导致的,可以把 latest 修改成之前的某个版本,latest是最新版本的意思,例如我就是修改成了 10.7,那么就在 docker-compose.yml 文件中将 mariadb:latest 修改成 mariadb:10.7 。同理,domserver 和 judgehost 的版本也可以指定,例如 8.2.0,并尽可能规定它们为同一个版本,否则可能会出现未知 Bug 。

(3) 启动判题机

文件默认创建两个判题机,请根据你的实际情况设置判题机的个数,最好 别超过 CPU 的核心数量

修改 docker-compose.yml 中 judgehost 服务中的 replicas 值,即可指定判题机个数

1
2
3
4
5
6
7
8
...
judgehost:
...
deploy:
...
replicas: 2 //修改这里
...
...

然后运行指令启动所有服务

1
sudo docker-compose up -d

过几秒后,我们打开docker列表,发现镜像都成功启动了(我是创建了四个判题机)

docker容器列表

此时打开浏览器输入部署机器的 IP 地址即可访问 DOMjudge

DOMjudge登陆界面

管理员账号:admin

密码需要通过如下命令获取

1
sudo docker exec -it domserver cat /opt/domjudge/domserver/etc/initial_admin_password.secret

在你确定了 DOMjudge 可以正常使用后,我们创建系统服务以设置开机启动

1
sudo systemctl enable docker-compose@domjudge.service

至此, DOMjudge系统已经部署成功。

若你想了解更多信息,可以访问下面的网站:

3. 配置比赛

主要需要实现三个功能:

  • 配置语言参数
  • 配置题目数据(包括spj、交互)
  • 外部导入队伍和登录账号
  • 配置比赛信息

(1) 配置语言

在Jury面板中点击Language选项,可以看到各种语言的配置。DOMJudge默认的编程语言是C、C++、Java、Python。我们需要修改一些东西才能适用于现在的XCPC比赛。

我们分别对这四个语言的编译参数作如下修改:

  • C语言:

添加 -DONLINE_JUDGE 选项,以支持local debugging;

添加 -std=c11 选项,以支持C11;

C语言 run.sh的配置

  • C++:

添加 -DONLINE_JUDGE 选项,以支持local debugging;

添加 -std=c++17 选项,以支持C++17;

C++ run.sh的配置

  • Java:

添加 -DONLINE_JUDGE 选项,以支持local debugging;

Java run.sh配置

  • Python:

添加一行 export ONLINE_JUDGE=1,如下图所示。

Python run.sh配置

(2)题目数据

首先,我们填入基本的题目信息,在 Problem Achieve 里面把题目下载到本地,此时题目是一个压缩包,解压后,让目录结构变成这样子:

1
2
3
4
5
6
7
8
9
10
│  domjudge-problem.ini
│ problem.pdf
│ problem.yaml

└─data
└─secret
1.ans
1.in
2.ans
2.in

若有 spj 的话,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
│  domjudge-problem.ini
│ problem.yaml

├─data
│ └─secret
│ 000.ans
│ 000.in
│ 001.ans
│ 001.in
│ 002.ans
│ 002.in
│ example0.ans
│ example0.in
│ example1.ans
│ example1.in

└─output_validators
└─validate
checker.cpp
testlib.h

spjproblem.yaml 也需要更改(添加了 validation: custom 一行):

1
2
3
4
name: 'HelloWorld'
validation: custom
limits:
memory: 512

你不使用 spj 的话就不需要这一行,也不需要 output_validators 文件夹。

随后,在/output_validators/validate/里放入checker.cpp(自己所编写的spj)即可。

所使用的 testlib.h 库文件,你可以参考以下链接:

打包好一切后,上传即可,DOMjudge会自动解析并生成题目。

(3)导入队伍和生成账号

DOMjudge 与其他的 OJ 系统略有不同,每个 user 需要有 team 才能参加比赛,而实际上在我学校举办的比赛里大部分比赛都是单人赛,因此每个队伍里只有一个人。

虽然可以一个一个导入用户信息,不过在举办一些比赛时,一般都是几十到一百人,这时候挨个挨个录入用户未免过于低效。我们可以准备 teams.tsvaccount.tsv 两个文件来批量导入以生成用户和队伍。

注意到 .tsv 文件是tab 键作为分隔符的,所以你可以事先在 Excel 里把信息输入好,再将信息复制到 vscode 或者记事本里,这样就可以做到相邻数据间有 tab 分隔。

  • 对于 team.tsv 文件而言,格式如下:
1
2
teams    1
唯一的队伍ID 唯一的外部ID team_category的ID 队名 校名 校名(缩写) 国家 唯一的校名外部ID

例如:

1
2
teams	1
101 1001 3 重生之我是菜狗 清华大学 清华 中国 THU

各个参数的含义在官方文档里是这样定义的:

  • 对于 account.tsv 文件而言,格式如下:
1
2
accounts	1
角色(team, admin等) 名字 username password

例如:

1
2
accounts	1
team 江莉 1000 11111111

各个参数的含义在官方文档里是这样定义的:

由此就完成了两个文件的配置。然后先导入 team.tsv,再导入 account.tsv 即可。

(4)配置比赛

配置比赛也能通过 .yaml 文件,不过比较麻烦,这里建议直接打开后台照着操作就是了。

不过要注意一点,建议对 Java 程序的评测开到 512M 以上,Java 经常会因为内存过大而导致 RE

关于配置比赛的更多详细的介绍,你可以参阅这篇博客:

或者去官方文档了解详情,官方文档啥都有。

4. 赛后

日后更新…