# 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 — 一键安装（推荐）

在 1Panel **终端**：

```bash
export NEXUS_GITEA_TOKEN='你的Gitea令牌'
export NEXUS_DOMAIN='api.synaglobal.vip'
# 从宝塔迁机:
# export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env'

cd /opt/nexus 2>/dev/null && git pull || true
# 若尚未 clone，从任意目录:
curl -fsSL "http://66.154.115.8:3000/admin/Nexus/raw/branch/main/deploy/install-1panel-docker.sh" | bash
# 或已 clone 后:
# cd /opt/nexus && bash deploy/install-1panel-docker.sh --skip-clone
```

脚本：`deploy/install-1panel-docker.sh`（clone → `.env.prod` → `compose up` → `/health`）。

### Phase 2b — 手工准备代码与生产 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` | `500m`（上传/推送） |

**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 一键升级（推荐）

```bash
# 可选：echo 'NEXUS_GITEA_TOKEN=...' > /root/.nexus-deploy.env && chmod 600 /root/.nexus-deploy.env
bash /opt/nexus/deploy/upgrade-1panel-docker.sh --check   # 仅查是否有更新
bash /opt/nexus/deploy/upgrade-1panel-docker.sh             # 备份 DB + pull + 重建 + /health
```

脚本：`deploy/upgrade-1panel-docker.sh`（`--no-backup` / `--prune` / `--dry-run`）。

### 6.2 手工升级

```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 |
