From 2f67e9e2b9f93deacb1e514cce7b2fa97c638ade Mon Sep 17 00:00:00 2001 From: Nexus Deploy Date: Fri, 5 Jun 2026 22:40:04 +0800 Subject: [PATCH] docs: add 1Panel Docker production deploy stack Add Dockerfile.prod, docker-compose.prod.yml, OpenResty example, and migration plan for Baota to 1Panel + Compose deployment. --- .gitignore | 2 + Dockerfile.prod | 44 +++ deploy/1panel/openresty-nexus.conf.example | 70 ++++ docker/.env.prod.example | 17 + docker/README.md | 11 + docker/docker-compose.prod.yml | 93 +++++ ...026-06-04-1panel-docker-production-plan.md | 37 ++ .../2026-06-04-1panel-docker-production.md | 334 ++++++++++++++++++ 8 files changed, 608 insertions(+) create mode 100644 Dockerfile.prod create mode 100644 deploy/1panel/openresty-nexus.conf.example create mode 100644 docker/.env.prod.example create mode 100644 docker/docker-compose.prod.yml create mode 100644 docs/changelog/2026-06-04-1panel-docker-production-plan.md create mode 100644 docs/design/plans/2026-06-04-1panel-docker-production.md diff --git a/.gitignore b/.gitignore index 08b2e290..5eab9418 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,9 @@ backups/ # Docker local secrets docker/.env +docker/.env.prod !docker/.env.example +!docker/.env.prod.example scripts/npm-proxy.env # Deploy diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 00000000..133cfb1b --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,44 @@ +# Nexus 6.0 — production image (frontend build + API) +# Build: docker compose -f docker/docker-compose.prod.yml build +# See: docs/design/plans/2026-06-04-1panel-docker-production.md + +# ── Stage 1: Vue/Vite SPA ── +FROM node:20-bookworm-slim AS frontend +WORKDIR /build/frontend + +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci --ignore-scripts + +COPY frontend/ ./ +RUN npx vite build + +# ── Stage 2: Python API + built static assets ── +FROM python:3.12-slim-bookworm + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY server/ ./server/ +COPY web/agent/ ./web/agent/ +COPY --from=frontend /build/web/app ./web/app/ +COPY docker/entrypoint.sh ./docker/entrypoint.sh +RUN chmod +x ./docker/entrypoint.sh + +ENV PYTHONPATH=/app \ + PYTHONUNBUFFERED=1 \ + NEXUS_HOST=0.0.0.0 \ + NEXUS_PORT=8600 + +EXPOSE 8600 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=90s --retries=3 \ + CMD curl -sf http://127.0.0.1:8600/health | grep -q '^ok' || exit 1 + +ENTRYPOINT ["/app/docker/entrypoint.sh"] +CMD ["uvicorn", "server.main:app", "--host", "0.0.0.0", "--port", "8600"] diff --git a/deploy/1panel/openresty-nexus.conf.example b/deploy/1panel/openresty-nexus.conf.example new file mode 100644 index 00000000..9baee51c --- /dev/null +++ b/deploy/1panel/openresty-nexus.conf.example @@ -0,0 +1,70 @@ +# 1Panel OpenResty — Nexus reverse proxy (paste into site custom config) +# Domain: api.synaglobal.vip +# Upstream: Nexus Docker on 127.0.0.1:8600 (docker-compose.prod.yml) +# +# SSL: configure in 1Panel UI (Let's Encrypt). This snippet assumes HTTPS server block. + +upstream nexus_api { + server 127.0.0.1:8600; + keepalive 32; +} + +# WebSocket log without query string (JWT must not hit access_log) +log_format nexus_ws '$remote_addr - [$time_local] "$request_method $uri" $status $body_bytes_sent'; + +client_max_body_size 100m; + +location /api/ { + proxy_pass http://nexus_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_connect_timeout 10s; +} + +location /ws/ { + access_log /www/sites/api.synaglobal.vip/log/ws.log nexus_ws; + proxy_pass http://nexus_api; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; +} + +location /health { + proxy_pass http://nexus_api; + proxy_set_header Host $host; +} + +location /app/ { + proxy_pass http://nexus_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; +} + +location /agent/ { + proxy_pass http://nexus_api; + proxy_set_header Host $host; +} + +# Fallback: install mode or misc routes served by FastAPI +location / { + proxy_pass http://nexus_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; +} diff --git a/docker/.env.prod.example b/docker/.env.prod.example new file mode 100644 index 00000000..030cf276 --- /dev/null +++ b/docker/.env.prod.example @@ -0,0 +1,17 @@ +# Production Compose env — copy to docker/.env.prod (never commit) +# Migrate NEXUS_* secrets from Baota /www/wwwroot/.../.env UNCHANGED. + +MYSQL_ROOT_PASSWORD=change_me_root_strong +MYSQL_PASSWORD=change_me_nexus_strong + +# Required — copy from production .env (do NOT regenerate) +NEXUS_SECRET_KEY= +NEXUS_API_KEY= +NEXUS_ENCRYPTION_KEY= + +# Public URL +NEXUS_CORS_ORIGINS=https://api.synaglobal.vip +NEXUS_API_BASE_URL=https://api.synaglobal.vip + +# Optional +# NEXUS_PUBLISH_PORT=8600 diff --git a/docker/README.md b/docker/README.md index 0127707d..8b2ac88b 100644 --- a/docker/README.md +++ b/docker/README.md @@ -53,4 +53,15 @@ docker compose build --no-cache nexus - 建议前置 Nginx/TLS,仅暴露 443。 - 多实例部署需共享 MySQL、Redis;后台任务由 Redis primary lock 保证单实例执行。 +### 1Panel 生产(Compose + OpenResty) + +```bash +cp docker/.env.prod.example docker/.env.prod # 从宝塔 .env 迁入密钥 +docker compose -f docker/docker-compose.prod.yml --env-file docker/.env.prod up -d --build +``` + +- 镜像:`Dockerfile.prod`(含 `npm ci` + `vite build`) +- 仅绑定 `127.0.0.1:8600`;OpenResty 反代见 `deploy/1panel/openresty-nexus.conf.example` +- 完整迁移步骤:`docs/design/plans/2026-06-04-1panel-docker-production.md` + 详见 `docs/design/specs/2026-06-03-docker-design.md`。 diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml new file mode 100644 index 00000000..56255a1b --- /dev/null +++ b/docker/docker-compose.prod.yml @@ -0,0 +1,93 @@ +# Nexus 6.0 — production Compose (1Panel / bare Docker host) +# Usage: +# cp docker/.env.prod.example docker/.env.prod # fill secrets from Baota .env +# docker compose -f docker/docker-compose.prod.yml --env-file docker/.env.prod up -d --build +# +# OpenResty on host proxies https://api.synaglobal.vip → 127.0.0.1:8600 + +name: nexus-prod + +services: + mysql: + image: mysql:8.4 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: nexus + MYSQL_USER: nexus + MYSQL_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD} + volumes: + - mysql-data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p\"$$MYSQL_ROOT_PASSWORD\" || exit 1"] + interval: 10s + timeout: 5s + retries: 8 + start_period: 40s + networks: + - nexus-internal + + redis: + image: redis:7-alpine + restart: unless-stopped + command: ["redis-server", "--appendonly", "yes"] + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 8 + networks: + - nexus-internal + + nexus: + build: + context: .. + dockerfile: Dockerfile.prod + restart: unless-stopped + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + ports: + - "127.0.0.1:${NEXUS_PUBLISH_PORT:-8600}:8600" + environment: + MYSQL_HOST: mysql + MYSQL_PORT: "3306" + REDIS_HOST: redis + REDIS_PORT: "6379" + NEXUS_PERSIST_DIR: /var/lib/nexus + NEXUS_DOCKER_WRITE_ENV: "1" + NEXUS_HOST: "0.0.0.0" + NEXUS_PORT: "8600" + NEXUS_DEPLOY_PATH: /app + NEXUS_DATABASE_URL: mysql+aiomysql://nexus:${MYSQL_PASSWORD}@mysql:3306/nexus + NEXUS_REDIS_URL: redis://redis:6379/0 + NEXUS_CORS_ORIGINS: ${NEXUS_CORS_ORIGINS:?set NEXUS_CORS_ORIGINS} + NEXUS_SECRET_KEY: ${NEXUS_SECRET_KEY:?set NEXUS_SECRET_KEY} + NEXUS_API_KEY: ${NEXUS_API_KEY:?set NEXUS_API_KEY} + NEXUS_ENCRYPTION_KEY: ${NEXUS_ENCRYPTION_KEY:-} + NEXUS_API_BASE_URL: ${NEXUS_API_BASE_URL:?set NEXUS_API_BASE_URL} + volumes: + - nexus-state:/var/lib/nexus + - nexus-web-data:/app/web/data + healthcheck: + test: ["CMD", "curl", "-sf", "http://127.0.0.1:8600/health"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 90s + networks: + - nexus-internal + +networks: + nexus-internal: + driver: bridge + +volumes: + mysql-data: + redis-data: + nexus-state: + nexus-web-data: diff --git a/docs/changelog/2026-06-04-1panel-docker-production-plan.md b/docs/changelog/2026-06-04-1panel-docker-production-plan.md new file mode 100644 index 00000000..212c1ec2 --- /dev/null +++ b/docs/changelog/2026-06-04-1panel-docker-production-plan.md @@ -0,0 +1,37 @@ +# Changelog — 1Panel + Docker 生产迁移方案 + +## 摘要 + +新增 1Panel 替代宝塔的生产迁移 SSOT:多阶段 `Dockerfile.prod`、生产 Compose、OpenResty 反代示例与分阶段 runbook。 + +## 动机 + +用户确认采用 **1Panel + Docker Compose** 部署 Nexus,需可执行文档与配置文件,避免继续依赖宝塔路径与手工 tar 前端。 + +## 涉及文件 + +| 文件 | 说明 | +|------|------| +| `docs/design/plans/2026-06-04-1panel-docker-production.md` | 迁移主文档(备份→Compose→OpenResty→L5) | +| `Dockerfile.prod` | Node 构建 SPA + Python 镜像 | +| `docker/docker-compose.prod.yml` | 生产栈(DB 不暴露、8600 仅 127.0.0.1) | +| `docker/.env.prod.example` | 生产 env 模板 | +| `deploy/1panel/openresty-nexus.conf.example` | 1Panel 反代片段 | +| `docker/README.md` | 增加 1Panel 快速命令 | +| `.gitignore` | 忽略 `docker/.env.prod` | + +## 迁移 / 重启 + +- 新环境:`docker compose -f docker/docker-compose.prod.yml up -d --build` +- 密钥:从宝塔 `.env` **原样**迁入,禁止重生成 + +## 验证 + +```bash +docker compose -f docker/docker-compose.prod.yml --env-file docker/.env.prod config +curl -s http://127.0.0.1:8600/health # 部署后 +``` + +## 回滚 + +见计划文档 §7;DNS 指回宝塔旧机 + 恢复 mysqldump。 diff --git a/docs/design/plans/2026-06-04-1panel-docker-production.md b/docs/design/plans/2026-06-04-1panel-docker-production.md new file mode 100644 index 00000000..a22b02c1 --- /dev/null +++ b/docs/design/plans/2026-06-04-1panel-docker-production.md @@ -0,0 +1,334 @@ +# 1Panel + Docker Compose 生产迁移计划 + +| 项 | 内容 | +|----|------| +| 日期 | 2026-06-04 | +| 状态 | **计划** — 待在新 VPS 或同机试跑后执行 | +| 目标 | 脱离宝塔,用 **1Panel 管机 + Docker Compose 跑 Nexus 全栈** | +| 关联 | [2026-06-03-docker.md](./2026-06-03-docker.md) · `docker-compose.yml` · `deploy/nginx_https.conf` | + +--- + +## 1. 背景与目标 + +### 1.1 现状 + +- 生产:`47.254.123.106` / `api.synaglobal.vip`,宝塔 + 裸机 Git + Supervisor + Nginx +- 代码:Gitea `main` @ `811eb97+` +- 本地:已有 `docker-compose.yml`(dev/staging),**不含前端构建**(`Dockerfile` 依赖预构建 `web/app/`) + +### 1.2 目标架构 + +```mermaid +flowchart TB + subgraph internet [Internet] + Users[浏览器 / Agent] + Agents[2000+ Agent] + end + + subgraph vps [VPS + 1Panel] + OP[OpenResty HTTPS] + subgraph compose [Docker Compose prod] + NX[nexus:8600] + MY[(mysql:3306)] + RD[(redis:6379)] + end + Vol1[(mysql-data)] + Vol2[(redis-data)] + Vol3[(nexus-state .env)] + end + + Users --> OP + Agents --> OP + OP -->|127.0.0.1:8600| NX + NX --> MY + NX --> RD + MY --> Vol1 + RD --> Vol2 + NX --> Vol3 +``` + +**原则** + +- 1Panel:防火墙、SSL、备份、终端、Compose 编排 UI +- Nexus:**单 Compose 栈**(MySQL + Redis + API/静态 SPA) +- OpenResty:**只反代到 `127.0.0.1:8600`**(静态由 FastAPI `StaticFiles` 提供,无需双份 `web/app`) +- **密钥不动**:`NEXUS_SECRET_KEY` / `NEXUS_API_KEY` / `NEXUS_ENCRYPTION_KEY` 从宝塔 `.env` 原样迁入 + +--- + +## 2. 涉及文件清单 + +| 文件 | 作用 | +|------|------| +| `Dockerfile.prod` | 多阶段:Node 构建前端 + Python 镜像 | +| `docker/docker-compose.prod.yml` | 生产 Compose(不暴露 DB 端口、仅绑定 127.0.0.1:8600) | +| `docker/.env.prod.example` | 生产环境变量模板(不入 Git) | +| `deploy/1panel/openresty-nexus.conf.example` | 1Panel 网站反代参考 | +| `docs/project/production-verification-checklist.md` | L5 验收 | + +**本计划不修改** `docker-compose.yml`(继续服务本地 dev)。 + +--- + +## 3. 服务器前置条件 + +| 项 | 要求 | +|----|------| +| OS | Ubuntu 22.04+ / Debian 11+ | +| 内存 | **≥ 4GB**(MySQL + Redis + Nexus + 1Panel) | +| 磁盘 | ≥ 40GB(视 Agent 历史与日志) | +| 域名 | `api.synaglobal.vip` A 记录指向本机 | +| 1Panel | [官方安装脚本](https://1panel.cn/docs/installation/) 最新稳定版 | +| Docker | 由 1Panel 安装或自带 | + +**建议**:在新 VPS 试跑通过后再切 DNS,宝塔旧机保留 24~48h 回滚窗口。 + +--- + +## 4. 实施步骤 + +### Phase 0 — 备份宝塔生产(必做) + +在**旧宝塔机**执行: + +```bash +# 1) 整库备份 +mysqldump -u Nexus -p nexus > /tmp/nexus-$(date +%F).sql + +# 2) 复制 .env(含不可改密钥) +cp /www/wwwroot/api.synaglobal.vip/.env /tmp/nexus.env.backup + +# 3) 可选:web/data +tar czf /tmp/nexus-web-data.tar.gz -C /www/wwwroot/api.synaglobal.vip/web data +``` + +将 `nexus-*.sql`、`nexus.env.backup` 下载到本机安全位置(勿提交 Git)。 + +--- + +### Phase 1 — 安装 1Panel + +```bash +curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh +sudo bash quick_start.sh +``` + +记录面板地址、端口、管理员密码。 +**安全**:改默认端口、开防火墙仅放行 22 / 443 / 面板端口。 + +--- + +### Phase 2 — 准备代码与生产 env + +在 1Panel **终端**或 SSH: + +```bash +export NEXUS_ROOT=/opt/nexus +sudo mkdir -p "$NEXUS_ROOT" +sudo git clone http://66.154.115.8:3000/admin/Nexus.git "$NEXUS_ROOT" +cd "$NEXUS_ROOT" +git checkout main && git pull +``` + +创建 `docker/.env.prod`(从 `docker/.env.prod.example` 复制): + +```bash +cp docker/.env.prod.example docker/.env.prod +chmod 600 docker/.env.prod +nano docker/.env.prod +``` + +**从宝塔 `.env` 原样填入**(不可改项): + +- `NEXUS_SECRET_KEY` +- `NEXUS_API_KEY` +- `NEXUS_ENCRYPTION_KEY` +- `NEXUS_DATABASE_URL` → 改为 Compose 内网:`mysql+aiomysql://nexus:<密码>@mysql:3306/nexus` +- `NEXUS_REDIS_URL` → `redis://redis:6379/0` +- `NEXUS_CORS_ORIGINS` → `https://api.synaglobal.vip` +- `NEXUS_API_BASE_URL` → `https://api.synaglobal.vip` + +MySQL 密码:可用宝塔库密码,或新设强密码并在导入后统一。 + +--- + +### Phase 3 — 构建并启动 Compose + +```bash +cd /opt/nexus +docker compose -f docker/docker-compose.prod.yml --env-file docker/.env.prod up -d --build +docker compose -f docker/docker-compose.prod.yml ps +docker compose -f docker/docker-compose.prod.yml logs -f nexus +``` + +等待 MySQL healthy → Nexus entrypoint 写/恢复 `.env` → `/health` 为 `ok`: + +```bash +curl -s http://127.0.0.1:8600/health # 期望: ok +curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8600/app/ # 期望: 200 +``` + +--- + +### Phase 4 — 导入 MySQL 数据 + +```bash +# 上传 nexus-YYYY-MM-DD.sql 到 /tmp/ +docker compose -f docker/docker-compose.prod.yml exec -T mysql \ + mysql -uroot -p"$MYSQL_ROOT_PASSWORD" nexus < /tmp/nexus-2026-06-04.sql +``` + +验证: + +```bash +docker compose -f docker/docker-compose.prod.yml exec mysql \ + mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT COUNT(*) FROM servers;" +``` + +**注意**:若库名/用户与宝塔不同,先建用户再导入;导入前建议空库或 `--force` 策略写进 runbook。 + +--- + +### Phase 5 — 1Panel OpenResty 反代 + SSL + +1. 1Panel → **网站** → 创建站点 → 域名 `api.synaglobal.vip` +2. **HTTPS** → Let's Encrypt 一键申请 +3. **反向代理** → 目标 `http://127.0.0.1:8600`(或「配置文件」粘贴 `deploy/1panel/openresty-nexus.conf.example` 内容) + +要点(与 `deploy/nginx_https.conf` 对齐): + +| 路径 | 行为 | +|------|------| +| `/api/` | 反代 API,`proxy_read_timeout 300s` | +| `/ws/` | WebSocket,`Upgrade` + `Connection upgrade`,超时 3600s | +| `/health` | 反代(或仅内网探测) | +| `/app/` | 反代(容器内 StaticFiles) | +| `client_max_body_size` | `100m`(上传/推送) | + +**WS 日志**:勿记录 query 中的 JWT(示例配置已用 `$uri` 无 `$args`)。 + +--- + +### Phase 6 — 在 1Panel 中托管 Compose(可选) + +1Panel → **容器** → **编排** → 导入 `/opt/nexus/docker/docker-compose.prod.yml` +环境文件指向 `docker/.env.prod`,便于 UI 启停、看日志、备份卷。 + +--- + +### Phase 7 — L5 验收 + +按 `docs/project/production-verification-checklist.md`: + +| # | 检查 | 命令/预期 | +|---|------|-----------| +| 1 | HTTPS health | `curl -s https://api.synaglobal.vip/health` → `ok` | +| 2 | SPA | 浏览器 `/app/` 登录 | +| 3 | JWT | 未登录 `/api/servers/` → 401 | +| 4 | WebSocket | 仪表盘连接、告警刷新 | +| 5 | WebSSH | 终端页连一台子机 | +| 6 | Agent | 子机心跳仍指向同域名,Redis key 有 TTL | +| 7 | 后台任务 | 10min 内 MySQL 有心跳历史写入 | + +记录到 `docs/reports/2026-06-04-1panel-migration-verification.md`(验收后创建)。 + +--- + +### Phase 8 — 切流量与下线宝塔 + +1. 确认新机 L5 全通过 +2. DNS `api.synaglobal.vip` → 新 IP(若换机) +3. 观察 24h:Agent、告警、Telegram +4. 宝塔机:停 `nexus` supervisor、保留备份后关机或降级为备用 + +--- + +## 5. 生产 Compose 与开发差异 + +| 项 | dev `docker-compose.yml` | prod `docker-compose.prod.yml` | +|----|--------------------------|--------------------------------| +| 构建 | `Dockerfile` | `Dockerfile.prod`(含前端 build) | +| MySQL/Redis 端口 | 可映射宿主机 | **不映射**(仅 compose 网络) | +| Nexus 端口 | `8600:8600` | `127.0.0.1:8600:8600` | +| env 文件 | `docker/.env` | `docker/.env.prod` | +| 副本数 | 1 | **1**(后台任务 primary worker,勿随意扩多实例) | + +--- + +## 6. 日常运维 + +### 6.1 升级(Gitea 有新 commit) + +```bash +cd /opt/nexus +git fetch && git reset --hard origin/main +docker compose -f docker/docker-compose.prod.yml --env-file docker/.env.prod up -d --build +curl -s http://127.0.0.1:8600/health +``` + +### 6.2 备份(1Panel 计划任务 + 手动) + +| 对象 | 方式 | +|------|------| +| MySQL | 1Panel 数据库备份 或 `mysqldump` cron | +| 卷 `nexus-state` | 含持久化 `.env`,随卷备份 | +| 卷 `nexus-web-data` | `web/data/config.json` 等 | + +### 6.3 日志 + +```bash +docker compose -f docker/docker-compose.prod.yml logs -f --tail=200 nexus +``` + +--- + +## 7. 回滚 + +| 场景 | 操作 | +|------|------| +| 新版本故障 | `git reset --hard <旧commit>` + `compose up -d --build` | +| 数据库误操作 | 恢复 Phase 0 的 `mysqldump` | +| 切 DNS 后严重故障 | DNS 指回宝塔旧 IP;旧机 `supervisorctl start nexus` | +| 密钥误改 | **禁止**随意重生成;从 `nexus.env.backup` 恢复 | + +--- + +## 8. 风险与对策 + +| 风险 | 对策 | +|------|------| +| 密钥变更导致 Agent/凭据失效 | Phase 0/2 原样迁移三项 KEY | +| 多副本重复 schedule/retry | 生产 `replicas: 1` | +| 前端未进镜像 | 必须用 `Dockerfile.prod` | +| OpenResty WS 超时 | `/ws/` 读超时 ≥ 3600s | +| 内存不足 | ≥4GB;MySQL 可限 `innodb_buffer_pool_size` | +| 1Panel 与 Compose 端口冲突 | Nexus 仅监听 127.0.0.1:8600 | + +--- + +## 9. 与 Coolify 的关系 + +本方案 **不需要 Coolify**。若后续要 Git push 自动构建,可在 1Panel 稳定运行后,再评估: + +- 1Panel 编排 + Gitea Webhook 触发 `compose up --build` +- 或同机加装 Coolify 仅负责 Nexus 镜像 CI + +--- + +## 10. 验收标准(DoD) + +- [ ] 1Panel 安装且仅必要端口开放 +- [ ] `docker compose -f docker/docker-compose.prod.yml ps` 三服务 healthy +- [ ] `https://api.synaglobal.vip/health` → `ok` +- [ ] 登录 + 文件管理 + WebSSH + 至少 1 台 Agent 心跳正常 +- [ ] 宝塔已下线或不再承担流量 +- [ ] changelog + 验收报告已写入 + +--- + +## 11. 变更记录 + +| 日期 | 说明 | +|------|------| +| 2026-06-04 | 初版:1Panel + Docker Compose 生产迁移 SSOT |