Files
OJ2/docker/compose.debian.yml
T
xuyueandClaude Opus 5.5 793848b06f
Deploy / deploy (push) Canceled after 0s
refactor(登录): 登录态从 Cookie 换成 access token + refresh token
- access token 是 15 分钟的 JWT,走 Authorization: Bearer;refresh token 7 天,每次换都轮换
- 退出登录、改密码、禁用账号仍然立刻生效:每个请求核对 Redis 会话和库里新加的 user.token_version
- 在服务器后台改密码 / 禁用,机房那边登着的也会一起下线(原来两边 Redis 分开,管不到)
- WebSocket 握手借子协议带 token;JWT 密钥存在本站 Redis,部署不用加配置
- 去掉 COOKIE_SECURE;上线后所有人要重新登录一次

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:22:52 +08:00

198 lines
8.1 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.
# 服务器(oj.xuyue.cc)。数据库在这里,机房那套连的就是这里的 5445。
#
# docker/deploy.sh # 平时走这个(自检 → 构建 → 迁移 → 起栈 → 冒烟)
#
# 构建上下文是仓库根,所以 build.context 写 ..(相对本文件)。
#
# **服务器上的全部容器都由这一个文件起**,包括 postgres 和 redis。以前库和 redis 是旧栈
# 那份 /root/OJDeploy/docker-compose.yml 起的、这里通过 DB_HOST 外接过去,升级它们就得去
# 改那份文件;2026-10 起收了进来(搬家用 docker/move-db-into-oj2.sh,一次性的)。
#
# ⚠️ `DATA_DIR` 默认值 `../data` 解析出来是 **`OJ2/data`**,不是部署目录的 `data/`。
# 不设它的话 postgres 会在空目录上初始化一个全新的空库,测试点和题面图片也全都不在,
# 而且**不报错**。`deploy.sh` 为此专门设了自检。
services:
oj-postgres:
image: postgres:18-alpine
container_name: oj-postgres
restart: always
# 18 起镜像的数据目录是 /var/lib/postgresql/18/docker,挂载点要是上一级,
# 还挂 .../data 的话 18 直接拒绝启动。宿主机目录叫 `postgresql/`,和旧栈 16 的
# `postgres/` 分开:18 读不了 16 的目录,分开放才留得住老库做回退
volumes:
- ${DATA_DIR:-../data}/postgresql:/var/lib/postgresql
environment:
POSTGRES_DB: onlinejudge
POSTGRES_USER: onlinejudge
# 只在空目录上初始化时用一次,之后改它不会改库里的口令(要 ALTER ROLE)。
# 这个账号是超级用户、端口又对公网开着,必须是 openssl rand -hex 32 那种强口令
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?请在 docker/.env 里设置 POSTGRES_PASSWORD}
# 机房那套要连这个端口,必须对外暴露
ports:
- "5445:5432"
# 这台机器 2 核 7.5G、没有 swap,和 gitea、judge0 等二十多个别的服务挤在一起(2026-10 实测
# 可用 3.5G)。所以这里的取舍是「别被挤死」而不是「吃满内存」:
# - 全库 249MB,shared_buffers 512MB 能整库常驻,work_mem 16MB 让统计页的聚合排序不落盘;
# - oom_score_adj 压低:内存真满了,内核先杀别的,不先杀库;
# - cpu_shares 加倍:上课一个班同时交题时,查库和网页比判题、比别的服务先拿到 CPU。
# 这是按比例分、不是封顶,闲的时候判题照样用满两个核;
# - 两个超时防机房断网:事务开着不动超过 60 秒就断,锁不会一直挂着;
# TCP 保活把断掉的远端连接从默认两小时缩到两分钟左右才发现。
command:
- postgres
- -c
- shared_buffers=512MB
- -c
- effective_cache_size=2GB
- -c
- work_mem=16MB
- -c
- maintenance_work_mem=128MB
- -c
- idle_in_transaction_session_timeout=60s
- -c
- tcp_keepalives_idle=60
- -c
- tcp_keepalives_interval=10
- -c
- tcp_keepalives_count=6
# 并行查询的工作区走 /dev/shm,docker 默认只给 64MB,大一点的并行哈希会直接报错
shm_size: 256m
oom_score_adj: -500
cpu_shares: 2048
healthcheck:
test: ["CMD-SHELL", "pg_isready -U onlinejudge -d onlinejudge"]
interval: 5s
timeout: 3s
retries: 10
oj-redis:
image: redis:8-alpine
container_name: oj-redis
restart: always
# 会话存在这里:谁能连上它,谁就能写一条 session:<token> 冒充超管。所以必须带密码,
# 端口也只发布在本机(api / worker 走 compose 网络连 oj-redis,用不着宿主机端口)。
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?}"]
environment:
# redis-cli 认这个变量,healthcheck 和 docker exec 进来的 redis-cli 都不用再带 -a
REDISCLI_AUTH: ${REDIS_PASSWORD:?}
volumes:
- ${DATA_DIR:-../data}/redis:/data
ports:
- "127.0.0.1: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}
# 同一时刻最多六份在编译(正式判题 2、试运行 2、提交前编译检查 2),g++ 编一份 iostream
# 峰值约 70MB、bits/stdc++.h 约 195MB,再加学生程序本身。512m 不够撑满,超了是容器里
# 随机杀进程、学生看到系统错误
mem_limit: 1g
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:
oj-postgres:
condition: service_healthy
oj-redis:
condition: service_healthy
volumes:
- ${DATA_DIR:-../data}/backend:/data
environment: &api-env
DATABASE_URL: postgres://onlinejudge:${POSTGRES_PASSWORD:?}@oj-postgres:5432/onlinejudge
# 密码拼进 URL,所以只能用 URL 安全的字符 —— 用 openssl rand -hex 32 生成
REDIS_URL: redis://:${REDIS_PASSWORD:?请在 docker/.env 里设置 REDIS_PASSWORD}@oj-redis:6379
JUDGE_SERVER_URL: http://oj-judge:8080
JUDGE_SERVER_TOKEN: ${OJ2_JUDGE_TOKEN:?}
JUDGE_CONCURRENCY: ${JUDGE_CONCURRENCY:-2}
TRIAL_CONCURRENCY: ${TRIAL_CONCURRENCY:-2}
AI_KEY: ${AI_KEY:-}
AI_HINT_DIAGNOSE: ${AI_HINT_DIAGNOSE:-}
healthcheck:
test: ["CMD", "oj2-api", "healthcheck"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
mem_limit: 512m
# 理由同 oj-postgres:上课时学生翻页、看结果要先于判题拿到 CPU
cpu_shares: 2048
oj-worker:
build: *build
image: oj2-api:latest
container_name: oj-worker
restart: always
depends_on:
- oj-api
volumes:
- ${DATA_DIR:-../data}/backend:/data
environment: *api-env
# 同一个镜像,换个子命令就是判题消费者
command: ["oj2-api", "worker"]
# SQL 判题的子进程各允许 512MB 数据段(judge/sql 的 ulimit -d),两个并发就能顶满 512m
mem_limit: 1g
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