ZJUI-Learn 学校服务器正式部署指南(生产环境)¶
本文基于仓库内官方文档与当前代码配置整理,目标是把本地开发环境迁移到学校服务器并可长期稳定运行。
参考来源(仓库内):
docs/running-in-production/setup.mddocs/running-in-production/docker-compose.mddocs/running-in-production/authentication.mddocs/running-in-production/admin-user.mddocker-compose-production.ymlapps/prairielearn/src/lib/config.ts
1. 先明确部署形态¶
对学校场景,建议分两阶段:
- 阶段A(试运行):单机Docker Compose(快)
- 阶段B(正式):单机/小集群 + 域名 HTTPS + 认证 + 备份 + 监控(稳定)
官方文档明确说明:生产部署不自动覆盖高可用/备份,需要你自行建设运维体系。
2. 生产前置条件(学校服务器)¶
最低建议:
- Linux服务器(Ubuntu 22.04/24.04)
- 4C8G启动(并发高建议8C16G+)
- 系统盘 + 数据盘(课程仓库与数据分离)
- 公网域名,例如
pl.school.edu.cn - 80/443 对外,SSH 仅堡垒机或白名单
安装依赖:
sudo apt update
sudo apt install -y git curl ca-certificates gnupg lsb-release
安装 Docker + Compose 插件(官方方式)后验证:
docker --version
docker compose version
3. 拉取项目代码(建议用你的学校维护仓库)¶
sudo mkdir -p /opt/zjuilearn
sudo chown -R $USER:$USER /opt/zjuilearn
cd /opt/zjuilearn
git clone < 你的学校仓库地址 > ZJUI-Learn
cd ZJUI-Learn
4. 准备持久化目录(非常关键)¶
docker-compose-production.yml默认只声明了postgres体积与/jobs映射。正式使用请显式规划持久化路径:
sudo mkdir -p /srv/zjuilearn/{courses,jobs,logs,ssh,config,backup}
sudo chown -R $USER:$USER /srv/zjuilearn
chmod 700 /srv/zjuilearn/ssh
5. 生产配置 config.json(必须)¶
官方支持在仓库根目录放config.json(或启动时--config 指定路径)。
在/srv/zjuilearn/config/config.json创建(先最小可用):
{
"serverCanonicalHost": "https://pl.school.edu.cn",
"cookieDomain": ".school.edu.cn",
"trustProxy": true,
"coursesRoot": "/srv/pl-courses",
"postgresqlHost": "localhost",
"postgresqlDatabase": "postgres",
"postgresqlUser": "postgres",
"postgresqlPassword": null,
"redisUrl": "redis://localhost:6379/",
"secretKey": "REPLACE_WITH_LONG_RANDOM_SECRET",
"databaseEncryptionKey": "REPLACE_WITH_64_HEX_CHARS",
"hasOauth": false
}
生成安全随机值示例:
openssl rand -hex 32 # 用于 databaseEncryptionKey(64位hex)
openssl rand -hex 48 # 可用于 secretKey
注意(来自代码校验逻辑):
NODE_ENV=production下cookieDomain必填,且必须以.开头databaseEncryptionKey不能使用默认全 0 值
以上要求可在apps/prairielearn/src/lib/config.ts中看到。
6. 认证配置(正式上线必做)¶
官方文档给了两类主流方案:
- 谷歌OAuth2(
docs/running-in-production/authentication.md) - LTI(活动LMS)
6.1 采用 Google OAuth2(示例)¶
在 Google Cloud 创建 OAuth Web 应用后,把以下配置写入 config.json:
{
"hasOauth": true,
"googleClientId": "xxx.apps.googleusercontent.com",
"googleClientSecret": "xxx",
"googleRedirectUrl": "https://pl.school.edu.cn/pl/oauth2callback"
}
7.覆盖制作(建议新建覆盖文件)¶
在仓库根目录新建docker-compose.production.override.yml:
services:
pl:
container_name: pl
image: prairielearn/prairielearn:latest
restart: unless-stopped
ports:
- '127.0.0.1:3000:3000'
environment:
- NODE_ENV=production
- HOST_JOBS_DIR=/srv/zjuilearn/jobs
volumes:
- postgres:/var/postgres
- /var/run/docker.sock:/var/run/docker.sock
- /srv/zjuilearn/jobs:/jobs
- /srv/zjuilearn/config/config.json:/PrairieLearn/config.json:ro
- /srv/zjuilearn/courses:/srv/pl-courses
- /srv/zjuilearn/ssh:/root/.ssh
- /srv/zjuilearn/logs:/PrairieLearn/logs
extra_hosts:
- 'host.docker.internal:172.17.0.1'
volumes:
postgres:
说明:
- 将3000绑定本机回环,外网经Nginx反代
- 持久化课程目录,避免容器重建导致课程丢失
- 挂载SSH密钥,支持课程Git同步
8. 首次启动与健康检查¶
cd /opt/zjuilearn/PrairieLearn
docker compose -f docker-compose-production.yml -f docker-compose.production.override.yml up -d
docker ps
curl -I http://127.0.0.1:3000/pl/webhooks/ping
返回 200 表示核心服务健康(官方推荐的健康端点)。
9.Nginx反向代理+HTTPS(推荐)¶
安装Nginx和证书工具(Let's Encrypt):
sudo apt install -y nginx certbot python3-certbot-nginx
Nginx 站点示例 /etc/nginx/sites-available/zjuilearn.conf:
server {
listen 80;
server_name pl.school.edu.cn;
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
}
}
启用并校验:
sudo ln -s /etc/nginx/sites-available/zjuilearn.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
签发证书:
sudo certbot --nginx -d pl.school.edu.cn
获取HTTPS后,请确保config.json中:
serverCanonicalHost是https://pl.school.edu.cntrustProxy为true
10. 创建管理员账号(官方流程)¶
- 先用认证登录一次系统(让用户进入
users表) - 进入容器:
docker exec -it pl /bin/bash
psql postgres
- 查询用户:
SELECT
*
FROM
users;
- 将目标
user_id提升为管理员:
INSERT INTO
administrators (user_id)
VALUES
(< user_id >);
该流程来自docs/running-in-production/admin-user.md。
11.课程仓库接入(Git同步)¶
生产环境通常按官方建议使用 Git 维护课程:
- 准备SSH密钥(建议专用密钥)
- 加入Git平台(GitHub/Gitee/GitLab)仓库部署密钥
- ZJUI-Learn 管理页面添加课程(Admin -> Courses)
- 课程路径填挂载目录下路径(如
/srv/pl-courses/mycourse) - 点击
Sync->Pull from remote git repository
为避免首次连接主机密钥报错,可在config.json设置:
{
"gitSshCommand": "ssh -o StrictHostKeyChecking=accept-new"
}
12. 上线验收清单(建议逐项打勾)¶
-
https://pl.school.edu.cn/pl/webhooks/ping返回 200 - 登录流程可用(OAuth/LTI)
- 管理员权限正常
- 课程可同步、拉取成功
- 题目预览/提交/评分链路正常
- 文件上传功能可用
- 服务重启后课程与数据仍在(持久化验证)
13. 运维必做项(正式投入使用)¶
13.1 自动拉起¶
建议以systemd托管撰写:
/etc/systemd/system/zjuilearn.service
[Unit]
Description=ZJUI-Learn ZJUI-Learn Service
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
WorkingDirectory=/opt/zjuilearn/PrairieLearn
RemainAfterExit=true
ExecStart=/usr/bin/docker compose -f docker-compose-production.yml -f docker-compose.production.override.yml up -d
ExecStop=/usr/bin/docker compose -f docker-compose-production.yml -f docker-compose.production.override.yml down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
启用:
sudo systemctl daemon-reload
sudo systemctl enable --now zjuilearn
13.2 备份策略(至少)¶
- 数据库卷备份(
postgres卷) - 课程目录备份(
/srv/zjuilearn/courses) - 配置与密钥备份(
/srv/zjuilearn/config,/srv/zjuilearn/ssh) - 日备份 + 异地副本 + 恢复演练
13.3 监控告警(至少)¶
- 设备、CPU、内存、磁盘
/pl/webhooks/pingHTTP 标记- Nginx 5xx 同样
- 磁盘剩余空间告警(课程仓库和日志增长)
14. 升级流程(推荐停机升级)¶
官方推荐:停服务 -> 迁移 -> 启服务。
cd /opt/zjuilearn/PrairieLearn
git fetch --all
git checkout <目标版本或分支>
git pull
docker compose -f docker-compose-production.yml -f docker-compose.production.override.yml down
docker compose -f docker-compose-production.yml -f docker-compose.production.override.yml pull
docker compose -f docker-compose-production.yml -f docker-compose.production.override.yml up -d
升级后执行验收清单第 12 节。
15. 你可以直接照抄的最短上线路径(单机)¶
- 准备好服务器+Docker+域名
- 克隆仓库到
/opt/zjuilearn/ZJUI-Learn - 创建
/srv/zjuilearn/{config,courses,jobs,ssh,logs} - 写入
config.json(至少配置域名、cookieDomain、secret、databaseEncryptionKey) - 写
docker-compose.production.override.yml docker compose ... up -d- 配置Nginx + HTTPS
- 登录并提升管理员
- 接入课程Git仓库并同步
- 完成验收后对外发布