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.
This commit is contained in:
@@ -73,7 +73,9 @@ backups/
|
|||||||
|
|
||||||
# Docker local secrets
|
# Docker local secrets
|
||||||
docker/.env
|
docker/.env
|
||||||
|
docker/.env.prod
|
||||||
!docker/.env.example
|
!docker/.env.example
|
||||||
|
!docker/.env.prod.example
|
||||||
scripts/npm-proxy.env
|
scripts/npm-proxy.env
|
||||||
|
|
||||||
# Deploy
|
# Deploy
|
||||||
|
|||||||
@@ -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"]
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -53,4 +53,15 @@ docker compose build --no-cache nexus
|
|||||||
- 建议前置 Nginx/TLS,仅暴露 443。
|
- 建议前置 Nginx/TLS,仅暴露 443。
|
||||||
- 多实例部署需共享 MySQL、Redis;后台任务由 Redis primary lock 保证单实例执行。
|
- 多实例部署需共享 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`。
|
详见 `docs/design/specs/2026-06-03-docker-design.md`。
|
||||||
|
|||||||
@@ -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:
|
||||||
@@ -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。
|
||||||
@@ -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 |
|
||||||
Reference in New Issue
Block a user