# 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,勿随意扩多实例) | | **2C8G 调优** | 无 | `mysql-prod-2c8g.cnf` + 容器内存上限 + pool 30/20 | ### 2C8G 资源预算(约) | 组件 | 上限 | |------|------| | MySQL | 2G 容器 / `innodb_buffer_pool_size=1024M` | | Redis | 384M 容器 / `maxmemory 256mb` | | Nexus | 2G 容器 | | 1Panel + OS | 其余 ~3G | `docker/.env.prod` 建议包含:`NEXUS_DB_POOL_SIZE=30`、`NEXUS_DB_MAX_OVERFLOW=20`。 --- ## 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 |