# 服务器(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: 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,浏览器侧是 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