Files
OJ2/docker/compose.debian.yml
yuetsh ea521e7b0a feat(阶段5): 镜像、三套 compose,前端切回 /api 与 /ws
## 镜像

一个 Dockerfile 两个 target:api(单二进制)和 web(Caddy + 前端产物)。
旧后端是一个容器里用 supervisord 跑 caddy+gunicorn+dramatiq,这里拆成
oj-web / oj-api / oj-worker 三个容器 —— Docker 本身就是进程管理器,
拆开之后 worker 挂了能单独重启、日志也分得开,少一层 supervisord 要维护。

同一个镜像换个子命令就是 worker,镜像里只有一份运行时。
新增 healthcheck 子命令:运行镜像是 debian-slim,没有 curl/wget,
让二进制自己打 /health(只打 /health 不碰库 —— 库挂了该由库的 healthcheck 报,
不该让 api 跟着被判不健康然后被重启)。

**数据目录照抄旧后端**(test_case、public/upload、public/avatar)。
不是审美问题:切换那天不用搬动任何文件,回滚时旧后端立刻能找到自己的数据。
少一次几十 GB 的 mv,就少一个在停机窗口里出错的机会。

构建路上踩到三个坑,都是「本地能过、容器里过不了」那一类:

- 构建上下文吸进了 data/,judge_server/run 是判题沙箱用别的 uid 建的,
  docker 连 stat 都做不了,构建直接失败 → 补 .dockerignore
- mermaid@9.4.3(机房老 Chrome 的 legacy 依赖,不能砍)从容器里连
  registry.npmjs.com 稳定失败,主机上没问题 → 换 npmmirror,并重试两次
- 容器里 bun 用 isolated 布局,本地是扁平的。靠「提升」才能解析到的包
  在容器里一律解析不到:@node-rs/jieba-linux-x64-gnu(编译要 import 它的 .node)、
  以及前端的 @codemirror/{language,state,view} 和 @lezer/highlight。
  这些本来就是代码直接 import 的,补成直接依赖。顺手写了个脚本扫全仓,
  确认只有这 4 个。

## 前端切回 /api、/ws

迁移期用 /api2、/ws2 指新后端,/api、/ws 还指着 Django。端点已全部搬完,
临时前缀去掉。改动只有三处(api2.ts 的 baseURL、websocket.ts 的两个 URL),
`api2` 那些 import 是模块名不是路径,不动。vite 代理同步收敛成三条。

## compose

- debian:全套(含 postgres,对外开 5445 给机房连)
- school:**没有 postgres**,连服务器的库;本地 Redis + 本地判题沙箱
- 两边共用一个库但各有各的队列和 WS 推送,和旧后端的 Dramatiq/Channels 拓扑一致

密钥一律走 env 且带 `:?`,没设置就直接报错退出,不静默用弱默认值。
COOKIE_SECURE 在机房必须是 false(http 直连 IP,带 Secure 的 Cookie 发不回来,
表现是「登录成功但立刻又变未登录」)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 23:41:20 -06:00

132 lines
3.7 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。
services:
oj-postgres:
image: postgres:16-alpine
container_name: oj-postgres
restart: always
volumes:
- ./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
volumes:
- ./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/backend/test_case:/test_case:ro
- ./data/judge_server/log:/log
- ./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
image: oj2-api:latest
container_name: oj-api
restart: always
depends_on:
oj-postgres:
condition: service_healthy
oj-redis:
condition: service_healthy
volumes:
- ./data/backend:/data
environment: &api-env
DATABASE_URL: postgres://onlinejudge:${POSTGRES_PASSWORD:?}@oj-postgres:5432/onlinejudge
REDIS_URL: redis://oj-redis:6379
JUDGE_SERVER_URL: http://oj-judge:8080
JUDGE_SERVER_TOKEN: ${OJ2_JUDGE_TOKEN:?}
JUDGE_CONCURRENCY: ${JUDGE_CONCURRENCY:-2}
AI_KEY: ${AI_KEY:-}
# 走 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
volumes:
- ./data/backend:/data
environment: *api-env
# 同一个镜像,换个子命令就是判题消费者
command: ["oj2-api", "worker"]
mem_limit: 512m
oj-web:
build:
context: ..
dockerfile: docker/Dockerfile
target: web
image: oj2-web:latest
container_name: oj-web
restart: always
depends_on:
- oj-api
volumes:
# Caddy 只往这里写访问日志,静态资源在镜像里
- ./data/backend/log:/data/log
ports:
- "0.0.0.0:8080:8000"
mem_limit: 256m