The backward-compat redirect from www.ityb.me/careerbot/* to career.ityb.me/careerbot/* has been removed per user request. Now all /careerbot paths on the main domain return 404, except /careerbot/uploads/* which is served by branch1's secondary mount so trunk-uploaded photo URLs still render on branch1 pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
519 lines
18 KiB
Markdown
519 lines
18 KiB
Markdown
# CareerBot 线上运维手册
|
||
|
||
## 1. 服务器信息
|
||
|
||
| 项目 | 详情 |
|
||
|------|------|
|
||
| 云服务商 | 阿里云 ECS |
|
||
| 公网 IP | `39.106.14.107` |
|
||
| 域名 | `ityb.me` / `www.ityb.me` |
|
||
| 操作系统 | Alibaba Cloud Linux 3 (OpenAnolis Edition) |
|
||
| 配置 | 2 核 CPU / 2GB 内存 / 40GB 磁盘 |
|
||
| Python 版本 | 3.11.13 |
|
||
| Nginx 版本 | 1.20.1 |
|
||
| Gitea 版本 | 1.22.6 |
|
||
|
||
---
|
||
|
||
## 2. 账号信息
|
||
|
||
### 2.1 服务器账号
|
||
|
||
| 账号 | 用途 | 登录方式 |
|
||
|------|------|---------|
|
||
| `root` | 系统管理 | SSH 密钥 (`~/.ssh/id_ed25519`) |
|
||
| `deploy` | 应用部署和运行 | SSH 密钥(同上),sudo 免密 |
|
||
| `gitea` | Gitea 服务运行 | 系统用户,不可直接登录 |
|
||
| `nginx` | Nginx 服务运行 | 系统用户,不可直接登录 |
|
||
|
||
**本地 SSH 快捷连接**(已配置在 `~/.ssh/config`):
|
||
```bash
|
||
ssh ecs # 以 deploy 用户连接
|
||
ssh root@ecs # 以 root 用户连接(需将 config 中 User 临时改为 root,或直接用 IP)
|
||
ssh root@39.106.14.107 # root 直连
|
||
```
|
||
|
||
### 2.2 应用账号
|
||
|
||
| 系统 | 地址 | 账号 | 密码 |
|
||
|------|------|------|------|
|
||
| CareerBot 展示版(branch1) | `https://www.ityb.me/` | (访问令牌或匿名) | - |
|
||
| CareerBot 完整版(trunk) | `https://career.ityb.me/careerbot/` | (访问令牌或匿名) | - |
|
||
| CareerBot 管理后台 | `https://career.ityb.me/careerbot/admin/login` | `ln0422@gmail.com` | `qshs123456` |
|
||
| Gitea 代码管理 | `https://git.ityb.me` | `ln0422` | `Qshs123456_` |
|
||
|
||
### 2.3 ECS 用户密码
|
||
|
||
| 用户 | 密码 |
|
||
|------|------|
|
||
| `deploy` | `CareerBot2026!` |
|
||
|
||
> **注意**:日常运维通过 SSH 密钥登录,密码仅在密钥不可用时作为备用。
|
||
|
||
---
|
||
|
||
## 3. 服务架构
|
||
|
||
```
|
||
外部访问
|
||
│
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────┐
|
||
│ Nginx (:80 / :443) │
|
||
│ 配置: /etc/nginx/sites-enabled/ │
|
||
│ 日志: /var/log/nginx/ │
|
||
├────────────────────────────────────────────────────────────────┤
|
||
│ https://www.ityb.me/ (主域,展示版 branch1) │
|
||
│ https://ityb.me/ ┤ │
|
||
│ → proxy_pass http://127.0.0.1:8001/ │
|
||
│ → CareerBot branch1 (uvicorn, BASE_PATH="", FAB 隐藏) │
|
||
│ │
|
||
│ https://www.ityb.me/careerbot/* → 404(已主动屏蔽) │
|
||
│ 仅 /careerbot/uploads/* 由 branch1 后端兜底(供 trunk 上传 │
|
||
│ 的头像 URL 在 branch1 页面正常显示) │
|
||
├────────────────────────────────────────────────────────────────┤
|
||
│ https://career.ityb.me/careerbot/ (完整版 trunk) │
|
||
│ → proxy_pass http://127.0.0.1:8000/careerbot/ │
|
||
│ → CareerBot main (uvicorn, BASE_PATH="/careerbot", 完整功能) │
|
||
├────────────────────────────────────────────────────────────────┤
|
||
│ https://git.ityb.me │
|
||
│ → proxy_pass http://127.0.0.1:3000 │
|
||
│ → Gitea │
|
||
│ │
|
||
│ http://39.106.14.107/careerbot/ (IP 直连兼容 trunk) │
|
||
│ http://39.106.14.107:3000 (IP 直连兼容 Gitea) │
|
||
└────────────────────────────────────────────────────────────────┘
|
||
|
||
两个 CareerBot 实例共享同一 DB + uploads:
|
||
/home/deploy/apps/CareerBot/careerbot.db ← 权威数据源
|
||
/home/deploy/apps/CareerBot/uploads/ ← 权威上传目录
|
||
branch1 通过 systemd 环境变量 DATABASE_URL / UPLOAD_DIR 指向这里
|
||
```
|
||
|
||
### 端口使用
|
||
|
||
| 端口 | 服务 | 监听地址 | 防火墙 | 安全组 |
|
||
|------|------|---------|--------|--------|
|
||
| 22 | SSH | 0.0.0.0 | 已放行 | 已放行 |
|
||
| 80 | Nginx (HTTP → HTTPS) | 0.0.0.0 | 已放行 | 已放行 |
|
||
| 443 | Nginx (HTTPS) | 0.0.0.0 | 已放行 | 已放行 |
|
||
| 3000 | Gitea | 0.0.0.0 | 已放行 | 已放行 |
|
||
| 8000 | CareerBot trunk (uvicorn) | 127.0.0.1 | 内部 | 无需 |
|
||
| 8001 | CareerBot branch1 (uvicorn) | 127.0.0.1 | 内部 | 无需 |
|
||
|
||
---
|
||
|
||
## 4. 文件目录结构
|
||
|
||
```
|
||
/home/deploy/apps/CareerBot/ ← Trunk 项目根目录(main 分支,:8000)
|
||
├── venv/ ← Python 虚拟环境
|
||
├── app/ ← 应用代码
|
||
├── templates/ ← 页面模板
|
||
├── careerbot.db ← SQLite 数据库(主数据源,branch1 也读写这里)
|
||
├── uploads/ ← 用户上传文件(主目录,branch1 也访问这里)
|
||
├── requirements.txt
|
||
├── init_data.py
|
||
├── DESIGN.md ← 设计文档
|
||
└── OPS_MANUAL.md ← 本文档
|
||
|
||
/home/deploy/apps/CareerBot-branch1/ ← Branch1 项目根目录(branch1 分支,:8001)
|
||
├── venv/ ← 独立虚拟环境
|
||
├── app/ ← BASE_PATH="" 的代码
|
||
├── templates/ ← 隐藏 FAB 的 index.html
|
||
└── (无独立 careerbot.db,通过环境变量指向 trunk 的)
|
||
|
||
/etc/systemd/system/
|
||
├── careerbot.service ← CareerBot trunk systemd 服务
|
||
├── careerbot-branch1.service ← CareerBot branch1 systemd 服务
|
||
└── gitea.service ← Gitea systemd 服务
|
||
|
||
/etc/nginx/
|
||
├── nginx.conf ← Nginx 主配置
|
||
├── sites-available/
|
||
│ ├── careerbot.conf ← CareerBot 站点配置
|
||
│ └── gitea.conf ← Gitea 站点配置
|
||
└── sites-enabled/
|
||
├── careerbot.conf → ../sites-available/careerbot.conf
|
||
└── gitea.conf → ../sites-available/gitea.conf
|
||
|
||
/usr/local/bin/gitea ← Gitea 二进制文件
|
||
/etc/gitea/app.ini ← Gitea 配置文件
|
||
/var/lib/gitea/ ← Gitea 数据目录(仓库、数据库)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 常用运维命令
|
||
|
||
### 5.1 服务管理
|
||
|
||
```bash
|
||
# ── CareerBot ──
|
||
sudo systemctl status careerbot # 查看状态
|
||
sudo systemctl restart careerbot # 重启
|
||
sudo systemctl stop careerbot # 停止
|
||
sudo systemctl start careerbot # 启动
|
||
sudo journalctl -u careerbot -f # 实时查看日志
|
||
sudo journalctl -u careerbot --since "1 hour ago" # 查看最近1小时日志
|
||
|
||
# ── Nginx ──
|
||
sudo systemctl status nginx
|
||
sudo systemctl reload nginx # 重载配置(不中断连接)
|
||
sudo systemctl restart nginx # 重启
|
||
sudo nginx -t # 测试配置文件语法
|
||
|
||
# ── Gitea ──
|
||
sudo systemctl status gitea
|
||
sudo systemctl restart gitea
|
||
sudo journalctl -u gitea -f
|
||
```
|
||
|
||
### 5.2 代码更新部署
|
||
|
||
**从本地推送代码并部署到线上(标准流程):**
|
||
|
||
```bash
|
||
# 1. 本地:提交并推送到 Gitea
|
||
cd D:/Files/Projects/VibeCoding/CareerBot
|
||
git add .
|
||
git commit -m "描述改动"
|
||
git push origin main
|
||
|
||
# 2. 线上:拉取并重启
|
||
ssh ecs "cd ~/apps/CareerBot && git pull && sudo systemctl restart careerbot"
|
||
```
|
||
|
||
**一键部署脚本(在本地执行):**
|
||
```bash
|
||
ssh ecs "cd ~/apps/CareerBot && git pull origin main && sudo systemctl restart careerbot && sleep 2 && sudo systemctl is-active careerbot"
|
||
```
|
||
|
||
### 5.3 数据库操作
|
||
|
||
```bash
|
||
# 连接到 SQLite 数据库
|
||
ssh ecs "cd ~/apps/CareerBot && sqlite3 careerbot.db"
|
||
|
||
# 常用 SQL
|
||
.tables # 查看所有表
|
||
SELECT * FROM admin_user; # 查看管理员
|
||
SELECT token, note, max_questions, used_questions, is_active FROM access_token; # 查看令牌
|
||
SELECT * FROM recruiter_message ORDER BY created_at DESC LIMIT 10; # 最近招聘消息
|
||
SELECT COUNT(*) FROM chat_history; # 对话记录总数
|
||
.quit # 退出
|
||
```
|
||
|
||
### 5.4 系统监控
|
||
|
||
```bash
|
||
# 查看内存使用
|
||
ssh ecs "free -h"
|
||
|
||
# 查看磁盘使用
|
||
ssh ecs "df -h /"
|
||
|
||
# 查看各服务内存占用
|
||
ssh ecs "ps aux --sort=-rss | head -10"
|
||
|
||
# 查看端口监听
|
||
ssh ecs "ss -tlnp"
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 配置文件详情
|
||
|
||
### 6.1 CareerBot systemd 服务
|
||
|
||
**Trunk(main 分支)**:`/etc/systemd/system/careerbot.service`
|
||
```ini
|
||
[Unit]
|
||
Description=CareerBot Web Application
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=deploy
|
||
WorkingDirectory=/home/deploy/apps/CareerBot
|
||
ExecStart=/home/deploy/apps/CareerBot/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
|
||
Restart=always
|
||
RestartSec=5
|
||
Environment=PATH=/home/deploy/apps/CareerBot/venv/bin:/usr/bin
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
**Branch1(branch1 分支,共享 trunk 数据)**:`/etc/systemd/system/careerbot-branch1.service`
|
||
```ini
|
||
[Unit]
|
||
Description=CareerBot branch1 (display-only, shared DB with trunk)
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=deploy
|
||
WorkingDirectory=/home/deploy/apps/CareerBot-branch1
|
||
ExecStart=/home/deploy/apps/CareerBot-branch1/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8001
|
||
Restart=always
|
||
Environment=PATH=/home/deploy/apps/CareerBot-branch1/venv/bin:/usr/bin
|
||
Environment=DATABASE_URL=sqlite:////home/deploy/apps/CareerBot/careerbot.db
|
||
Environment=UPLOAD_DIR=/home/deploy/apps/CareerBot/uploads
|
||
Environment=BASE_PATH=
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
关键差异:
|
||
- **端口**:trunk 用 8000,branch1 用 8001
|
||
- **数据共享**:branch1 通过 `DATABASE_URL` 和 `UPLOAD_DIR` 环境变量指向 trunk 的数据库和上传目录
|
||
- **BASE_PATH**:branch1 设置为空串,应用部署在根路径
|
||
- **FAB 显示**:branch1 的 `templates/index.html` 已修改,对话按钮 `style="display:none;"`
|
||
- `Restart=always`:崩溃后自动重启
|
||
|
||
### 6.2 Nginx 站点配置
|
||
|
||
文件:`/etc/nginx/sites-available/careerbot.conf`(含三段 server block + certbot 自动追加的 HTTPS 块)
|
||
|
||
```nginx
|
||
# ===== TRUNK: career.ityb.me (完整版,/careerbot/ 子路径) =====
|
||
server {
|
||
server_name career.ityb.me;
|
||
client_max_body_size 10M;
|
||
|
||
location /careerbot/ {
|
||
proxy_pass http://127.0.0.1:8000/careerbot/;
|
||
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_buffering off; proxy_cache off; proxy_read_timeout 300s;
|
||
}
|
||
location = / { return 302 /careerbot/; }
|
||
|
||
listen 443 ssl; # by certbot
|
||
ssl_certificate /etc/letsencrypt/live/career.ityb.me/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/career.ityb.me/privkey.pem;
|
||
}
|
||
|
||
# ===== BRANCH1: www.ityb.me + ityb.me (展示版,根路径) =====
|
||
server {
|
||
listen 80;
|
||
server_name www.ityb.me ityb.me;
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
server {
|
||
listen 443 ssl;
|
||
server_name www.ityb.me ityb.me;
|
||
client_max_body_size 10M;
|
||
|
||
ssl_certificate /etc/letsencrypt/live/www.ityb.me/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/www.ityb.me/privkey.pem;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8001/; # branch1
|
||
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_buffering off; proxy_cache off; proxy_read_timeout 300s;
|
||
}
|
||
}
|
||
|
||
# ===== IP 直连(向后兼容,HTTP only) =====
|
||
server {
|
||
listen 80 default_server;
|
||
server_name 39.106.14.107;
|
||
client_max_body_size 10M;
|
||
|
||
location /careerbot/ {
|
||
proxy_pass http://127.0.0.1:8000/careerbot/;
|
||
# ... 同 trunk ...
|
||
}
|
||
location = / { return 302 /careerbot/; }
|
||
}
|
||
```
|
||
|
||
关键配置说明:
|
||
- **双域双实例**:career 子域名走 trunk(:8000),主域名走 branch1(:8001)
|
||
- **SSL 证书**:`career.ityb.me` 和 `www.ityb.me+ityb.me` 分别独立证书(均由 certbot 自动申请续期)
|
||
- **主域路径隔离**:`www.ityb.me/careerbot/*` 返回 404(nginx 层不做任何特殊处理,请求透传到 branch1 后端,branch1 没有 /careerbot 前缀路由因此 404);仅 `/careerbot/uploads/*` 能命中 branch1 的二级挂载,用于显示 trunk 管理员上传的头像
|
||
- `proxy_buffering off` + `proxy_cache off`:**必须关闭**,否则 SSE 流式对话无法实时返回
|
||
- `proxy_read_timeout 300s`:LLM 长回复可能需要较长时间
|
||
|
||
### 6.3 Gitea 站点配置
|
||
|
||
文件:`/etc/nginx/sites-available/gitea.conf`(HTTPS 部分由 certbot 自动生成)
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name git.ityb.me;
|
||
client_max_body_size 512M;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:3000;
|
||
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;
|
||
}
|
||
|
||
# certbot 自动追加 443 监听块 + 80→443 重定向
|
||
listen 443 ssl;
|
||
ssl_certificate /etc/letsencrypt/live/git.ityb.me/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/git.ityb.me/privkey.pem;
|
||
}
|
||
```
|
||
|
||
关键说明:
|
||
- `client_max_body_size 512M`:支持大仓库 push/pull
|
||
- Gitea 仍在本机 3000 端口监听,Nginx 反向代理到 443(HTTPS)
|
||
- 端口 3000 保持对外开放作为备用访问方式
|
||
- 对应 Gitea 配置 `/etc/gitea/app.ini`:`ROOT_URL=https://git.ityb.me/`、`DOMAIN=git.ityb.me`、`SSH_DOMAIN=git.ityb.me`
|
||
|
||
### 6.3 防火墙规则
|
||
|
||
```bash
|
||
# 查看当前规则
|
||
sudo firewall-cmd --list-ports
|
||
# 输出: 22/tcp 80/tcp 443/tcp 3000/tcp
|
||
|
||
# 添加新端口(如未来部署新项目)
|
||
sudo firewall-cmd --permanent --add-port=PORT/tcp
|
||
sudo firewall-cmd --reload
|
||
```
|
||
|
||
> **注意**:开放端口还需要在阿里云控制台的**安全组**中同步添加入方向规则,否则外部仍无法访问。
|
||
|
||
---
|
||
|
||
## 7. 备份策略
|
||
|
||
### 7.1 数据库备份
|
||
|
||
CareerBot 的所有业务数据存储在 SQLite 文件中:
|
||
|
||
```bash
|
||
# 手动备份
|
||
ssh ecs "cp ~/apps/CareerBot/careerbot.db ~/apps/CareerBot/careerbot.db.bak.$(date +%Y%m%d)"
|
||
|
||
# 下载到本地
|
||
scp ecs:~/apps/CareerBot/careerbot.db ./careerbot_backup_$(date +%Y%m%d).db
|
||
```
|
||
|
||
### 7.2 Gitea 数据备份
|
||
|
||
```bash
|
||
# Gitea 数据和仓库
|
||
ssh ecs "sudo tar czf /tmp/gitea-backup.tar.gz /var/lib/gitea /etc/gitea"
|
||
scp ecs:/tmp/gitea-backup.tar.gz ./gitea-backup.tar.gz
|
||
```
|
||
|
||
### 7.3 需要备份的关键文件
|
||
|
||
| 文件/目录 | 说明 | 重要程度 |
|
||
|-----------|------|---------|
|
||
| `~/apps/CareerBot/careerbot.db` | CareerBot 全部业务数据 | **极高** |
|
||
| `~/apps/CareerBot/uploads/` | 用户上传的文件 | 高 |
|
||
| `/var/lib/gitea/` | Gitea 所有仓库和数据 | **极高** |
|
||
| `/etc/gitea/app.ini` | Gitea 配置 | 中 |
|
||
| `/etc/nginx/sites-available/` | Nginx 站点配置 | 中 |
|
||
| `/etc/systemd/system/careerbot.service` | 服务配置 | 低(可重建) |
|
||
|
||
---
|
||
|
||
## 8. 故障排查
|
||
|
||
### 8.1 CareerBot 无法访问
|
||
|
||
```bash
|
||
# 1. 检查服务状态
|
||
sudo systemctl status careerbot
|
||
|
||
# 2. 查看错误日志
|
||
sudo journalctl -u careerbot --since "10 min ago"
|
||
|
||
# 3. 检查端口
|
||
ss -tlnp | grep 8000
|
||
|
||
# 4. 检查 Nginx
|
||
sudo nginx -t
|
||
sudo systemctl status nginx
|
||
|
||
# 5. 手动启动测试
|
||
cd ~/apps/CareerBot
|
||
source venv/bin/activate
|
||
uvicorn app.main:app --host 127.0.0.1 --port 8000
|
||
```
|
||
|
||
### 8.2 AI 对话不工作
|
||
|
||
- 检查管理后台(`http://www.ityb.me/careerbot/admin/llm-config`)LLM Config 是否已配置(API URL、API Key、Model Name)
|
||
- 点击 "Test Connection" 测试连通性
|
||
- 确认 ECS 能访问外网:`curl https://api.deepseek.com`
|
||
|
||
### 8.3 Gitea 无法访问
|
||
|
||
```bash
|
||
sudo systemctl status gitea
|
||
sudo journalctl -u gitea --since "10 min ago"
|
||
# 检查 3000 端口和安全组
|
||
```
|
||
|
||
### 8.4 磁盘/内存不足
|
||
|
||
```bash
|
||
# 查看磁盘
|
||
df -h /
|
||
|
||
# 清理日志
|
||
sudo journalctl --vacuum-time=7d
|
||
|
||
# 查看内存
|
||
free -h
|
||
|
||
# 查看占用最多的进程
|
||
ps aux --sort=-rss | head -10
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 新项目部署指南
|
||
|
||
后续在同一台 ECS 上部署新项目的标准流程:
|
||
|
||
1. **Gitea 上创建仓库**:`https://git.ityb.me` → New Repository
|
||
|
||
2. **本地推送代码**
|
||
3. **ECS 上克隆并配置**:
|
||
```bash
|
||
cd ~/apps
|
||
git clone https://git.ityb.me/ln0422/新项目.git
|
||
# 安装依赖、初始化等
|
||
```
|
||
|
||
4. **创建 systemd 服务**(参考 careerbot.service,注意更换端口号)
|
||
|
||
5. **添加 Nginx 站点配置**(在同一个 `careerbot.conf` 文件或新建独立 conf 文件中,添加新的 `location /新路径/` 块):
|
||
```bash
|
||
sudo vim /etc/nginx/sites-available/新项目.conf
|
||
sudo ln -s /etc/nginx/sites-available/新项目.conf /etc/nginx/sites-enabled/
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
```
|
||
> 建议参考 CareerBot 的子路径部署模式:`location /新路径/ { proxy_pass http://127.0.0.1:新端口/新路径/; }`
|
||
|
||
6. **如需新端口**:防火墙 + 阿里云安全组同步放行
|
||
|
||
---
|
||
|
||
## 10. 安全注意事项
|
||
|
||
1. **SSH 密钥**:本地私钥 `~/.ssh/id_ed25519` 请妥善保管,丢失需重新生成并更新服务器
|
||
2. **Gitea 端口**:3000 端口对外开放,建议设置强密码,或后续通过 Nginx 代理并限制访问
|
||
3. **数据库文件**:`careerbot.db` 不应对外暴露,uvicorn 仅监听 127.0.0.1 已确保安全
|
||
4. **定期更新**:定期执行 `sudo dnf update -y` 更新系统安全补丁
|
||
5. **HTTPS**:域名 `ityb.me` 已绑定,建议配置 Let's Encrypt SSL 证书(`certbot --nginx -d www.ityb.me -d ityb.me`)
|