Deploy DotOJ

主要的流程是先在虚拟机上配一遍,再放到某服务器上,所以先踩踩坑。

#DotOJ on VM

先看一下需要装的机子的系统和版本:

1
2
3
4
5
6
dotoj@Genshin-Impact-Server:/$ lsb_release -a
No LSB modules are available.
Distributor ID: Linuxmint
Description: Linux Mint 21.3
Release: 21.3
Codename: virginia

Releases 这里可以看到对应的 Ubuntu 版本是 Ubuntu Jammy。

#虚拟机配置

拿 VMware 装一下这个 Linux Mint 21.3 “Virginia”。

直接选 Linux 5.x(64 位)然后正常操作一通之后开机,哇噻,爆炸了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[    1.442360] Failed to execute /init (error -2)
[ 1.442553] Kernel panic - not syncing: No working init found. Try passing i
nit= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance
[ 1.442729] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.15.0-91-generic #101-
Ubuntu
[ 1.442808] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop
Reference Platform, BIOS 6.00 11/12/2020
[ 1.442908] Call Trace:
[ 1.442938] <TASK>
[ 1.442968] show_stack+0x52/0x5c
[ 1.443009] dump_stack_lvl+0x4a/0x63
[ 1.443062] dump_stack+0x10/0x16
[ 1.443107] panic+0x15c/0x334
[ 1.443147] ? putname+0x59/0x70
[ 1.443206] kernel_init+0x150/0x150
[ 1.443252] ? rest_init+Ox100/Ox100
[ 1.443296] ret_from_fork+0x1f/0x30
[ 1.443342] </TASK>
[ 1.443411] Kernel Offset: 0x26400000 from 0xffffffff81000000 (relocation ran
ge: 0xffffffff80000000-0xffffffffbfffffff)
[ 1.443529] ---[ end Kernel panic - not syncing: No working init found. Try
passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for
guidance. ]---

原因是内存设太小了,默认给的 768MB 不够用,开到 2GB 成功启动,启动之后记得按照桌面上给的指引安装一下操作系统。

#在虚拟机上安装 docker

直接参考 Docker on Ubuntu 安装指南就行了,有一些需要注意的地方:

首先建立 apt repo(记得把 ${UBUNTU_CODENAME:-$VERSION_CODENAME} 改成对应的 ubuntu 版本例如这里的 jammy):

1
2
3
4
5
6
7
8
9
10
11
12
13
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

可以 apt-cache madison docker-ce | awk '{ print $3 }' 看一下 repo 里面有什么版本,不过直接装就可以了,rt:

1
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

检查:

  • systemctl status docker 看 docker daemon 的运行状态,目前应该能看到服务正在运行
  • sudo docker run hello-world 来测试 docker,应当可以正常运行

#在虚拟机上部署 DotOJ

参考 Deployment 来搞一搞:

#1. Deployment Prerequisite

DotOJ 的判题服务需要依赖 Linux 内核的 CGroupV1 (Control Groups Version 1) 特性来限制进程的资源使用。

较新的 Linux 发行版,如 Fedora 31+ 和 Debian 11+ (包括 Mint Virginia 对应的 Ubuntu Jammy,它也是基于 Debian 11+ 的内核),默认已经切换到了 CGroupV2。所以如果直接在这些系统上运行,可能会遇到判题服务无法正常工作的问题,因为它期待的是 CGroupV1。

这个问题需要通过编辑 GRUB 配置文件 /etc/default/grub,找到 GRUB_CMDLINE_LINUX_DEFAULT,在引号内添加 systemd.unified_cgroup_hierarchy=0 来解决。

另外,对于 Debian 8+ 及衍生版(包括现在讨论的 Mint),还需要额外启用 CGroup 的内存和 swap 账户功能,所以也需要相同地添加 cgroup_enable=memory swapaccount=1 来解决。

完成修改后,为了更新 GRUB,你需要运行 sudo update-grub 并重启以刷新配置。

省流:需要先将 /etc/default/grub 中进行如下修改:

1
2
3
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
^ old --- new v
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash systemd.unified_cgroup_hierarchy=0 cgroup_enable=memory swapaccount=1"

然后 sudo update-grub 并重启。

