guanghulab/docs/ops/forgejo-deployment-experience.md

9.8 KiB
Raw Blame History

Forgejo 部署与排障经验库

光湖语言世界代码仓库(guanghulab.com)从 CVM 迁移到广州轻量云服务器的完整踩坑记录。 换服务器或遇到类似问题时,先看这个文档,不用重新推演。


核心:一键部署脚本

优先使用仓库里的 scripts/smart-deploy-forgejo.sh。 这个脚本做了智能检测(Docker可用性、COS配置、已有Forgejo状态),一条命令跑完全部部署流程。 换服务器时的第一步。


二进制部署(Docker 不可用的替代方案)

当 Docker pull 失败(429限流或被墙)时,直接下载 Gitea 二进制:

# 下载 Gitea 1.23.7(Forgejo 的底层引擎)
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:

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:

[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
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 限制 1MB,Git 推送的 packfile 远超此值
  3. 检查配置是否写在正确的 server block 里(certbot 可能创建了多个配置)

解决:

# 在 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. 尝试推送观察具体错误信息

解决:

# 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 证书配置

# 申请证书
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(腾讯云对象存储)备份恢复

# 安装 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. 验证部署完整性

确认服务运行:

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 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. 经验要沉淀到仓库里,不能只留在本地终端