e44cdf913e
Use npm ci/install in deploy script and docs to avoid frozen pnpm lockfile failures on the server. Co-authored-by: Cursor <cursoragent@cursor.com>
460 lines
10 KiB
Markdown
460 lines
10 KiB
Markdown
# 知命阁(zhimingge)Ubuntu 部署文档
|
||
|
||
> 仓库:[https://git.bz121.com/dekun/zhimingge.git](https://git.bz121.com/dekun/zhimingge.git)
|
||
|
||
本文档说明在 **Ubuntu 服务器**上使用 **root 用户**、**PM2** 将知命阁部署到 **`/opt/zhimingge`** 的完整流程。
|
||
|
||
---
|
||
|
||
## 1. 部署架构
|
||
|
||
```
|
||
┌─────────────────┐
|
||
用户浏览器 ──→ │ Nginx (可选) │ :80 / :443
|
||
│ 反向代理 │
|
||
└────────┬────────┘
|
||
│
|
||
┌────────▼────────┐
|
||
│ PM2 │
|
||
│ zhimingge │ :3130
|
||
└────────┬────────┘
|
||
│
|
||
┌────────▼────────┐
|
||
│ Next.js │
|
||
│ standalone │
|
||
│ /opt/zhimingge │
|
||
└─────────────────┘
|
||
│
|
||
┌────────▼────────┐
|
||
│ op.bz121.com │ AI API
|
||
│ /v1 │
|
||
└─────────────────┘
|
||
```
|
||
|
||
| 组件 | 说明 |
|
||
|------|------|
|
||
| 操作系统 | Ubuntu 20.04 / 22.04 / 24.04 LTS |
|
||
| 运行用户 | root(按需求) |
|
||
| 安装目录 | `/opt/zhimingge` |
|
||
| 进程管理 | PM2 |
|
||
| 构建模式 | Next.js `output: "standalone"` |
|
||
| 默认端口 | **3130** |
|
||
|
||
---
|
||
|
||
## 2. 服务器要求
|
||
|
||
| 项目 | 最低配置 | 推荐配置 |
|
||
|------|----------|----------|
|
||
| CPU | 1 核 | 2 核 |
|
||
| 内存 | 1 GB | 2 GB+ |
|
||
| 磁盘 | 2 GB | 10 GB |
|
||
| 网络 | 可访问 git.bz121.com、op.bz121.com | — |
|
||
|
||
---
|
||
|
||
## 3. 一次性环境准备
|
||
|
||
以 **root** 登录服务器后执行:
|
||
|
||
### 3.1 更新系统
|
||
|
||
```bash
|
||
apt update && apt upgrade -y
|
||
```
|
||
|
||
### 3.2 安装 Node.js 20(推荐 LTS)
|
||
|
||
```bash
|
||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||
apt install -y nodejs
|
||
node -v # 应 >= v20
|
||
npm -v
|
||
```
|
||
|
||
### 3.3 安装 PM2
|
||
|
||
```bash
|
||
npm install -g pm2
|
||
pm2 -v
|
||
```
|
||
|
||
> **说明**:项目使用 **npm** + `package-lock.json` 安装依赖(无需 pnpm)。若已安装 pnpm 也可改用 `npm install`,避免 `pnpm-lock.yaml` 不同步问题。
|
||
|
||
### 3.4 安装 Git
|
||
|
||
```bash
|
||
apt install -y git
|
||
```
|
||
|
||
### 3.5(可选)安装 Nginx 反向代理
|
||
|
||
若需域名 + HTTPS,建议安装 Nginx 与 Certbot:
|
||
|
||
```bash
|
||
apt install -y nginx certbot python3-certbot-nginx
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 克隆与目录
|
||
|
||
```bash
|
||
mkdir -p /opt
|
||
cd /opt
|
||
|
||
# 首次克隆(私有仓库需配置凭据,见第 5 节)
|
||
git clone https://git.bz121.com/dekun/zhimingge.git zhimingge
|
||
|
||
cd /opt/zhimingge
|
||
```
|
||
|
||
最终目录:
|
||
|
||
```
|
||
/opt/zhimingge/
|
||
├── app/
|
||
├── content/
|
||
├── ecosystem.config.cjs
|
||
├── .env.local ← 生产环境变量(勿提交 Git)
|
||
├── package.json
|
||
└── ...
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Git 凭据(私有仓库)
|
||
|
||
仓库 [dekun/zhimingge](https://git.bz121.com/dekun/zhimingge.git) 为私有仓库,克隆前需配置认证。
|
||
|
||
### 方式 A:HTTPS + 访问令牌(推荐)
|
||
|
||
```bash
|
||
git config --global credential.helper store
|
||
git clone https://git.bz121.com/dekun/zhimingge.git /opt/zhimingge
|
||
# 按提示输入 Gitea 用户名与 Personal Access Token
|
||
```
|
||
|
||
在 Gitea 生成 Token:用户设置 → Applications → Generate New Token(勾选 `repo` 权限)。
|
||
|
||
### 方式 B:SSH
|
||
|
||
```bash
|
||
ssh-keygen -t ed25519 -C "zhimingge-deploy" -f /root/.ssh/zhimingge -N ""
|
||
cat /root/.ssh/zhimingge.pub
|
||
# 将公钥添加到 Gitea → Settings → SSH Keys
|
||
|
||
cat >> /root/.ssh/config << 'EOF'
|
||
Host git.bz121.com
|
||
IdentityFile /root/.ssh/zhimingge
|
||
StrictHostKeyChecking accept-new
|
||
EOF
|
||
|
||
git clone git@git.bz121.com:dekun/zhimingge.git /opt/zhimingge
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 环境变量
|
||
|
||
```bash
|
||
cd /opt/zhimingge
|
||
cp .env.example .env.local
|
||
nano .env.local
|
||
```
|
||
|
||
**`.env.local` 示例:**
|
||
|
||
```env
|
||
# AI 接口(必填)
|
||
OPENAI_API_KEY=你的密钥
|
||
|
||
# AI 地址与模型(已有默认值,可按需覆盖)
|
||
OPENAI_BASE_URL=https://op.bz121.com/v1
|
||
OPENAI_MODEL=huihui_ai/gemma-4-abliterated:e4b
|
||
|
||
# 服务端口(与 PM2 ecosystem.config.cjs 一致)
|
||
PORT=3130
|
||
NODE_ENV=production
|
||
```
|
||
|
||
保存后限制权限:
|
||
|
||
```bash
|
||
chmod 600 .env.local
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 构建与启动
|
||
|
||
### 7.1 安装依赖
|
||
|
||
```bash
|
||
cd /opt/zhimingge
|
||
npm ci || npm install
|
||
```
|
||
|
||
### 7.2 生产构建
|
||
|
||
```bash
|
||
npm run build
|
||
```
|
||
|
||
构建成功后生成 `.next/standalone/` 独立运行包。
|
||
|
||
### 7.3 复制静态资源(standalone 必需)
|
||
|
||
Next.js standalone 模式需手动链接静态文件:
|
||
|
||
```bash
|
||
cd /opt/zhimingge
|
||
|
||
# 复制 static 与 public 到 standalone 目录
|
||
cp -r .next/static .next/standalone/.next/static
|
||
cp -r public .next/standalone/public
|
||
|
||
# 复制内容库(服务端读取卦辞 Markdown)
|
||
cp -r content .next/standalone/content
|
||
```
|
||
|
||
> **说明**:若应用从项目根目录读取 `content/`,PM2 的 `cwd` 应设为 `/opt/zhimingge` 而非 standalone 子目录。当前 `ecosystem.config.cjs` 使用项目根目录 + `npm start`,构建后也可改用 standalone 的 `server.js`(见 7.5 备选方案)。
|
||
|
||
### 7.4 使用 PM2 启动(推荐方式)
|
||
|
||
项目根目录已包含 `ecosystem.config.cjs`:
|
||
|
||
```bash
|
||
cd /opt/zhimingge
|
||
pm2 start ecosystem.config.cjs
|
||
pm2 save
|
||
pm2 startup
|
||
# 按 PM2 提示执行生成的命令,确保开机自启
|
||
```
|
||
|
||
常用 PM2 命令:
|
||
|
||
```bash
|
||
pm2 status # 查看状态
|
||
pm2 logs zhimingge # 查看日志
|
||
pm2 restart zhimingge # 重启
|
||
pm2 stop zhimingge # 停止
|
||
pm2 delete zhimingge # 删除进程
|
||
```
|
||
|
||
### 7.5 备选:standalone 直接运行
|
||
|
||
若希望仅运行 standalone 产物(体积更小):
|
||
|
||
```bash
|
||
cd /opt/zhimingge/.next/standalone
|
||
PORT=3130 node server.js
|
||
```
|
||
|
||
对应 PM2 配置片段:
|
||
|
||
```javascript
|
||
{
|
||
name: "zhimingge",
|
||
cwd: "/opt/zhimingge/.next/standalone",
|
||
script: "server.js",
|
||
env: {
|
||
PORT: 3130,
|
||
NODE_ENV: "production",
|
||
},
|
||
}
|
||
```
|
||
|
||
此时需确保 `content/`、`public/` 路径在代码中相对于 `cwd` 可访问。
|
||
|
||
### 7.6 验证
|
||
|
||
```bash
|
||
curl -I http://127.0.0.1:3130
|
||
# 应返回 HTTP/1.1 200
|
||
```
|
||
|
||
浏览器访问:`http://服务器IP:3130`
|
||
|
||
---
|
||
|
||
## 8. Nginx 反向代理(可选)
|
||
|
||
假设域名为 `zhimingge.example.com`:
|
||
|
||
```bash
|
||
nano /etc/nginx/sites-available/zhimingge
|
||
```
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name zhimingge.example.com;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:3130;
|
||
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_cache_bypass $http_upgrade;
|
||
|
||
# AI 流式响应需禁用缓冲
|
||
proxy_buffering off;
|
||
proxy_read_timeout 300s;
|
||
}
|
||
}
|
||
```
|
||
|
||
```bash
|
||
ln -s /etc/nginx/sites-available/zhimingge /etc/nginx/sites-enabled/
|
||
nginx -t
|
||
systemctl reload nginx
|
||
|
||
# HTTPS(Let's Encrypt)
|
||
certbot --nginx -d zhimingge.example.com
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 更新部署流程
|
||
|
||
代码推送至 Gitea 后,在服务器执行:
|
||
|
||
```bash
|
||
cd /opt/zhimingge
|
||
|
||
# 拉取最新代码
|
||
git pull origin main
|
||
|
||
# 安装新依赖(如有变更)
|
||
npm ci || npm install
|
||
|
||
# 重新构建
|
||
npm run build
|
||
|
||
# 若使用 standalone 复制方式,重新复制 static/public/content
|
||
cp -r .next/static .next/standalone/.next/static
|
||
cp -r public .next/standalone/public
|
||
cp -r content .next/standalone/content
|
||
|
||
# 重启
|
||
pm2 restart zhimingge
|
||
```
|
||
|
||
可写入脚本 `/opt/zhimingge/scripts/deploy.sh` 一键执行(见项目内脚本)。
|
||
|
||
---
|
||
|
||
## 10. 防火墙
|
||
|
||
若使用 UFW:
|
||
|
||
```bash
|
||
# 仅 Nginx 对外(推荐)
|
||
ufw allow 22
|
||
ufw allow 80
|
||
ufw allow 443
|
||
ufw enable
|
||
|
||
# 若不用 Nginx,直接暴露 3130
|
||
ufw allow 3130
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 日志与排错
|
||
|
||
### PM2 日志
|
||
|
||
```bash
|
||
pm2 logs zhimingge --lines 100
|
||
```
|
||
|
||
日志文件位置(默认):
|
||
|
||
```
|
||
/root/.pm2/logs/zhimingge-out.log
|
||
/root/.pm2/logs/zhimingge-error.log
|
||
```
|
||
|
||
### 常见问题
|
||
|
||
| 现象 | 可能原因 | 处理 |
|
||
|------|----------|------|
|
||
| 502 Bad Gateway | PM2 进程未运行 | `pm2 restart zhimingge` |
|
||
| AI 解读失败 | API Key 错误或模型名不对 | 检查 `.env.local` |
|
||
| 卦辞读取失败 | `content/` 路径不存在 | 确认 `content/zhouyi/docs/` 存在 |
|
||
| 构建 OOM | 内存不足 | 增加 swap 或升级内存 |
|
||
| 流式输出中断 | Nginx 缓冲 | 设置 `proxy_buffering off` |
|
||
| Git pull 失败 | 凭据过期 | 重新配置 Token 或 SSH |
|
||
|
||
### 构建内存不足时增加 Swap
|
||
|
||
```bash
|
||
fallocate -l 2G /swapfile
|
||
chmod 600 /swapfile
|
||
mkswap /swapfile
|
||
swapon /swapfile
|
||
echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
||
```
|
||
|
||
### 测试 AI 接口
|
||
|
||
```bash
|
||
curl https://op.bz121.com/v1/models \
|
||
-H "Authorization: Bearer 你的OPENAI_API_KEY"
|
||
```
|
||
|
||
确认返回列表中包含 `huihui_ai/gemma-4-abliterated:e4b`。
|
||
|
||
---
|
||
|
||
## 12. 安全建议
|
||
|
||
虽然当前按 **root** 部署,仍建议:
|
||
|
||
1. **`.env.local` 权限** 设为 `600`,勿提交 Git
|
||
2. **API Key** 仅保存在服务器,不要写入前端代码
|
||
3. 生产环境使用 **Nginx + HTTPS**
|
||
4. 定期 `apt update` 与 `npm update`
|
||
5. 若后续改为非 root 运行,创建专用用户:
|
||
|
||
```bash
|
||
useradd -r -s /bin/false zhimingge
|
||
chown -R zhimingge:zhimingge /opt/zhimingge
|
||
# PM2 中以 zhimingge 用户启动
|
||
```
|
||
|
||
---
|
||
|
||
## 13. 快速命令参考
|
||
|
||
```bash
|
||
# 完整首次部署
|
||
cd /opt && git clone https://git.bz121.com/dekun/zhimingge.git zhimingge
|
||
cd /opt/zhimingge
|
||
cp .env.example .env.local && nano .env.local
|
||
npm ci || npm install
|
||
npm run build
|
||
pm2 start ecosystem.config.cjs
|
||
pm2 save && pm2 startup
|
||
|
||
# 日常更新
|
||
cd /opt/zhimingge && git pull && npm ci && npm run build && pm2 restart zhimingge
|
||
|
||
# 查看状态
|
||
pm2 status && curl -I http://127.0.0.1:3130
|
||
```
|
||
|
||
---
|
||
|
||
## 14. 相关文档
|
||
|
||
- 产品说明:[docs/SPEC.md](./SPEC.md)
|
||
- 环境变量示例:[../.env.example](../.env.example)
|
||
- PM2 配置:[../ecosystem.config.cjs](../ecosystem.config.cjs)
|