Deploy / deploy (push) Canceled after 0s
compose.debian.yml 去掉外接形态,postgres / redis 固定由本栈起(PostgreSQL 18、Redis 8)。 docker/move-db-into-oj2.sh 把服务器上旧栈的 16 / 7 倒进来:先编镜像、确认机房已断开、 备份、恢复时剥掉 dump 里的 md5 口令(18 只认 SCRAM)、核对行数和网络登录,最后交给 deploy.sh。 生产库口令原来是默认的 onlinejudge(超级用户、5445 对公网开着),脚本要求换成强口令才执行。 本机按线上「之前」的状态完整演练过:停机到恢复 36 秒,行数一致,登录态保住,提交照常判题。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
164 lines
6.4 KiB
YAML
164 lines
6.4 KiB
YAML
# 服务器(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"
|
||
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}
|
||
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:
|
||
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:-}
|
||
# 走 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
|
||
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
|