Deploy / deploy (push) Waiting to run
上游 QingdaoU/JudgeServer 停更在 2024-04(registry 上的 latest 和 1.6.1 是同一份 镜像,编译器停在 gcc-13),没有新版可拉,所以自己重编。docker/judge/ 是只改工具链 的 Dockerfile 分叉,server/ 和 Judger/ 从上游固定 commit b28aa56 拉,一行没动。 镜像 oj2-judge-2(不在任何 registry 上:本机 build.sh --save → scp → docker load) - gcc/g++ 13 → 14.2,Python 3.12 → 3.13.5,都是 trixie 默认 - Go / JDK / Node 整套删掉:前端的题目语言复选框从来只给 C / C++ / Python / SQL, 12 万条提交里 Java 44 条、Golang 15、JavaScript 3,全是很早以前的 - 体积 1.1GB → 433MB;默认走清华源,构建 12 分钟 → 40 秒(--no-mirror 换回官方) - deploy.sh 加一道自检:镜像不在本机就中止,并打印该跑的三条命令 C 的编译参数加三个 -Wno-error(implicit-function-declaration / int-conversion / incompatible-pointer-types):gcc-14 把它们从 warning 提成了 error,而 -w 压不住。 语言值统一成 Python(迁移 0019 / 0020) - 0019:Python3(104527 条提交)与 Python2(3 条)并成 Python,一并改掉 937 道题的 languages、75 个 template 键、15 个 ast_rules 键、257 条 answers、1235 个用户的 成就指标 _languages(languages_used 重算,总和 1928 → 1925,少的 3 个是同时用过 两种 Python 的人) - 0020:把 Java / JavaScript / Golang 从 84 道题的可选语言里摘掉 —— 不摘的话那些题 的语言下拉还能选 Java,提交必 SYSTEM_ERROR - 契约新增 normalizeLanguage() 别名表,判题侧一律走 judgeConfigFor():旧客户端 localStorage 里的 Python3、迁移前排进队列的任务都还能判;协作的语言归一也走它, 否则上线那一刻学生页面里的 Python3 会静默落到 C - 回滚要连数据一起回,只滚代码会让所有 Python 提交变 SYSTEM_ERROR 实跑 - 判题冒烟 docker/judge/smoke.ts 13 条全过:三种语言、六种状态码、gcc 宽松度 - 拿备份里的真实代码逐文件比对新旧镜像的编译结果,0 差异:C 提交 1951 份 (1725 过 / 226 CE)、C++ 882 份、Python 2000 份、20 篇 C 教程的 93 个代码块。 不加那三个 -Wno-error 的话,C 有 26 份会从能过变成 CE - 迁移在灌了 12.4 万行真实数据的一次性库里跑过:0 残留、没有题目被清空; dev 库用真正的执行器跑通 - check:ast 56 个 target 全过,前后端 typecheck 均 0 顺带记下一个升级之前就有的坑(现在随 Go 一起消失,写在 README 里):GOCACHE 指向 容器的 tmpfs,判题机重启后第一次 Go 提交是冷构建,Go 1.22 要 5.6 秒 CPU、超过 3 秒 的编译预算,于是重启后第一个交 Go 的学生必吃一次 CE,后面的人缓存热了又都正常。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
188 lines
7.5 KiB
YAML
188 lines
7.5 KiB
YAML
# 服务器(xuyue.cc)。数据库在这里,机房那套连的就是这里的 5445。
|
||
#
|
||
# docker compose -f docker/compose.debian.yml --env-file docker/.env up -d --build
|
||
#
|
||
# 构建上下文是仓库根,所以 build.context 写 ..(相对本文件)。
|
||
#
|
||
# 和旧的 docker-compose.debian.yml 的对应关系:
|
||
# oj-backend(supervisord 跑 caddy+gunicorn+dramatiq)→ 拆成 oj-web + oj-api + oj-worker
|
||
# 端口、数据目录、判题机配置全部保持不变,这样回滚只是换回旧 compose。
|
||
#
|
||
# ## 两种形态
|
||
#
|
||
# **自带数据**(演练用的就是这个):postgres 和 redis 也由本文件起,要显式加 profile。
|
||
#
|
||
# docker compose -f docker/compose.debian.yml --env-file docker/.env --profile local-data up -d
|
||
#
|
||
# **只换前后端**(默认,上线用这个):旧栈的 postgres / redis 容器继续跑,
|
||
# 这里只起 api / worker / web / judge,
|
||
# 通过 env 指过去。切换当天数据库进程根本不重启,库和文件都不用挪位置。
|
||
#
|
||
# docker compose -f docker/compose.debian.yml --env-file docker/.env up -d
|
||
#
|
||
# 后者要在 env 里设 `DB_HOST` / `REDIS_HOST` / `DATA_DIR`,见 `.env.example` 末尾。
|
||
#
|
||
# **并行试跑**(新站挂在 oj2.xuyue.cc、旧站原样不动)是「只换前后端」再加两个变量:
|
||
# `WEB_PORT`(8080 被旧 backend 占着)和 `JUDGE_STATE_DIR`(两个判题机不能共用运行目录)。
|
||
# 这种形态下旧栈一个容器都不用停。见手册第四节。
|
||
#
|
||
# ⚠️ `DATA_DIR` 默认值 `../data` 解析出来是 **`OJ2/data`**,不是部署目录的 `data/`。
|
||
# 旧栈用的是 `<部署目录>/data/`,两者不是一个地方 —— 直接用默认值切过去,postgres 会在
|
||
# 空目录上初始化一个全新的空库,测试点和题面图片也全都不在。**用旧数据就必须设 `DATA_DIR`。**
|
||
|
||
services:
|
||
oj-postgres:
|
||
image: postgres:16-alpine
|
||
container_name: oj-postgres
|
||
restart: always
|
||
# 只在「自带数据」形态下启动。用旧栈的库时不启动它 —— 否则 5445 端口会和
|
||
# 旧的 postgres 撞,而且两个进程开同一个数据目录本来也起不来。
|
||
profiles: ["local-data"]
|
||
volumes:
|
||
- ${DATA_DIR:-../data}/postgres:/var/lib/postgresql/data
|
||
environment:
|
||
POSTGRES_DB: onlinejudge
|
||
POSTGRES_USER: onlinejudge
|
||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?请在 docker/.env 里设置 POSTGRES_PASSWORD}
|
||
# 机房那套要连这个端口,必须对外暴露
|
||
ports:
|
||
- "5445:5432"
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "pg_isready -U onlinejudge -d onlinejudge"]
|
||
interval: 5s
|
||
timeout: 3s
|
||
retries: 10
|
||
|
||
oj-redis:
|
||
image: redis:7-alpine
|
||
container_name: oj-redis
|
||
restart: always
|
||
# 同上:旧栈的 redis 也发布了 5446,两个一起跑会撞端口。
|
||
# redis 里没有非丢不可的东西(会话、判题队列),用旧的那个也无所谓 ——
|
||
# 旧后端已经停了,新后端独占它,剩下的 Django / dramatiq 残留键前缀不同,互不干扰。
|
||
profiles: ["local-data"]
|
||
volumes:
|
||
- ${DATA_DIR:-../data}/redis:/data
|
||
ports:
|
||
- "5446:6379"
|
||
healthcheck:
|
||
test: ["CMD", "redis-cli", "ping"]
|
||
interval: 5s
|
||
timeout: 3s
|
||
retries: 10
|
||
|
||
oj-judge:
|
||
# 自己构建的判题沙箱(上游 JudgeServer 1.6.1 + 新工具链,见 docker/judge/)。
|
||
# 上游停更在 2024-04,registry 上的 1.6.1 == latest,没有新版可拉。
|
||
# ⚠️ 这个 tag 不在任何 registry 上:本机 docker/judge/build.sh 构建,
|
||
# 服务器/机房用 docker load 装。镜像不在本地时 compose 会去 pull 然后报找不到。
|
||
image: oj2-judge-2
|
||
container_name: oj-judge
|
||
restart: always
|
||
read_only: true
|
||
cap_drop:
|
||
- SETPCAP
|
||
- MKNOD
|
||
- NET_BIND_SERVICE
|
||
- SYS_CHROOT
|
||
- SETFCAP
|
||
- FSETID
|
||
tmpfs:
|
||
- /tmp
|
||
volumes:
|
||
- ${DATA_DIR:-../data}/backend/test_case:/test_case:ro
|
||
# 判题机的运行状态目录。默认跟着 DATA_DIR 走,和旧栈是同一份 ——
|
||
# 一次性切换时无所谓(旧判题机已经停了),但**并行试跑时必须单独设 JUDGE_STATE_DIR**,
|
||
# 否则新旧两个 judger 同时往一个 run/log 目录里写。
|
||
- ${JUDGE_STATE_DIR:-${DATA_DIR:-../data}/judge_server}/log:/log
|
||
- ${JUDGE_STATE_DIR:-${DATA_DIR:-../data}/judge_server}/run:/judger
|
||
environment:
|
||
SERVICE_URL: http://oj-judge:8080
|
||
# 心跳路径跟着新后端改了:judge_server_heartbeat/ → judge-server/heartbeat
|
||
BACKEND_URL: http://oj-api:3000/api/judge-server/heartbeat
|
||
TOKEN: ${OJ2_JUDGE_TOKEN:?请在 docker/.env 里设置 OJ2_JUDGE_TOKEN}
|
||
mem_limit: 512m
|
||
|
||
oj-api:
|
||
build: &build
|
||
context: ..
|
||
dockerfile: docker/Dockerfile
|
||
target: api
|
||
# 产物来源,见 docker/Dockerfile 顶部。默认 build(服务器自己编);
|
||
# CI 传 ARTIFACTS=prebuilt 用 runner 编好的产物。
|
||
args:
|
||
ARTIFACTS: ${ARTIFACTS:-build}
|
||
image: oj2-api:latest
|
||
container_name: oj-api
|
||
restart: always
|
||
depends_on:
|
||
# required: false —— 「只换前后端」时这两个服务不在启动集合里(profile 未启用),
|
||
# 严格的 depends_on 会让整个 project 直接判定 invalid(实测过,不是猜的)。
|
||
# 代价:自带数据形态下 postgres 起不来时,compose 只警告不中止,oj-api 照样起,
|
||
# 然后自己 crash 循环。看 `docker compose ps`,oj-api 会是 unhealthy。
|
||
oj-postgres:
|
||
condition: service_healthy
|
||
required: false
|
||
oj-redis:
|
||
condition: service_healthy
|
||
required: false
|
||
# 用旧栈的库时,库在宿主机上(旧 postgres 发布了 5445),走 host-gateway 过去,
|
||
# 不出本机、不走公网。DB_HOST 填 host.docker.internal 即可。
|
||
extra_hosts:
|
||
- "host.docker.internal:host-gateway"
|
||
volumes:
|
||
- ${DATA_DIR:-../data}/backend:/data
|
||
environment: &api-env
|
||
DATABASE_URL: postgres://onlinejudge:${POSTGRES_PASSWORD:?}@${DB_HOST:-oj-postgres}:${DB_PORT:-5432}/onlinejudge
|
||
REDIS_URL: redis://${REDIS_HOST:-oj-redis}:${REDIS_PORT:-6379}
|
||
JUDGE_SERVER_URL: http://oj-judge:8080
|
||
JUDGE_SERVER_TOKEN: ${OJ2_JUDGE_TOKEN:?}
|
||
JUDGE_CONCURRENCY: ${JUDGE_CONCURRENCY:-2}
|
||
AI_KEY: ${AI_KEY:-}
|
||
AI_HINT_DIAGNOSE: ${AI_HINT_DIAGNOSE:-}
|
||
# 走 NPM 终止 TLS,浏览器侧是 https,Cookie 必须带 Secure
|
||
COOKIE_SECURE: "true"
|
||
healthcheck:
|
||
test: ["CMD", "oj2-api", "healthcheck"]
|
||
interval: 30s
|
||
timeout: 3s
|
||
retries: 3
|
||
start_period: 10s
|
||
mem_limit: 512m
|
||
|
||
oj-worker:
|
||
build: *build
|
||
image: oj2-api:latest
|
||
container_name: oj-worker
|
||
restart: always
|
||
depends_on:
|
||
- oj-api
|
||
extra_hosts:
|
||
- "host.docker.internal:host-gateway"
|
||
volumes:
|
||
- ${DATA_DIR:-../data}/backend:/data
|
||
environment: *api-env
|
||
# 同一个镜像,换个子命令就是判题消费者
|
||
command: ["oj2-api", "worker"]
|
||
mem_limit: 512m
|
||
|
||
oj-web:
|
||
build:
|
||
context: ..
|
||
dockerfile: docker/Dockerfile
|
||
target: web
|
||
args:
|
||
ARTIFACTS: ${ARTIFACTS:-build}
|
||
image: oj2-web:latest
|
||
container_name: oj-web
|
||
restart: always
|
||
depends_on:
|
||
- oj-api
|
||
volumes:
|
||
# Caddy 只往这里写访问日志,静态资源在镜像里
|
||
- ${DATA_DIR:-../data}/backend/log:/data/log
|
||
ports:
|
||
# 并行试跑(oj2.xuyue.cc)时换一个端口,8080 还被旧 backend 占着。
|
||
- "0.0.0.0:${WEB_PORT:-8080}:8000"
|
||
mem_limit: 256m
|