前言

最近想找一个支持远程评测的开源OJ,目前可能主要有这些:

  • NOJ

    南邮近几年新开发的OnlineJudge,目前支持远程评测:

    Codeforces, UVa, UVaLive, HDU, ContestHunter, POJ, Vijos, PTA

    是目前看到的支持远程评测最多的开源OJ(除了远古的Vjudge)

    但是部署相对比较麻烦…

    Demo:

    Home | NOJ(但是很多时候可能上不去,比如现在QAQ)

    Home | YZZXOJ(扬州中学的OnlineJudge,基于NOJ搭建,可参考)

这篇博客主要介绍一下NOJ的简易版本搭建,如需按照这个博客部署到生产环境,代码演示中所有的账户或密码请自行更换。

开始

由于在某方面上,本博客可能会给我的学弟学妹看到,因此会写的较为啰嗦

先放上官方文档链接:https://njuptaaa.github.io/docs/#/noj/guide/deploy

准备工作

首先,你需要有至少一台服务器,用于配置NOJ

本文中展示的是单机配置,但同样简单介绍了多机的配置方法。

另外,本文使用的虽然并非官方文档中写的Docker安装法,需要自己配置Nginx,Php,Redis和MySql,但本文依旧会在部署JudgeServer的时候用到Docker。

服务器

我选用的是阿里云的服务器,系统为Centos 7.8 64位

如果使用阿里云,腾讯云等云服务商的服务器,需要提前在安全组内设置好开放端口

为便于操作,安全组开放了全部端口的TCP访问。

但这虽然方便但是并不安全,最好是根据后面自己定义的各个服务所需要的端口进行开放。


服务器建立完成后,本地建立SSH连接,以root身份登录,我使用的是MobaXterm


宝塔面板

为了便于我们后续的操作,这里我建议安装宝塔面板,会比手动调整要方便不少。

详细信息可见:宝塔linux面板,一键安装LAMP/LNMP/SSL/Tomcat

执行:

1
yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh

执行成功后,得到:

复制外网面板地址,按照给定的用户名和密码登录宝塔面板:

接着,根据他的指示绑定自己的宝塔账号即可登入系统。


必需软件配置

官方文档内,自行配置所需要的软件版本要求如下:

此处要特别注意的是PHP的版本2021年8月5日有效):

如果你下载的是master分支的,那么请选用PHP7.3

如果你下载的是dev分支的,那么请选用PHP7.4

否则,在部署NOJ评测功能时,会报错:

下文采用的是dev分支的安装策略。


在最开始的宝塔面板提示中,我选择如下配置:

注:phpMyAdmin只是方便自己查看,安不安装都行。

然后再去应用商店安装Redis,这里安装的是6.2版本

等待必需的软件安装完成后,就可以开始部署OnlineJudge了。


基本部署

代码上传

下载ZsgsDesign/NOJ at dev中的代码。并通过最初建立的SSH会话上传文件。

你可以自由选择部署目录,我选择的目录为/home/OnlineJudge/Web

当然,也可以通过宝塔面板进行上传和解压。

上传完毕后,应该是这样的:

1
2
3
4
# pwd
/home/OnlineJudge/Web
# ls
NOJ-dev.zip

而后,解压文件到当前目录:

1
unzip NOJ-dev.zip

按照官方文档,现在进入到第三步

Change your website root to public folder and then if there is a open_basedir restriction, remove it;

那么,我们在宝塔面板中进行设置:

  • 域名:填写你的域名或IP地址

  • 根目录:选择刚刚解压出来的public文件夹作为根目录

  • 数据库:顺便在这里可以直接建,在后续编辑.env文件时会用到。


Nginx设置 & 数据库配置

建站完毕后,在右侧设置中进行进一步设置:

  • 清除防跨站攻击


PHP配置

现在进入第四步

我们需要先解除对一些php函数的禁用,再执行composer install

在下图所在的位置中,删除掉proc_open()popen()putenv()

事实上,在后面的某些地方,还会在遇到某些函数被禁用而无法执行的情况,需要再到这里删除。我忘了是哪些了,遇到的时候再写。

