部署 LibreOJ
前言
本文将会带你一步一步部署 LibreOJ 的基础服务,包括前端、后端和评测机。LibreOJ 的 Github 为 LibreOJ · Github。本文借鉴了 xzm111 的搭建 Lyrio。
本文将在同一台服务器上的 /opt 目录部署 LibreOJ。当然,评测机最好单独部署,本文会在介绍评测机需要的前置时进行提示。尖括号包住的字符串是变量,请根据实际情况填写。
前置
服务器 & 系统
你需要至少一台装有 Linux 系统的服务器来部署 LibreOJ,若条件有限,可以使用虚拟机。本文使用的系统是 Ubuntu 22.04。
安装时请切换到 root 用户。
Node.js
评测机需用。注意不能使用最新版,否则在 yarn 构建时会报错,若已安装 node.js 要先卸载。这里使用 v18.15.0 版。
cd /opt
curl -O https://nodejs.org/dist/v18.15.0/node-v18.15.0-linux-x64.tar.xz
tar -xvf node-v18.15.0-linux-x64.tar.xz
mv node-v18.15.0-linux-x64 node
export PATH=$PATH:/opt/node/bin/ # 加入到 PATH 中,临时加入
用以下命令检查。
node -v
npm -v
Yarn
评测机需用。用以下命令安装 Yarn。
npm install yarn
export PATH=$PATH:/opt/node_modules/yarn/bin/ # 加入到 PATH 中,临时加入
用以下命令检查。
yarn -v
在部署时若更换了终端,请执行以下命令。
export PATH=$PATH:/opt/node/bin/
export PATH=$PATH:/opt/node_modules/yarn/bin/
数据库
本文选择安装 MariaDB,也可以安装 MySQL。
curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash
apt install mariadb-server
systemctl enable mariadb # 设置开机自启
测试 MariaDB,输入 exit; 退出。
mariadb
Redis
用以下命令安装 Redis。
apt install redis
nginx
用以下命令安装 nginx。
apt install nginx
systemctl start nginx
访问服务器的 IP 地址(域名),若成功出现 nginx 的网页则成功。若失败,大概率为 80 端口被占用,排查是否在安装系统时安装了其他占用 80 端口的软件。用下面的命令查询哪些进程占用了 80 端口。
lsof -i:80
:::align{center}
nginx 安装成功后访问 IP 地址(域名)出现的网页 :::
最后执行以下命令停止 nginx 服务。
systemctl stop nginx
MinIO
下载 MinIO,将 dl.min.io 改为 dl.minio.org.cn 可以更快的下载。
cd /opt
curl -O https://dl.min.io/server/minio/release/linux-amd64/minio
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
授权。
chmod +x ./minio
chmod +x ./mc
g++ & fmt & zstd & CMake
仅评测机需用。在评测机执行以下命令。
apt install g++ # Ubuntu 22.04 安装的是 g++-9
apt install libfmt-dev
apt install zstd # 解压用
apt install cmake # Ubuntu 22.04 安装的是 cmake 3.16.3
注意 cmake 的版本要为 3.0 及以上 3.31.11 及以下。
后端
克隆仓库并构建,然后创建配置文件。
cd /opt
git clone https://github.com/LibreOJ/backend.git
cd backend
yarn
cp config-example.yaml config.yaml
数据库
用 mariadb 进入数据库,创建 LibreOJ 数据库和数据库用户,使用随机字符串替换 <db-password> 并记下。
CREATE DATABASE `lyrio` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON `lyrio`.* TO "lyrio"@"localhost" IDENTIFIED BY "<db-password>";
exit;
MinIO
用以下命令启动 MinIO,使用随机字符串替换 <minio-user> 和 <minio-password> 并记下它们。可以使用 nohup 等工具让 MinIO 在后台运行,也可以新开一个终端。
MINIO_ROOT_USER=<minio-user> MINIO_ROOT_PASSWORD=<minio-password> ./minio server /mnt/data # /mnt/data 是数据目录
使用 mc 创建存储桶。
./mc alias set minio http://127.0.0.1:9000 "<minio-user>" "<minio-password>"
./mc mb -p minio/lyrio-files
配置文件
编辑配置文件 /opt/backend/config.yaml。
有尖括号的地方需要修改:
- 将
<session-secret>和<maintaince-key>替换为随机字符串并记下它们,不要泄露; - 将
<server-addr>替换为可以解析到服务器的 IP 地址或域名,不要加上http://或https://; - 将
<site-name>换为你的站点名字,这里以UniqueOJ为例。
注意 <server-addr> 千万不能设置为本地回环地址。
...
services:
database:
type: mariadb
host: 127.0.0.1
port: 3306
username: lyrio
password: <db-password>
database: lyrio
minio:
default:
endpoint: http://<server-addr>:9000
signEndpoint: null
forUser: null
forJudge: null
accessKey: <minio-user>
secretKey: <minio-password>
bucket: lyrio-files
...
security:
crossOrigin:
enabled: true
whiteList:
- http://127.0.0.1
- http://<server-addr>
sessionSecret: <session-secret>
maintainceKey: <maintaince-key>
...
preference:
siteName: <site-name>
...
requireEmailVerification 可以先改为 false 关闭邮箱验证,后续再配置。
启动
cd /opt/backend
LYRIO_CONFIG_FILE=./config.yaml yarn start
出现类似 [Nest] xxxx - xx/xx/xxxx, xx:xx:xx xx LOG [Bootstrap] @lyrio/lyrio is listening on 127.0.0.1:2002 代表成功。
用以下命令测试:
curl http://127.0.0.1:2002/api/auth/getSessionInfo?jsonp=1&token=
若返回响应且不是错误则成功。
前端
克隆仓库并构建。
cd /opt
git clone https://github.com/LibreOJ/frontend.git
cd frontend
yarn
启动 & 配置 nginx
用以下命令启动。
cd /opt/frontend
yarn start
备份 /etc/nginx/sites-enabled/default 并打开这个文件,删除所有内容并写入以下内容。
- 将
<back-port>替换为一个未被占用的端口号; - 将
<title>替换为网页的标题(但是好像并没有用),这里以UniqueOJ为例。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
server_name <server-addr>;
listen 80;
location / {
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Accept-Encoding "";
sub_filter '__default_title__' '"<title>"';
sub_filter '__api_endpoint__' '"http://<server-addr>:<back-port>"';
sub_filter_once on;
proxy_pass http://127.0.0.1:3000;
}
}
server {
server_name <server-addr>;
listen <back-port>;
location / {
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:2002;
}
}
启动 nginx:
systemctl start nginx
这时访问你的服务器 IP(域名)就应该可以看见 LibreOJ 的主页了,注意在开头加上 http://。
现在在右上角注册一个账号,如果关闭了邮箱验证则邮箱可以乱填,回到终端打开数据库。
mariadb
将 id=1 的用户设为管理员。
USE lyrio;
UPDATE user SET isAdmin=1 WHERE id=1; #
用以下命令检查。
SELECT * FROM user;
看见表上 id=1 一行上的 isAdmin=1 后回到主页,在下拉框中点击编辑资料,找到特权,将自己的特权全部打开,再提交,弹出特权修改成功则为成功。
:::align{center}
打开特权后的特权页面 :::
评测机
配置 Grub
沙箱的宿主机需要带参数启动。注意,若使用虚拟机,则是虚拟机带参数启动,不是主机带参数启动。编辑 /etc/default/grub,用
"quiet splash cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y"
替换 GRUB_CMDLINE_LINUX_DEFAULT 的值,然后更新配置并重启。
update-grub
reboot
沙箱
下载沙箱并解压,文件约 1.8GB。
cd /opt
mkdir rootfs
cd rootfs
curl -O https://github.com/LibreOJ/sandbox-rootfs/releases/download/alpha2/sandbox-rootfs-ng_alpha2.tar.zst
tar -xvf sandbox-rootfs-ng_alpha2.tar.zst
rm sandbox-rootfs-ng_alpha2.tar.zst
评测系统
克隆仓库并构建。
cd /opt
git clone https://github.com/LibreOJ/judge.git --recursive # 递归克隆,有 testlib
cd judge
export CXX=g++ # 指定 C++ 编译器
yarn
返回主页,点击最底下的评测机按钮,点击添加,填写一个名字,点击钥匙图标,将弹出的文本记下来,这是 key。
:::align{center}
评测机页面,第一次添加的的评测机应与图中评测机 crfandx 相同 :::
创建配置文件,并配置。
cp config-example.yaml config.yaml
配置文件的内容:
serverUrl: http://<server-addr>:<back-port> # 后端 IP 地址和端口号
downloadEndpointOverride: null
key: 40uXJPXzuO2Ha41iuh8Pjw1h0ahvP9i/zJk7Rtn/ # 获得的 key
dataStore: /root/judge/data # 数据存储目录,若不存在会自动创建
binaryCacheStore: /root/judge/cache # 存储编译后的二进制文件的目录
binaryCacheMaxSize: 536870912 # 二进制文件目录的最大大小
taskConsumingThreads: 2 # 同时最多执行的评测任务数
maxConcurrentDownloads: 10 # 同时最多执行的下载任务数
maxConcurrentTasks: 3 # 同时最多执行的任务数,编译、运行一组测试数据等都是任务
taskWorkingDirectories: # 同时进行的任务需要的不同的工作目录,数量应与 maxConcurrentTasks 的值相同,会自动创建
- /root/judge/1
- /root/judge/2
- /root/judge/3
rpcTimeout: 20000 # 与服务器进行 RPC 操作的最大执行时间
downloadTimeout: 20000 # 下载操作的最大执行时间
downloadRetry: 3 # 重试下载的最大次数
sandbox:
rootfs: /opt/rootfs # 沙箱的目录
user: sandbox
hostname: null
environments:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME: /sandbox
LC_ALL: en_US.UTF-8
cpuAffinity:
compiler: [0, 1]
userProgram: [1]
interactor: [0]
checker: [0, 1]
请酌情设置 maxConcurrentTasks 的值,不应该让这个值大于物理核心数,若前后端与评测系统都部署在一台服务器上,就更应注意这个值,避免将前后端卡死。
启动
使用以下命令启动。
LYRIO_JUDGE_CONFIG_FILE=./config.yaml yarn start
启动后根据警告进行调整配置文件。在保证评测机内存充裕时,将工作目录挂载到 tmpfs。
mount tmpfs /root/judge/1 -t tmpfs
mount tmpfs /root/judge/2 -t tmpfs
mount tmpfs /root/judge/3 -t tmpfs
返回评测机页面,刷新,若你的评测机在线,恭喜你,你的评测机连接到了后端,接下来用一道题来测试你的评测机吧!
:::align{center}
某一条 AC 的评测记录 :::
结语
:::align{center}
LibreOJ 主页,关闭了通知 :::
安装好后可以进行配置,一些安全性的方面请自行调整,例如应用一个普通用户运行 LibreOJ,而不是 root 用户。本文仅是讲述如何较快速的部署 LibreOJ。
恭喜你部署好了 LibreOJ!