guanghulab/docs/ops/forgejo-deployment-experience.md
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

329 lines
9.8 KiB
Markdown
Raw Permalink 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.

# Forgejo 部署与排障经验库
> 光湖语言世界代码仓库guanghulab.com从 CVM 迁移到广州轻量云服务器的完整踩坑记录。
> 换服务器或遇到类似问题时,先看这个文档,不用重新推演。
---
## 核心:一键部署脚本
优先使用仓库里的 `scripts/smart-deploy-forgejo.sh`
这个脚本做了智能检测Docker可用性、COS配置、已有Forgejo状态一条命令跑完全部部署流程。
换服务器时的第一步。
---
## 二进制部署Docker 不可用的替代方案)
当 Docker pull 失败429限流或被墙直接下载 Gitea 二进制:
```bash
# 下载 Gitea 1.23.7Forgejo 的底层引擎)
wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/1.23.7/gitea-1.23.7-linux-amd64
chmod +x /usr/local/bin/gitea
# 创建专用用户
adduser --disabled-login --gecos "" git
# 目录结构
mkdir -p /opt/forgejo/custom/conf /opt/forgejo/data
chown -R git:git /opt/forgejo
```
### app.ini 配置
`/opt/forgejo/custom/conf/app.ini`
```ini
APP_NAME = 光湖 · Forgejo
RUN_USER = git
RUN_MODE = prod
[database]
DB_TYPE = sqlite3
PATH = /opt/forgejo/data/forgejo.db
[server]
DOMAIN = guanghulab.com
HTTP_PORT = 3001
ROOT_URL = https://guanghulab.com/
HTTP_ADDR = 127.0.0.1
LANDING_PAGE = explore
OFFLINE_MODE = false
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
[mailer]
ENABLED = false
[session]
PROVIDER = file
[log]
MODE = console
LEVEL = info
ROOT_PATH = /opt/forgejo/data/log
```
> **注意**`ROOT_URL` 必须用 `https://`,否则克隆链接会显示 `http://`。
### systemd 服务
`/etc/systemd/system/forgejo.service`
```ini
[Unit]
Description=Forgejo (Git with a cup of tea)
After=network.target
[Service]
ExecStart=/usr/local/bin/gitea web -c /opt/forgejo/custom/conf/app.ini --work-path /opt/forgejo
Restart=always
User=git
Group=git
WorkingDirectory=/opt/forgejo
[Install]
WantedBy=multi-user.target
```
```bash
systemctl daemon-reload
systemctl enable --now forgejo
systemctl status forgejo
```
---
## 常见问题与排查流程
### 1. Forgejo 拒绝以 root 运行
**现象**:启动报错 "Forgejo/Gitea refuses to run as root"
**原因**Forgejo/Gitea 安全机制禁止 root 运行。
**解决**
- 直接用 `sudo -u git /usr/local/bin/gitea ...` 启动
- 或者在 systemd 里设 `User=git`
---
### 2. Git 推送失败HTTP 413 Request Entity Too Large
**现象**
```
error: RPC failed; HTTP 413 curl 22 The requested URL returned error: 413
send-pack: unexpected disconnect while reading sideband packet
```
**排查流程**
1. 确认 Nginx 配置中有 `client_max_body_size``cat /etc/nginx/sites-available/guanghulab`
2. 默认 Nginx 限制 1MBGit 推送的 packfile 远超此值
3. 检查配置是否写在正确的 server block 里certbot 可能创建了多个配置)
**解决**
```bash
# 在 location / 块中添加 client_max_body_size
sudo sed -i '/location \/ {/a \ client_max_body_size 100M;' /etc/nginx/sites-available/guanghulab
# 重载 Nginx
sudo systemctl reload nginx
```
**验证**`git push` 不再报 413 即为成功。
---
### 3. 仓库数据损坏:推送成功但网页不显示文件
**现象**
- `git push` 返回成功(`[new branch] main -> main`
- Forgejo 网页显示仓库名但看不到文件
- 再次推送报错:`Gitea: Internal Server Error Decoding Failed` + `pre-receive hook declined`
- 远程 HEAD 不一致:`git ls-remote` 显示的 hash 与本地不同
**根本原因**:从 COS 备份恢复旧 Gitea 数据时Git 内部数据损坏或版本不兼容。
**排查流程**
1. `git ls-remote origin HEAD main` — 检查远程 HEAD 是否匹配本地
2. `curl -u user:token https://domain.com/api/v1/user/repos` — 查看仓库状态empty、size
3. 尝试推送观察具体错误信息
**解决**
```bash
# 1. 通过 API 删除损坏仓库
curl -s -X DELETE -u "bingshuo:ACCESS_TOKEN" "https://guanghulab.com/api/v1/repos/bingshuo/guanghulab"
# 2. 创建新空仓库
curl -s -X POST -u "bingshuo:ACCESS_TOKEN" \
"https://guanghulab.com/api/v1/user/repos" \
-H "Content-Type: application/json" \
-d '{"name":"guanghulab","description":"光湖代码仓库·国内镜像","private":false,"auto_init":false}'
# 3. 从本地推送完整代码
git push https://user:token@guanghulab.com/user/repo.git main
```
---
### 4. SSL 证书配置
```bash
# 申请证书
sudo certbot --nginx -d guanghulab.com
# 验证自动续期
sudo certbot renew --dry-run
# 证书位置
/etc/letsencrypt/live/guanghulab.com-0001/
```
certbot 会自动在 Nginx 配置中添加 `listen 443 ssl` 和 HTTP→HTTPS 重定向。
---
### 5. COS腾讯云对象存储备份恢复
```bash
# 安装 coscmd
pip3 install coscmd
# 配置
coscmd config -a {secret_id} -s {secret_key} -b {bucket_name} -r ap-guangzhou
# 列出文件
coscmd list
# 下载备份
coscmd download backups/guanghu-old-backup-20260512.tar.gz /root/forgejo-backup.tar.gz
# 恢复到 Forgejo 目录
tar -xzf /root/forgejo-backup.tar.gz -C /opt/forgejo/
chown -R git:git /opt/forgejo/
```
> **注意**COS 存储桶名称:`zy-team-hub-1317346199`,区域:`ap-guangzhou`。
> 备份文件在 `backups/` 目录下。
---
### 6. 验证部署完整性
**确认服务运行**
```bash
systemctl status forgejo # 服务 active
curl -I http://127.0.0.1:3001 # 返回 200
curl -I https://guanghulab.com # HTTPS 正常
```
**确认仓库状态**
- Forgejo 网页登录 → 仓库列表有数据
- `git ls-remote https://guanghulab.com/bingshuo/guanghulab.git HEAD` → 返回正确 commit
- Forgejo API `GET /api/v1/user/repos` → 返回仓库列表
---
## 部署流程速查
从零开始部署的完整流程(假设已有 COS 备份):
1. **执行 `scripts/smart-deploy-forgejo.sh`** 或手动部署二进制
2. **从 COS 下载备份并恢复**
3. **配置 Nginx 反向代理**(脚本已自动处理)
4. **申请 SSL 证书**`certbot --nginx`
5. **启动 Forgejo 并验证**
6. **如果仓库损坏** → 删除仓库,重建,重新推送
7. **如果推送报 413** → 配置 Nginx `client_max_body_size 100M`
---
## 注意事项
- **不要用 root 直接运行 Forgejo** — 必须用 git 用户
- **Nginx 配置改动后必须 reload** — `sudo systemctl reload nginx`
- **备份恢复后检查仓库完整性** — 用 `git ls-remote` 对比本地 hash
- **ROOT_URL 要写 https** — 否则克隆链接显示 http://
- **certbot 会修改 Nginx 配置** — 注意它创建的是独立的 server block
---
### MCP Server 配置
MCP Server 迁移到新服务器后需要手动启动:
```nginx
# Nginx location block放在 location / 前面)
location /mcp {
proxy_pass http://127.0.0.1:8083;
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 /repo-mcp {
rewrite ^/repo-mcp$ /mcp break;
proxy_pass http://127.0.0.1:3903;
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;
}
```
**启动步骤**
1. 克隆仓库:`cd /opt && git clone https://user:token@guanghulab.com/bingshuo/guanghulab.git repo-name`
2. 启动 guanghulab-mcp-server端口 8083
- `cd /opt/repo-name/server/mcp-server && npm install`
- 创建密钥:`echo "TOKEN" > /data/guanghulab/mcp-server/.access-key`
- `pm2 start ecosystem.config.js`
3. 启动 repo-mcp-server端口 3903
- `cd /opt/repo-name/mcp-servers/repo-mcp-server && npm install`
- `REPO_MCP_PORT=3903 REPO_MCP_SECRET=SECRET pm2 start index.js --name repo-mcp-server`
4. 验证:`curl -X POST -H "Authorization: Bearer TOKEN" https://guanghulab.com/mcp`
**常见问题**
- **Nginx location 嵌套**sed 拼接容易把 location 块塞进其他 block 里,直接 cat 覆盖最稳
- **路径重写**repo-mcp-server 后端路由是 `/mcp`,但对外暴露 `/repo-mcp`Nginx 需要 `rewrite ^/repo-mcp$ /mcp break;`
- **access key 路径**`ecosystem.config.js` 写死了 `/data/guanghulab/mcp-server/.access-key`,需检查是否存在
- **PM2 进程冲突**:多次启动同一进程名会创建重复实例,用 `pm2 delete ID` 清除 errored 的
## 2026-05-16 实测记录
本次迁移完整时间线:
| 时间 | 事件 | 结果 |
|------|------|------|
| 14:30 | 从 COS 下载 44MB 备份 | ✅ |
| 14:45 | 解压恢复 forgejo.db + 仓库 | ✅ |
| 15:00 | 下载 Gitea 1.23.7 二进制 | ✅ |
| 15:20 | Nginx 反向代理 + SSL 证书 | ✅ |
| 15:50 | 域名解析切换到广州服务器 | ✅ |
| 16:37 | 推送代码到新仓库 | ⚠️ 推送成功但网页不显示 |
| 16:50 | 发现仓库数据损坏 | ❌ Internal Server Error |
| 16:52 | 删除损坏仓库,重建 | ✅ |
| 16:55 | 再次推送 → HTTP 413 | ❌ |
| 16:58 | 排查 Nginx 配置 → 缺 client_max_body_size | ✅ 修复 |
| 17:02 | 重载 Nginx第三次推送 | ✅ 成功 |
| 17:12 | MCP Server 迁移 | ❌ Invalid CSRF token / SSE 404 |
| 17:15 | Nginx 配置修复location 嵌套错误) | ❌ Nginx 配置乱 |
| 17:17 | cat 覆盖完整 Nginx 配置 | ✅ 配置正常 |
| 17:20 | MCP Server 启动access key 路径问题) | ❌ 403 认证失败 |
| 17:25 | 创建 /data/guanghulab/mcp-server/.access-key | ✅ 认证通过 |
| 17:26 | repo-mcp-server 路由重写 `/repo-mcp``/mcp` | ✅ 全链路通过 |
**教训总结**
1. COS 备份恢复后,仓库数据可能损坏——先 `git ls-remote` 验证
2. Nginx 默认 1MB body 限制——Git 推送必超,需显式设置
3. MCP Server 迁移关键access key 路径、Nginx rewrite、location 不能嵌套
4. PM2 进程迁移后要清理冲突的 errored 实例
5. 经验要沉淀到仓库里,不能只留在本地终端