Files
OpenFlare/docs/DEPLOYMENT.md
T

336 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wavelet 部署指南
本文档详细介绍了 **wavelet** 脚手架系统在不同业务阶段的部署方案,涵盖从**最小化单机部署**到**最大化高可用分布式部署**的全生命周期架构。
---
## 一、 系统组件概览
在部署系统前,请了解各运行组件及其角色:
| 组件名称 | 运行命令/形式 | 职责说明 | 必选/可选 |
| :--- | :--- | :--- | :--- |
| **HTTP API 服务** | `bin/wavelet api` | 接收并处理前端及第三方的 RESTful API 请求 | **必选** |
| **异步任务工作进程** | `bin/wavelet worker` | 消费并处理异步队列任务(如邮件发送、清理上传文件等) | **必选** |
| **定时任务调度器** | `bin/wavelet scheduler` | 定时向 Redis 队列下发 Cron 任务(仅负责触发,不负责执行) | **必选** |
| **前端服务 (Node.js)** | `pnpm start` | 提供 React/Next.js 页面服务(在分离部署时使用) | 分离模式必选 |
| **PostgreSQL** | 关系型主数据库 | 存储用户、系统配置、认证源、任务执行记录等核心数据 | **必选** |
| **Redis** | 缓存与消息队列中间件 | 存储 Session 会话、临时缓存以及 Asynq 异步任务队列数据 | **必选** |
| **ClickHouse** | 分析型数据库 | 可选的日志主库;关闭时访问审计由 PostgreSQL/SQLite 承接 | 可选 |
| **对象存储 (S3)** | 兼容 S3 的云存储/私有云 | 存放用户上传的静态文件、图片等 | 可选 |
---
## 二、 部署配置准备
系统在启动前会默认加载 `manifest/config/config.default.yaml`,并自动读取 `manifest/config/config.yaml`(或 `CONFIG_PATH` 环境变量指定的文件)进行覆盖。
生产环境部署前,可在 `manifest/config/config.yaml` 中按需覆盖以下关键参数:
```yaml
app:
env: "production" # 生产环境标识
addr: ":8000" # API 服务监听端口
session_secret: "prod-random-secret" # 极其重要的加密密钥,首发启动后不可更改
session_domain: ".yourdomain.com" # 跨域共享 Session 时需配置
database:
host: "db.yourdomain.com"
port: 5432
username: "postgres"
password: "YOUR_DB_PASSWORD"
database: "refreshing"
redis:
addrs:
- "redis.yourdomain.com:6379"
password: "YOUR_REDIS_PASSWORD"
```
---
## 三、 方案一:最小部署 — 单机嵌入式极简版 (推荐)
此部署方案将**前端静态网页全部直接打入 Go 后端二进制文件中**,极大地简化了部署运维,是中小型应用、内部系统、SaaS 早期阶段的首选。
### 📊 架构设计
- **服务载体**:单台云服务器 (1核2G 即可)。
- **依赖服务**:在一台机器上启动轻量级 PostgreSQL 与 Redis(可采用 Docker 部署)。
- **进程管理**:在一台机器上直接拉起打包好的 Go 单文件,并分别运行 `api`、`worker`、`scheduler` 进程。
- **前端托管**:Go 服务直接在 8000 端口承载前端的所有页面,不需要额外配置 Node.js 生产服务器。
### 🛠️ 步骤说明
#### 1. 单机依赖服务初始化 (使用 Docker Compose)
在机器上准备以下 `docker-compose.yml` 快速启动 PostgreSQL 和 Redis:
```yaml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: refreshing-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: YOUR_DB_PASSWORD
POSTGRES_DB: refreshing
ports:
- "5432:5432"
volumes:
- ./data/pg:/var/lib/postgresql/data
restart: always
redis:
image: valkey/valkey:8.0-alpine
container_name: refreshing-redis
command: valkey-server --requirepass YOUR_REDIS_PASSWORD
ports:
- "6379:6379"
volumes:
- ./data/redis:/data
restart: always
```
执行命令启动:
```bash
docker compose up -d
```
#### 2. 前后端一键嵌入式打包
在开发或编译机上,运行编译指令:
```bash
make build-embedded
```
该命令会自动完成前端的静态编译导出 (`frontend/out`)、复制到 Go 后端目录,最后使用 `-tags embed_frontend` 生成后端单文件:
- 产物路径:`bin/wavelet`
#### 3. 进程管理 (使用 Systemd)
将 `bin/wavelet` 拷贝到生产服务器 `/usr/local/bin/wavelet`,并为 `api`、`worker` 和 `scheduler` 配置 Systemd 管理服务。
新建 API 进程服务文件 `/etc/systemd/system/wavelet-api.service`:
```ini
[Unit]
Description=Refreshing API Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/app
ExecStart=/usr/local/bin/wavelet api
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
同理,新建 Worker 服务 `/etc/systemd/system/wavelet-worker.service`(将命令改为 `wavelet worker`),以及 Scheduler 服务 `/etc/systemd/system/wavelet-scheduler.service`(将命令改为 `wavelet scheduler`)。
启动并启用所有服务:
```bash
systemctl daemon-reload
systemctl enable --now refreshing-api refreshing-worker refreshing-scheduler
```
#### 4. 配置 Nginx 证书
配置 Nginx 作为反向代理并启用 HTTPS 证书:
```nginx
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /path/to/cert.crt;
ssl_certificate_key /path/to/cert.key;
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}
```
---
## 四、 方案二:标准部署 — 前后端物理分离架构
此方案中前端与后端彻底解耦。前端采用 SSR/ISR (Next.js Node 服务) 运行,后端采用独立的 API 服务运行。
### 📊 架构设计
- **前端部署**:单独部署到 Node.js 托管环境(如多台前端机器或 Vercel/Cloudflare Pages)。
- **后端部署**:多台后端云服务器,统一指向云数据库 RDS 与云缓存 Redis。
- **通信方式**:前后端通过 Nginx 规则路由或独立域名(如 `app.yourdomain.com` 访问前端,`api.yourdomain.com` 访问后端)进行跨域通信。
### 🛠️ 步骤说明
#### 1. 部署后端 Go 服务
1. 编译后端:
```bash
go build -o bin/wavelet main.go
```
2. 在后端服务器上,同样使用 Systemd 或 Docker 守护启动 `wavelet api`、`wavelet worker` 和 `wavelet scheduler`。
3. 配置后端 Nginx 将客户端 API 请求(如 `/api/...`)反向代理至后端绑定的端口(如 `:8000`)。
#### 2. 部署前端 Next.js 服务
1. 前端服务器环境确保已安装 Node.js 和 pnpm。
2. 安装依赖并编译生产版本:
```bash
cd frontend
pnpm install
pnpm build
```
3. 使用 PM2 守护前端 Node.js 服务运行。新建 `ecosystem.config.js`:
```javascript
module.exports = {
apps: [
{
name: 'refreshing-frontend',
script: 'node_modules/next/dist/bin/next',
args: 'start -p 3000',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
WAVELET_BACKEND_URL: 'https://api.yourdomain.com'
}
}
]
};
```
启动前端服务:
```bash
pm2 start ecosystem.config.js
```
#### 3. 跨域与 Cookie 说明
- 若前后端使用**不同子域名**部署(例如 `app.yourdomain.com` 和 `api.yourdomain.com`),必须在 `config.yaml` 中将 `app.session_domain` 显式设置为顶级域名(`.yourdomain.com`),以确保 Session Cookie 可以在子域间顺利透传。
- 在跨域状态下,前端请求必须配置 `withCredentials: true`,API 端的跨域中间件(`corsMiddleware`)会自动将该域添加至允许源中。
---
## 五、 方案三:最大部署 — 企业级高可用分布式架构 (Max)
当系统面临高并发流量、海量后台任务或极高的可用性要求时,需要将所有组件拆分为无状态水平扩容,并引入高可用的云基础设施。
### 📊 架构设计图
```
┌────────────────────────┐
│ 域名 / 负载均衡器 │
│ (SLB / Cloudflare) │
└──────────┬─────────────┘
│
┌──────────────────┴──────────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ 前端集群 │ │ 后端 API 集群 │
│ (Next.js Node) │ │ (Go 无状态实例) │
│ [弹性扩容 / 8台+] │ │ [弹性扩容 / 8台+] │
└─────────────────────┘ └──────────┬──────────┘
│
┌────────────────────────────────────────┼────────────────────────────────────────┐
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ 异步 Worker 集群 │ │ 定时 Scheduler │ │ S3 对象存储集群 │
│ (多节点并发处理) │ │ (主备模式,限单节点)│ │(R2/MinIO/AWS S3) │
└─────────┬─────────┘ └─────────┬─────────┘ └───────────────────┘
│ │
└───────────────────┬────────────────────┘
│
┌───────────────────┴────────────────────┐
▼ ▼
┌───────────────────────────────────┐ ┌───────────────────────────────────┐
│ Redis 哨兵/集群 │ │ PG 主从读写分离集群 │
│ (高可用缓存/Asynq 队列) │ │ (RDS Primary-Replica) │
└───────────────────────────────────┘ └───────────────────────────────────┘
```
### ⚙️ 最大部署配置要点
#### 1. 数据库高可用 (主从读写分离)
在 `config.yaml` 中配置 `database` 的主库写与从库读:
```yaml
database:
enabled: true
host: "pg-primary.yourdomain.com" # 主库地址(写)
port: 5432
username: "postgres"
password: "YOUR_DB_PASSWORD"
database: "refreshing"
# 配置读写分离只读副本(GORM 自动轮询读,支持配置多个从库)
replicas:
- host: "pg-replica-1.yourdomain.com"
port: 5432
username: "postgres"
password: "YOUR_DB_PASSWORD"
- host: "pg-replica-2.yourdomain.com"
port: 5432
username: "postgres"
password: "YOUR_DB_PASSWORD"
```
#### 2. Redis 高可用 (哨兵/Sentinel 或集群)
- **Sentinel 哨兵模式**:通过配置 `redis.master_name` 启用,SDK 会自动监视 Master 的主备切换。
- **Cluster 集群模式**:将 `redis.cluster_mode` 设为 `true`,并提供所有集群节点的 `addrs`。
```yaml
redis:
addrs:
- "redis-node-1.yourdomain.com:6379"
- "redis-node-2.yourdomain.com:6379"
- "redis-node-3.yourdomain.com:6379"
cluster_mode: true
```
#### 3. 对象存储与缓存分离 (S3 + Local Cache)
高可用集群下,本地文件系统不再可共享。文件存储必须启用 S3 兼容服务,并在多节点间开启本地高速磁盘缓存加速读取:
```yaml
s3:
enabled: true
endpoint: "https://your-r2-or-s3-id.r2.cloudflarestorage.com"
region: "auto"
bucket: "refreshing-assets"
access_key_id: "YOUR_S3_KEY"
secret_access_key: "YOUR_S3_SECRET"
local_cache:
enabled: true # 开启本地磁盘缓存
cache_dir: "/data/s3_cache" # 本地高性能 SSD 挂载点
```
#### 4. 后端进程横向拆分部署
- **API 集群**:启动数十个甚至上百个 `wavelet api` 无状态容器。它们可以通过负载均衡器直接挂载,支持随时弹性缩容扩容。
- **Worker 集群**:启动多个 `wavelet worker` 容器。因为 `Asynq` 基于 Redis 分布式处理,多个 Worker 进程可以安全地同时运行并竞抢同一队列的异步任务,自动保障任务的并发吞吐能力。
- **Scheduler 独占**:**【注意】** 为避免重复触发定时 Cron 任务,`wavelet scheduler` 定时调度器进程**同一时间应仅运行单个活跃实例**(主备高可用可以通过容器平台的单实例保障或 K8s Job 机制来限制实例数为 1)。
#### 5. ClickHouse 高并发同步
访问审计等日志表默认写在当前业务主库。数据量大、需要列式扫描时,开启 ClickHouse,再在任务管理运行「切换日志数据库」迁到 ClickHouse(迁移期间冻结写入,源数据不删)。开发约定见 [日志用途表](./LOGSTORE.md)。
```yaml
clickhouse:
enabled: true
hosts:
- "ch-node-1.yourdomain.com:9000"
- "ch-node-2.yourdomain.com:9000"
```
#### 6. OpenTelemetry 分布式链路追踪
最大部署架构必须引入链路追踪(Jaeger 或 OTel Collector)以便排查节点间请求延迟或网络问题。
在生产环境,通过配置 OTel 将 Span 发送至公共日志分析平台。
```yaml
otel:
sampling_rate: 0.05 # 开启 5% 的流量追踪采样率以减少开销
```
---
## 六、 部署方案对比与选择建议
| 指标维度 | 方案一:最小单机嵌入版 | 方案二:标准前后端分离版 | 方案三:最大高可用分布式版 |
| :--- | :--- | :--- | :--- |
| **支持流量/并发** | 1,000 ~ 5,000 QPS (视机器性能) | 5,000 ~ 20,000 QPS | 20,000 ~ 100,000+ QPS (无限扩展) |
| **服务器数量** | 1 台 | 3 ~ 5 台 | 10 台以上集群 |
| **运维复杂度** | 极简 (只需部署一个程序) | 中等 (需维护 Node 和 Go 两套环境) | 较高 (K8s/多组件集群维护) |
| **适合场景** | 个人项目、内部系统、SaaS 早期起步 | 正常线上运营项目、有中等规模团队 | 大型企业级应用、高并发核心交易系统 |