如果出现这种提示就去解禁函数吧(

顺便,安装两个拓展fileinforedis,否则后面某些步骤可能会报错而无法进行。

做完这些之后,执行:

1
compose install

等待安装完成,大概就变成下面这样了:


权限设置

安装完成之后,开始设置权限。

在这个路径下:

1
2
# pwd
/home/OnlineJudge/Web

执行:

1
chown -R www:www ./

注意:此处我使用的wwwNginx创建的默认用户。如果你使用的是Apache之类(默认用户为www-data)要按需修改该命令。

当然,你同样可以使用宝塔面板来进行权限的修改。

再设置文件夹和文件的权限:

1
2
find /home/OnlineJudge/Web/ -type f -exec chmod 644 {} \;
find /home/OnlineJudge/Web/ -type d -exec chmod 755 {} \;

.env文件配置

设置完权限之后进入关键的一步,设置.env文件。

/home/OnlineJudge/Web下执行:

1
cp .env.example .env

而后,我们需要使用文本编辑器修改.env,你可以使用vim,也可以使用nano

如果没有,可能需要使用yum install nano进行安装,如下所示:

1
2
yum install nano
nano .env

打开.env后,关键的几点是:

  • APP_URL

  • SESSION_DOMAIN

    如果不设为null或设为IP,则在POST请求时容易出现419异常返回。

  • DB_USERNAME & DB_PASSWORD

    前文中创建网站时设置的数据库账号和密码。

  • APP_LOCALE

    网站显示的语言,如果你喜欢英文那么不修改也可以。

    语言配置见/home/OnlineJudge/Web/resources/lang


更改并保存.env后,执行:

1
php artisan key:generate

即新建一个APP_KEY并自动写入.env中。

这个APP_KEY在后文配置JudgeServer和测试数据同步时还会用到,需要记录下来。

然后,再执行:

1
php artisan migrate

此时会根据你设置的.env自动进行创建数据库表等操作。

而后,由于:

Since 0.4.1 NOJ uses Passport for API Auth Services, you need to install it first;

我们需要执行:

1
php artisan passport:install

大概的结果会是这样的:


定时任务

最后,我们需要设置定时任务:

1
crontab -e

插入一分钟执行一次的语句:

1
* * * * * php /home/OnlineJudge/Web/artisan schedule:run

保存并退出即可。

杂谈

vim下,按下insert进入插入模式,esc退出插入模式。退出插入模式后,按下冒号,写下:wq,就能够保存并退出。(虽然应该大家都知道)

到此,我们完成了所有前后端的配置,网站的注册功能应该已经是正常的了。

接下来我们要配置评测机和系统管理员


功能部署

Babel & Admin Panel

最基本的配置:

回到网站的根目录/home/OnlineJudge/Web

执行:

1
php artisan babel:install noj

即安装了自带的OJ评测。如果不安装则会出现:

新增题目时,没有可供选择的OJ · Issue #544 · ZsgsDesign/NOJ

同样的目录下,执行:

1
php artisan manage:create-admin

即启动交互式的创建管理员程序,按照指示创建用户后,可在:

1
http://【IPADDRESS】/admin/auth/login

进入管理员面板。


Docker 评测机 & 测试数据同步

而后,是另一个重头戏,配置JudgeServer和测试数据同步系统。

如果使用单部机器的话,是不那么需要测试数据同步系统的,会浪费不少空间,不过这里为了演示还是部署一下。

为了更加清晰,我们在原来/home/OnlineJudge下新建目录结构如下:

其中,Web就是我们刚刚配置完的前后端。

1
2
3
4
# pwd
/home/OnlineJudge
# ls
JudgeServer RsyncMaster RsyncSlave Web

我们需要使用至少三个docker镜像:

因此,我们需要先配置好docker环境。

这里我们使用国内源DaoCloud | Docker 极速下载

在任意地方,执行:

1
2
3
4
5
6
# 安装Docker
curl -sSL https://get.daocloud.io/docker | sh

# 安装Docker-compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

执行完毕后检查是否安装成功:

1
2
3
4
# docker -v
Docker version 20.10.8, build 3967b7d
# docker-compose -v
docker-compose version 1.29.2, build 5becea4c

检查完毕后顺便启动Docker服务,不启动的话docker-compose无法执行。

1
systemctl start docker

一切就绪后,我们按照官方文档配置docker-compose.yml

注意,由于我是单机配置,因此所有的IP地址都是一样的,多机配置需要自行修改。

注意:在某些地方上我所作的可能并不是作者本来的意图,或许会引入新的BUG(

需要重点修改的地方:

docker_compose.yml

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
version: '2'

services:

judge_server:
image: njuptaaa/judge_server
restart: always
read_only: true
cap_drop:
- SETPCAP
- MKNOD
- NET_BIND_SERVICE
- SYS_CHROOT
- SETFCAP
- FSETID
tmpfs:
- /tmp
volumes:
- $PWD/tests/test_case:/test_case:ro
- $PWD/log:/log
# - $PWD/server:/code:ro
- $PWD/run:/judger
environment:
- BACKEND_URL=http://YOUR_IPADDRESS:3000/api/judge_server_heartbeat
- SERVICE_URL=http://YOUR_IPADDRESS:12358
- TOKEN=YOUR_APP_KEY
ports:
- 12358:8080
  • RsyncMaster(推送测试数据)

需要重点修改的地方:

docker_compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: "3"

services:
noj-rsync-master:
image: njuptaaa/rsync
container_name: noj-rsync-master
volumes:
- /home/OnlineJudge/Web/storage/test_case:/test_case
- $PWD/storage/logs:/log
environment:
- RSYNC_MODE=master
- RSYNC_USER=ojrsync
- RSYNC_PASSWORD=CHANGE_THIS_PASSWORD
ports:
- "0.0.0.0:873:873"
  • RsyncSlave(接受测试数据到评测机)

重点修改的地方:

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: "3"

services:
noj-rsync-slave:
image: njuptaaa/rsync
container_name: noj-rsync-slave
volumes:
- /home/OnlineJudge/JudgeServer/tests/test_case:/test_case
- $PWD/data/rsync_log:/log
environment:
- RSYNC_MODE=slave
- RSYNC_USER=ojrsync
- RSYNC_PASSWORD=CHANGE_THIS_PASSWORD
- RSYNC_MASTER_ADDR=YOUR_IP_ADDRESS

后台配置

到此,Docker的配置就告一段落,回后台配置评测机:

然后,在题库中添加一道题目,尝试着提交吧!

注:你需要启动评测队列才能够提交题目,最基本的启动方法:

1
php artisan queue:work --queue=noj

事实上,此时你还需要禁用pcntl_signal(),pcntl_alarm()两个函数,才能够正常使用评测队列。


没了

这就是所有的内容了,算是一个基础向的操作指南。

第一次写这种教程(属实是上网找不到…摸索着安装略痛苦)

都看到这里了给答主留个言鼓励一下!

或者如果有哪里写错了欢迎评论区指出U•ェ•*U