检查:

  • 运行 cat /proc/self/cgroup 应当有若干不为 0 开头的行(修改之前只有一行)
  • 运行 cat /proc/cmdline 应当可以在输出中找到前面编辑加入的行

#2. Docker Containers

先为 start.sh 后面拉镜像的操作提前登陆一下:(sudo 的时候无法访问当前用户认证的结果,所以仅登录是不够的,除非你就是 root)

  1. docker login reg.nju.edu.cn,账号密码登录

  2. sudo usermod -aG docker ${USER} 把自己加到 docker 用户组里

  3. 然后 newgrp docker 或注销并重新登录

然后把 docker 拉下来跑:

  1. apt 装一下 docker-compose

  2. 参考 env-example 进行配置然后改名为 .env

    • HTTP_PORT_HOST 是最终访问 oj 的端口
  3. 先跑 start.sh,这时应该还访问不了 oj 页面。

    • 可以使用 docker compose ps 查看运行情况,docker compose logs webapp 看 log

做完以上的内容之后需要手动创建一下 worker webapp plagiarism 用户。(不知道本来有没有自动脚本,总之可以折腾出来)

1
2
# 使用 psql 连接到默认的 postgres 数据库,以 postgres 用户身份
docker exec -it judge-new-postgres-1 psql -U postgres -W
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
-- 确保这里的密码与 */appsettings.json 中 PgSqlConnection 的密码一致
CREATE USER webapp WITH PASSWORD 'aaaaaaa';
CREATE USER worker WITH PASSWORD 'bbbbbbb';
CREATE USER plagiarism WITH PASSWORD 'ccccccc';

\c judge; -- 注意!这里需要输密码,不要一下子全粘贴进去

GRANT CONNECT ON DATABASE judge TO webapp;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO webapp;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO webapp;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO webapp;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO webapp;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO webapp;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO webapp;

GRANT CONNECT ON DATABASE judge TO worker;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO worker;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO worker;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO worker;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO worker;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO worker;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO worker;

GRANT CONNECT ON DATABASE judge TO plagiarism;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO plagiarism;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO plagiarism;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO plagiarism;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO plagiarism;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO plagiarism;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO plagiarism;

然后重启一下因为认证失败被停掉的 webapp worker plagiarsim 就可以了。

到了现在这一步,应该可以访问 oj、新建考试/题目、进行测评了,可以搞一道题过来试试看:

#DotOJ on Server

你永远想象不全一个内网服务器会遇上什么问题。

#在服务器上安装 docker

apt 的代理被填了一个已经挂掉的地址,可以如下检测并清除:

1
2
3
4
sudo grep -r "Proxy" /etc/apt/
sudo mv /etc/apt/apt.conf /etc/apt/apt.conf.bak # 备份
sudo mv /etc/apt/apt.conf.d/proxy.conf /etc/apt/apt.conf.d/proxy.conf.bak # 备份可能存在的代理配置文件
sudo apt-get update

关于镜像:

总之 apt 什么的我们是可以使用 mirror.nju.edu.cn 的,docker 理论上可以用 docker.nju.edu.cn,但是似乎设置一直没有生效,不过对 DotOJ 没什么影响(因为它的镜像本来就在这里)。

然后折腾了半天还是因为签名的问题装不了。

最后按照 Docker on Ubuntu 安装指南的第二种方法做的(直接用软件包安装),用 git.nju.edu.cn 把软件包复制进去再手动安装,总之也算跑起来了。

#在服务器上部署 DotOJ

其余部分继续按照上面的做法就可以了。遇到的关键问题是镜像站挂了,所以需要在别的地方下好了再传进去手动导入。

下载并打包(此处一定要注意导出文件的权限,可能需要 chmod):

1
2
docker pull postgres:16.1
docker save postgres:16.1 -o postgres-16.1.tar

传到服务器之后:

1
2
docker load -i postgres-16.1.tar
docker images # 查看镜像列表

rabbitmq pgsql 和 oj 的账号都可以阅读对应容器的配置文件来得到;另外,可以 start.sh <count> 来实现多线程测评,推荐设置为 cpu 核数。

在完成后,最好对以下功能进行测试:

  1. 导入题目

  2. 提交答案并测试(传统题、Lab problem)

  3. 多线程测评

  4. 注册多个用户,提交相似代码并查重

喜报:这下真的搞完了!腰疼。