Files
OJ2/docker/compose.debian.yml
yuetsh 6356643cc9
Some checks failed
Deploy / deploy (push) Has been cancelled
ci(部署): 部署走 GitHub Actions,产物挪到 runner 上编
服务器性能差,镜像 builder 阶段(1238 个包的 bun install + bun compile +
vite build)首次约 5 分钟,光前端就 160s。改成在 runner 上编好 dist/oj2-api
和 apps/web/dist,rsync 过去,服务器那边只剩几条 COPY。

服务器自己编的能力没有砍掉。Dockerfile 顶部加了全局 ARG ARTIFACTS:

  build(默认)    builder 阶段自己编,只要有 docker 就能手动部署
  prebuilt         用构建上下文里现成的产物,builder 不进构建图

实现是两个 scratch 阶段归一成 /artifacts/ 布局,再用变量阶段名
FROM artifacts-${ARTIFACTS} 选一个,运行时阶段不关心产物是谁编的。

deploy.sh 加 --prebuilt:产物缺失在自检就 die;顺带检测 buildx —— 没装的话
compose 会退回 classic builder,那个不看依赖图、所有阶段挨个跑,产物白编
(结果正确但不省时间),黄字提醒不阻断。

.dockerignore 末尾放行两个产物路径,必须在 dist/ 那些规则之后,
dockerignore 是最后一条匹配说了算。

顺手删掉 apps/web/.github/ —— 从 ojnext 抄来的残留,路径不对(GitHub 只认
仓库根的 .github/workflows/)从来没跑过,内容也是 ojnext 的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 18:30:22 -06:00

187 lines
7.5 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 服务器xuyue.cc。数据库在这里机房那套连的就是这里的 5445。
#
# docker compose -f docker/compose.debian.yml --env-file docker/.env up -d --build
#
# 构建上下文是仓库根,所以 build.context 写 ..(相对本文件)。
#
# 和旧的 docker-compose.debian.yml 的对应关系:
# oj-backendsupervisord 跑 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:
image: registry.cn-hongkong.aliyuncs.com/oj-image/judge:1.6.1
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:-}
# 登录时把 Django 的 pbkdf2 哈希升级成 argon2 写回库。**默认关闭**
# 升级过的账号旧后端再也验不了,是一道单向门,回滚窗口内打开会锁死学生。
# 详见 apps/api/src/config.ts 里 passwordHashUpgrade 的注释。
PASSWORD_HASH_UPGRADE: ${PASSWORD_HASH_UPGRADE:-}
# 走 NPM 终止 TLS浏览器侧是 httpsCookie 必须带 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