本文介绍 Patpat Online 的部署环境,即本仓库的目录结构。
开始前,确保你完成了服务器配置。
Patpat Online 部署环境的目录结构如下。
. # 部署根目录
|-- app/ # 前端部署目录
|-- server/ # 后端部署目录
|-- volume/ # 后端挂载目录
|-- config/ # 配置目录(除了后端的 Deployment)
\-- scripts/ # 脚本工具
前端项目部署于 app/
目录下,包括:
patpat/
:Patpat Online 平台前端,部署时生成。tutorial/
:Patpat Online 平台教程博客,部署时生成,作为平台的iframe
。welcome/
:一个欢迎页~~(占位用的)~~,可用于监测前后端状态。game/
:休闲小游戏,目前是 2048,作为平台的iframe
。
前端部署目录的映射关系如下,具体可参考 config/patpat.conf
,注意 URL 结尾的 /
,不加 /
会被映射为平台的路由,而不是对应的网页。
/patpat -- /
/tutorial -- /tutorial/
/welcome -- /welcome/
/game -- /game/
app/deploy.sh
为前端的部署脚本,使用方式如下:
- 将前端要部署的目录上传至
app/
目录下,注意不要和部署目录重名。对于 Vue 项目为dist/
,Hexo 博客为public
。以下用{src}
指代该目录。 - 确定要部署的目录,如平台为
patpat
,教程为tutorial
。以下用{dest}
指代该目录。 - 在
app/
目录下,执行./deploy.sh {src} {dest}
进行部署,部署后会自动删掉{src}
。
每次部署时,都会将部署日志输出至 app/deploy.log
。
后端项目使用 Docker + K3s 部署,后端文件位于 server/
目录下。后端包括平台后端 PatBoot 和评测机 PatJudge,PatBoot 部署目录为 server/boot/
,PatJudge 为 server/judge/
,二者部署方式相同,下面以 PatBoot 为例说明。
部署时,需要将项目的 Dockerfile 以及打包好的 JAR 包上传至部署目录,上传好的结构如下。
server/
|-- boot/
| |-- target
| | \-- PatBoot-0.1.0.jar # 需要上传
| |-- Dockerfile # 需要上传
| |-- boot.template.yaml
| |-- deploy.sh
| |-- ...
|-- ...
打包 JAR 包使用 maven package
,可于 IDEA 的 Maven Lifecycle 中自动执行。务必上传名称完整的 JAR 包(包含版本号),镜像打包时依赖该版本号。target/
下可以存在多个 JAR 包,部署时自动选择版本号最高的 JAR 包。
上传好后,在 server/boot/
目录下执行 deploy.sh
即可完成从镜像打包到 K3s 部署 Deployment 的完整流程。Deployment 模板中有两个参数,{VERSION}
为部署镜像的版本,{BASE}
为部署目录的父目录(即包含 README.md 的目录),二者都可以由脚本自动获取。脚本会根据 boot.template.yaml
自动生成最新版本的部署配置文件 boot.yaml
,并将当前版本输出至文件 version
中。
注意,此处的部署只是更新 K3s 的 Deployment,具体向外暴露服务需要通过对应脚本完成,参见部署脚本部分。
部署后,volume
下的各个目录会被挂载到容器中,具体含义如下。
/log/
: 日志目录。/bucket/
: 私有文件目录(无法通过 URL 直接访问)${buaa-id}/
: 存放每个用户产生的文件。problem/${problem-id}/
: 编程题目录,为解压后的题目配置文件。lab/${lab-id}/${buaa-id}-${name}
: Lab 报告提交目录。iter/${iter-id}/${buaa-id}-${name}
: 迭代提交目录。proj/${course-id}/${group-id}
: 小组大作业提交目录。submission/${problem-id}/${buaa-id}
: 题目提交目录。temp/
: 临时目录,理论上为空,服务器会自动删除文件。
/wwwroot/
: 公有文件目录(可以直接通过 URL 访问。${buaa-id}/
: 用户上传的公开文件,如头像。course/${course-id}/
: 课程资料目录。
首次启动时,需要确保如下目录存在。由于题目和提交目录直接挂载在 PatJudge 里,所以需要提前创建,其他目录由后端自动创建。
volume/log/
volume/wwwroot/
volume/bucket/problem/
volume/bucket/submission/
config/
目录包含了 Nginx 和 K3s 的相关配置文件(除了后端的 Deployment 配置)的模板,具体部署时,对于每个 *.template.*
,首先复制一份去除 .template.
的正式配置文件,并更改相关内容,更改说明如下。
如果使用校园网内服务器,则公网 IP 和内网 IP 相同。
对于 Nginx 部署文件,首先在 config/
目录下运行 config/init.sh
,初始化目录映射,然后更改 patpat.conf
中的 {public ip}
为公网 IP。
对于 Service 配置,更改 service.yaml
中的 {public ip}
和 {private ip}
。注意 Service 里的 nodePort
要和 patpat.conf
里一致。
对于 ConfigMap
的配置,更改 env.yaml
中的环境变量。注意这里 MySQL 和 RabbitMQ 的 HOST
不能是 localhost 或 127.0.0.1,否则无法在容器中连接。生产环境 PROFILE
选择 prod
,测试选择 stag
。JWT_SECRET
为任意 32 位字符串即可。DOMAIN
为 Cookies 的 Domain,虽然应用不一定使用该值,但是需要设置。
使用下一节的部署脚本进行管理。
在 scripts/
目录下包含若干部署脚本,方便部署操作,下面简单介绍,具体参数见对应的脚本文件的 Usage。
deployment.sh
:管理 K3s Deployment。service.sh
:管理 K3s Service。env.sh
:管理 K3s 的 ConfigMap。monitor.sh
:监控 K3s 状态。log.sh
:监控后端日志。nginx.sh
:更新 Nginx 配置。deploy.sh
:调用后端部署脚本(server/deploy.sh
)执行完整的部署操作。image.sh
:镜像推送和拉取(需要配置自己的 registry,推荐阿里云)。
在部署目录根目录也有 manage.sh
用来快捷地调用这些脚本。
这里需要说明的是,很多脚本有 apply
和 reload
选项。前者在当前基础上进行更新,但当对应 YAML 没有发生改变时(通常是镜像更新了,但版本号没更新)不起作用。后者首先删除当前 YAML 再 apply,从而强制更新配置。
这节介绍在服务器配置完成的情况下,如何部署项目以及更新部署。
1. 初始化配置
根据上一节内容,初始化 config/
目录下的所有配置。
2. 配置环境变量
./manage.sh env apply
3. 启动后端服务
根据后端部署一节中的内容,准备好 PatBoot 和 PatJudge 的 JAR 包和 Dockerfile,然后执行如下命令。
./manage.sh deploy all
该命令会从构建镜像开始,耗时比较长,完成后后端 Deployment 就上线了,接下来启动 Service。
./manage.sh service apply
此时,可以通过 monitor 查看 K3s 状态。
./manage.sh monitor all
4. 部署前端服务
根据前端部署一节中的内容,部署 patpat/
和 tutorial/
。
5. 启用 Nginx 代理
最后,执行如下命令进行反向代理。
./manage.sh nginx reload
现在访问公网 IP 即可看到 Patpat Online 主页。
前端更新只需要重复前端部署一节的内容即可。
后端如果只是后端应用更新,同样重复后端部署一节中的内容,然后直接执行部署目录中的 deploy.sh
,或是使用 ./manage.sh
重新部署对应项目即可。
如果环境变量更新,在更新环境变量的同时,也需要重启后端服务让环境变量生效,具体命令如下。
./manage.sh env apply
./manage.sh deployment reload # 强制更新 Deployment
临时扩容/缩容时,直接更新 boot.yaml
或 judge.yaml
,更改其中的 replica
,然后执行如下命令更新 Deployment。
./manage.sh deployment apply
如果想在下一次部署时保留更改,则同时更新对应的 template 即可。
注意:PatBoot 不能扩容,只能有 1 个实例,因为 WebSocket 会与实例绑定! 注意:PatBoot 不能扩容,只能有 1 个实例,因为 WebSocket 会与实例绑定! 注意:PatBoot 不能扩容,只能有 1 个实例,因为 WebSocket 会与实例绑定!