154 lines
5.9 KiB
Markdown
154 lines
5.9 KiB
Markdown
# deploy-ai.sh — tlyq.ai 站点部署脚本
|
||
|
||
## 概述
|
||
|
||
`deploy-ai.sh` 将本地代码部署到 tlyq.ai 云服务器(txjp,IP: 43.133.38.210)。采用"源码上传 → 服务器构建"模式,本地不做生产构建。
|
||
|
||
**核心流程**:打包源码 → 上传至 txjp → 服务器 `npm install` + `npm run build` → 容器重启生效
|
||
|
||
## 用法
|
||
|
||
```bash
|
||
bash scripts/deploy-ai.sh [选项]
|
||
```
|
||
|
||
| 选项 | 说明 |
|
||
|------|------|
|
||
| 无参数 | 自动检测:初次构建 or 增量更新(源码快照比对) |
|
||
| `--force` | 强制完整构建(跳过快照比对) |
|
||
| `--restart` | 仅重启容器(跳过构建) |
|
||
| `--init` | 强制初次完整构建 |
|
||
|
||
## 站点选择
|
||
|
||
脚本启动后显示交互式菜单:
|
||
|
||
| 编号 | 站点 | 容器 | 构建方式 |
|
||
|------|------|------|---------|
|
||
| 1 | www.tlyq.ai | — | Next.js `output: 'export'` 静态站点 |
|
||
| 2 | cloud.tlyq.ai | — | 纯静态 HTML |
|
||
| 3 | token.tlyq.ai | — | 纯静态 HTML |
|
||
| 4 | issue.tlyq.ai | issue-ai | Next.js standalone(Docker) |
|
||
| 5 | assets.tlyq.ai | assets-ai | Next.js standalone(Docker) |
|
||
| 6 | oa.tlyq.ai | oa-ai | Next.js standalone(Docker) |
|
||
|
||
## 部署流程
|
||
|
||
### 通用步骤
|
||
|
||
1. **链接替换**:`localhost` → 正式域名(www/cloud/token 站点;OA 站点额外替换内部 API 地址)
|
||
2. **快照比对**:计算本地源码 MD5,与服务器上次快照对比,无变化则跳过构建
|
||
3. **打包上传**:tar 打包(排除 `node_modules`、`.next`、`.env` 等),scp 上传至 txjp
|
||
4. **解压同步**:rsync 同步源码到目标目录
|
||
5. **依赖安装**:服务器 `npm install --prefer-offline`
|
||
6. **环境变量**:写入生产环境所需的环境变量到 `.env`
|
||
7. **构建**:服务器 `npm run build`
|
||
8. **清理**:删除 Alpine musl 残留(sharp、swc)
|
||
9. **重启容器**:`docker compose up -d` + `docker compose restart`
|
||
10. **快照保存**:保存本次源码 MD5 用于下次比对
|
||
|
||
### issue-ai 专属
|
||
|
||
- 构建前写入 `ASSETS_API_URL=http://assets-ai:3000/api`
|
||
- 构建前写入 `NEXT_PUBLIC_ASSETS_URL=https://assets.tlyq.ai`
|
||
- **部署后自动验证 issue→assets API 连通性**(见下方)
|
||
|
||
### assets-ai 专属
|
||
|
||
- 构建前写入 `NEXT_PUBLIC_ISSUE_URL=https://issue.tlyq.ai/tickets`
|
||
- 构建前写入 `ISSUE_API_URL=http://issue-ai:3000/api`
|
||
|
||
### OA 专属
|
||
|
||
- 替换三类 URL:前端导航卡片(`localhost` → 生产域名)、服务端内部 API(`localhost` → 容器名)、退出登录重定向
|
||
|
||
## 部署后验证
|
||
|
||
### 部署后 API 连通性检查
|
||
|
||
部署 issue-ai 和 assets-ai 后自动执行双向连通性检查,均带 **重试机制**(3 次、间隔 10s),应对同时部署两个站点时目标容器暂不可达的情况。
|
||
|
||
| 部署站点 | 检查方向 | 使用 Key | 目标 URL |
|
||
|---------|---------|---------|----------|
|
||
| issue-ai | issue → assets | `ASSETS_API_KEY` | `ASSETS_API_URL/assets?pageSize=1` |
|
||
| assets-ai | assets → issue | `ISSUE_API_KEY` | `ISSUE_API_URL/tickets?pageSize=1` |
|
||
|
||
流程:
|
||
1. 将连通性检查脚本发送到 txjp 宿主机
|
||
2. `docker cp` 到对应容器内
|
||
3. 容器内 Node.js 发起 HTTP 请求,失败自动重试(最多 3 次,间隔 10s)
|
||
4. 返回 200 → 通过;全部重试仍失败 → **部署失败退出**
|
||
|
||
**错误输出示例**:
|
||
```
|
||
[✗] issue→assets API 连通性检查失败!请检查 ASSETS_API_KEY 是否在 assets-ai 中注册
|
||
连接失败: HTTP 401 (第1次)
|
||
10s 后重试...
|
||
连通失败: HTTP 401 (第2次)
|
||
已达最大重试次数
|
||
```
|
||
|
||
## 排除文件
|
||
|
||
打包时排除以下目录和文件,不上传到服务器:
|
||
|
||
| 类别 | 排除项 |
|
||
|------|--------|
|
||
| 依赖 | `node_modules` |
|
||
| 构建产物 | `.next`、`out`、`data` |
|
||
| 环境配置 | `.env`、`.env.local`、`.env.development`、`.env.production` |
|
||
| 版本控制 | `.git` |
|
||
| 文档 | `docs` |
|
||
| 报告 | `reports/*.docx` |
|
||
| macOS | `._*` |
|
||
|
||
部署后服务器上的 `.env.local` / `.env.development` / `.env.production` 会被自动删除,防止覆盖 `.env` 中的生产配置。
|
||
|
||
## 源码快照机制
|
||
|
||
脚本计算本地源码文件的组合 MD5,保存到服务器的 `/tmp/.snapshot.{site}.md5`。下次部署时比对,若无变化则跳过构建仅重启容器。`--force` 可绕过此机制。
|
||
|
||
快照包含:所有源码文件(排除 `node_modules`、`.next`、`out`、`data`、`reports`、`docs`、`.env*`、`.git`)。
|
||
|
||
## 故障排查
|
||
|
||
### 构建失败
|
||
```bash
|
||
# 查看服务器构建日志
|
||
ssh txjp "tail -100 /tmp/build_output.txt"
|
||
```
|
||
|
||
### 容器启动失败
|
||
```bash
|
||
ssh txjp "docker logs {container}"
|
||
```
|
||
|
||
### 连通性检查失败
|
||
```bash
|
||
# 手动验证 issue→assets 连通性
|
||
ssh txjp "docker exec issue-ai sh -c 'cd /app && NODE_PATH=/app/node_modules node -e \"
|
||
const http = require(\\\"http\\\");
|
||
const url = process.env.ASSETS_API_URL + \\\"/assets?pageSize=1\\\";
|
||
const key = process.env.ASSETS_API_KEY || \\\"\\\";
|
||
http.get(url, {headers:{\\\"Authorization\\\":\\\"Bearer \\\"+key}}, (res) => {
|
||
console.log(\\\"status:\\\", res.statusCode);
|
||
process.exit(res.statusCode === 200 ? 0 : 1);
|
||
}).on(\\\"error\\\", (e) => { console.error(e.message); process.exit(1); });
|
||
\"'"
|
||
|
||
# 确认 assets-ai 的 api_keys 表中是否有对应的 key
|
||
ssh txjp "docker exec assets-ai node -e \"
|
||
const db = require('better-sqlite3')('/app/data/assets.db');
|
||
const rows = db.prepare('SELECT id, name, is_active FROM api_keys').all();
|
||
rows.forEach(r => console.log(JSON.stringify(r)));
|
||
\""
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
- **跨容器凭据**:禁止在代码或配置中硬编码另一服务的密码,通过运行时读取源容器环境变量获取
|
||
- **共享 JWT 密钥**:所有 `.tlyq.ai` 子站点的 `JWT_SECRET` 和 `COOKIE_DOMAIN` 必须一致
|
||
- **日期时区**:系统统一 UTC+8,禁止使用 `toISOString()` 和 `datetime('now')`
|
||
- **nginx 重载**:容器重启后需 `docker restart nginx-ai` 清除 DNS 缓存
|
||
- **新增 npm 依赖**:需重建 Docker 镜像(`docker compose build --no-cache`),因为容器内 `/app/node_modules/` 来自镜像构建时
|