代码资产自托管:群晖 NAS 搭建 Gitea、GitHub 镜像备份与本地双 Remote 实战

科技8小时前更新 ICSteve
7 0

# 代码资产自托管:群晖 NAS 搭建 Gitea、GitHub 镜像备份与本地双 Remote 实战

## 正文

*English version / 英文版: [Git Sovereignty: Setting Up Gitea on Synology NAS, Mirroring GitHub, and Mastering Dual Remotes](https://www.icsteve.com/1551.html)*

在现代软件工程与芯片设计领域,Git 是不可或缺的协作底座。然而,许多工程师常常将 Git(分布式版本控制系统)与 GitHub(微软旗下的商业托管平台)混为一谈。

代码资产自托管:群晖 NAS 搭建 Gitea、GitHub 镜像备份与本地双 Remote 实战

2005 年 Linus Torvalds 设计 Git 时,其核心哲学是彻底的去中心化:每个本地克隆都是一个功能完备的仓库,拥有完整的历史记录与提交链。Git 协议中根本不存在所谓的“中心服务器”。但在实际开发中,我们对单一云平台的过度依赖,正在形成新的单点故障:

  1. 账号合规与不可抗力风险:自动化封控、DMCA 版权争议或账户异动,可能导致仓库瞬间无法访问。
  2. 上游服务中断与外网波动:云端服务宕机或断网时,本地开发、脚本调试及测试流程不应停摆。
  3. 敏感代码与私有资产安全性:EDA 自动化脚本、物理设计约束、私有量化工具及个人笔记,理应存放在防火墙之内的私有介质上。

解决方案并不是放弃 GitHub——它依然是开源协作与开发者声誉的核心枢纽。最稳健的工程架构是“双轨 Git 体系”:对外使用 GitHub 协作与展示,对内依托群晖 NAS 部署轻量级 Gitea,搭建私有代码枢纽,并配合自动化镜像同步与多 Remote 本地推送。


架构拓扑:本地与云端协同模型

               +-------------------------------------------+
               |        云端托管:GitHub (开源与协作中心)   |
               +-------------------------------------------+
                                   ^     |
                                   |     | [Gitea 定时拉取镜像]
            [git push origin]      |     v (自动同步 Commit/Tag)
                               +-----------------------------+
                               | 本地群晖 NAS: Gitea 容器    |
                               | (局域网内网枢纽 / 3000 端口)|
                               +-----------------------------+
                                        ^
                                        | [git push gitea]
                                        |
                 +--------------------------------------+
                 | 开发者本地工作站 (Mac / PC)          |
                 | - origin: github.com/user/repo       |
                 | - gitea:  nas-ip:2222/user/repo      |
                 +--------------------------------------+

该架构具备三大优势:

  • 局域网全速访问:内网 2.5G/10G 带宽读写,无视外网网络环境波动。
  • 全自动免维护备份:Gitea 周期性从 GitHub 拉取全量分支与标签,历史记录自动同步。
  • 灵活的分支分流策略:敏感或试验性分支推送至 NAS,稳定版代码合流推送至 GitHub。

第一部分:群晖 NAS 部署轻量级 Gitea (Container Manager)

为什么选择 Gitea?

在自建 Git 方案(GitLab、Gitea、Forgejo、Gogs)中,Gitea 是个人与团队私有化部署的首选。它由 Go 语言编写,内存占用极低(通常仅 80MB~120MB),空闲 CPU 占用几乎为零,非常适合运行在群晖 x86 或 ARM 架构的 NAS 上。

代码资产自托管:群晖 NAS 搭建 Gitea、GitHub 镜像备份与本地双 Remote 实战

步骤 1:创建目录结构

打开群晖 DSM 的 File Station,在 docker 共享文件夹下创建如下路径:

/volume1/docker/gitea/
  └── data/          <-- 存放仓库数据、SSH 秘钥及 SQLite 数据库

确认当前 DSM 管理员或 docker 用户组对该目录具有读写权限。

步骤 2:通过 Container Manager 配置 docker-compose.yml

打开群晖 DSM 中的 Container Manager:

  1. 点击左侧 项目 (Project) -> 点击 新增。
  2. 项目名称填写 gitea,路径选择 /docker/gitea。
  3. 来源勾选 创建 docker-compose.yml,粘贴以下配置文件:
version: "3"

networks:
  gitea_net:
    driver: bridge

services:
  server:
    image: gitea/gitea:latest
    container_name: gitea
    restart: always
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=sqlite3
      - GITEA__database__PATH=/data/gitea/gitea.db
      - GITEA__server__DOMAIN=192.168.1.100       # 请替换为你的群晖内网 IP
      - GITEA__server__SSH_DOMAIN=192.168.1.100   # 请替换为你的群晖内网 IP
      - GITEA__server__SSH_PORT=2222              # 容器外部映射的 SSH 端口,避开群晖默认 22
      - GITEA__server__SSH_LISTEN_PORT=22
      - GITEA__server__ROOT_URL=http://192.168.1.100:3000/
      - GITEA__server__LFS_START_SERVER=true
      - TZ=Asia/Shanghai
    networks:
      - gitea_net
    volumes:
      - /volume1/docker/gitea/data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"   # Web 管理后台端口
      - "2222:22"     # Git SSH 端口(映射至容器内 22)

为何使用 2222 端口? 群晖 DSM 默认占用系统 22 端口用于管理终端。将容器 SSH 端口映射为 2222,既可保留系统终端,又可保证 Git 协议顺畅工作。

点击下一步直至完成,Container Manager 将自动拉取镜像并启动容器。

步骤 3:初次启动向导

  1. 浏览器访问 http://<群晖IP>:3000。
  2. 确认配置参数(SQLite3 数据库文件路径 /data/gitea/gitea.db,SSH 端口 2222,基础 URL http://<群晖IP>:3000/)。
  3. 展开底部的 管理员账号设置,填入你的管理员用户名、密码及邮箱。
  4. 点击 立即安装,数秒后即可进入管理界面。

第二部分:在 Gitea 中设置 GitHub 仓库自动镜像备份

Gitea 内置了企业级的 镜像迁移(Mirror Migration) 功能。与简单的静态备份不同,镜像仓库会持续追踪上游提交,保持分支、Tag 与提交链的实时一致。

Git Distributed Architecture: Cloud Remote + Local NAS Mirror ☁️ GitHub (Cloud) origin · Primary Upstream 🏠 Synology Gitea (NAS) gitea · Scheduled Pull Mirror Auto Sync Local Workstation (MacBook/PC) Dual Remotes (git push origin / gitea) Multi-push: git remote set-url –add –push

方法 A:Web 界面配置自动拉取镜像(推荐)

  1. 登录 Gitea 后台,点击右上角 + 号,选择 迁移外部仓库。
  2. 在迁移来源中选择 GitHub。
  3. 填写迁移参数:
    • 迁移 URL:https://github.com/<用户名>/<仓库名>.git
    • 访问凭证:若为私有仓库,勾选“需要授权”,填入具有 repo 权限的 GitHub Personal Access Token (PAT)。
    • 镜像选项(核心):务必勾选 该仓库将作为镜像(This repository will be a mirror)。
    • 同步周期:根据需求设置自动同步间隔(如 4h、8h 或 24h)。
  4. 点击 迁移仓库。

此后,Gitea 后台会自动执行周期性同步。即使你在云端推送代码,群晖本地也会全自动保有最新的镜像副本。

方法 B:命令行裸镜像推送(适合首次全量导入)

若需一次性快速导入现有大型仓库:

# 1. 抓取完整的 bare 镜像
git clone --mirror https://github.com/username/my-project.git

# 2. 进入裸仓库
cd my-project.git

# 3. 添加本地 Gitea 地址为推送目标
git remote set-url --push origin ssh://git@192.168.1.100:2222/username/my-project.git

# 4. 一键镜像推送所有分支与 Tag
git push --mirror

第三部分:本地工作机配置多 Remote(双轨推送实战)

在本地开发机(Mac、Linux 或 Windows)上,无需在 GitHub 与群晖之间二选一。通过 Git 原生的多 Remote 机制,我们可以游刃有余地控制推送路径。

策略一:独立双 Remote,精准分流(推荐)

1. 检查当前本地仓库 Remote

git remote -v

通常会显示默认的 GitHub 远程端点:

origin  git@github.com:steve/my-project.git (fetch)
origin  git@github.com:steve/my-project.git (push)

2. 添加本地群晖 Gitea 作为第二个 Remote

git remote add gitea ssh://git@192.168.1.100:2222/steve/my-project.git

再次查看远程端点:

gitea   ssh://git@192.168.1.100:2222/steve/my-project.git (fetch)
gitea   ssh://git@192.168.1.100:2222/steve/my-project.git (push)
origin  git@github.com:steve/my-project.git (fetch)
origin  git@github.com:steve/my-project.git (push)

3. 按需分流推送

  • 未定稿代码或私有调试分支仅推送到群晖 NAS:
    git push gitea feature/timing-closure-opt
  • 正式发布版本推送到 GitHub:
    git push origin main
  • 全量同步本地全部历史与标签至群晖:
    git push gitea --all && git push gitea --tags

策略二:一条命令同步推送到两个 Remote

若希望每次执行 git push 时,代码能同时提交到 GitHub 与群晖 NAS,可以利用 Git 的多 pushurl 特性:

# 1. 显式保留 GitHub 作为主推送目标
git remote set-url --add --push origin git@github.com:steve/my-project.git

# 2. 将本地 Gitea 地址追加为 origin 的第二个推送目标
git remote set-url --add --push origin ssh://git@192.168.1.100:2222/steve/my-project.git

检查配置结果:

origin  git@github.com:steve/my-project.git (fetch)
origin  git@github.com:steve/my-project.git (push)
origin  ssh://git@192.168.1.100:2222/steve/my-project.git (push)

此后只需执行:

git push origin main

Git 便会按序依次推送到 GitHub 和群晖 Gitea,实现双活冗余备份。


第四部分:配置 SSH 免密访问

为了避免在端口 2222 上反复输入密码,可在本地开发机的 ~/.ssh/config 中加入如下别名配置:

Host synology-gitea
    HostName 192.168.1.100
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519

配置后,添加 Remote 的命令即可简化为:

git remote add gitea synology-gitea:steve/my-project.git

同时,将本地公钥(cat ~/.ssh/id_ed25519.pub)添加到 Gitea 后台的 用户设置 -> SSH / GPG 密钥 中。



第五部分:灾难重建实战 — Gitea 备份与完整恢复指南

任何自托管系统的安全性,最终都由其灾难恢复流程(Disaster Recovery)决定。容器镜像与软件包本身是可随手抛弃的消耗品;真正不可替代的是你的 Git 仓库数据、数据库(SQLite/PostgreSQL)、用户身份令牌以及服务端配置文件中的核心加密密钥(SECRET_KEY 与 INTERNAL_TOKEN)。

Gitea 原生提供了一站式原子备份命令:gitea dump。它能在无需停止服务的情况下,将数据库、代码仓库裸文件、LFS 大文件对象与主配置文件整合成一个标准的 .zip 归档包。

1. 执行全量原子备份

场景 A:群晖套件中心部署 (SynoCommunity 原生套件)

如果你是通过群晖套件中心(SynoCommunity)安装的原生 Gitea,系统会分配专用的 sc-gitea 运行账号。请在群晖 SSH 终端中直接运行以下备份命令:

sudo su -s /bin/sh -c "/var/packages/gitea/target/bin/gitea dump -c /var/packages/gitea/var/conf.ini" sc-gitea

命令逐项拆解:

  • sudo su -s /bin/sh -c "..." sc-gitea:切换至 Gitea 的底层专属运行账号 sc-gitea 执行操作,避免因 root 权限产生文件属主错乱,确保备份进程具备完整读取权限;
  • /var/packages/gitea/target/bin/gitea dump:调用群晖套件路径下的 Gitea 原生 CLI 备份程序;
  • -c /var/packages/gitea/var/conf.ini:精准指定群晖套件的当前生效配置文件路径。

命令执行完毕后,会在当前工作目录生成带有时间戳的归档文件(如 gitea-dump-1727600000.zip)。

场景 B:Container Manager (Docker) 容器化部署

如果是本文第一部分推荐的 Docker 容器方式,只需在宿主机终端向容器发送执行指令:

docker exec -u 1000 -it gitea /app/gitea/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-dump-backup.zip

生成的压缩包会实时保存在群晖宿主机挂载的 /volume1/docker/gitea/data/gitea-dump-backup.zip,可配合群晖 Hyper Backup 定期转储至冷存储。


2. 从零重建与完整恢复步骤 (Restore & Rebuild)

当群晖硬盘损坏、阵列重组或迁移至新 NAS 时,只需遵循以下五步即可无损重建:

步骤 1:启动全新的 Gitea 干净实例

在全新群晖系统中通过 Container Manager(使用本文提供的 docker-compose.yml)或套件中心重新安装 Gitea。启动后立即停止容器/服务,防止恢复过程中数据库产生写入锁:

# Docker 容器:
docker stop gitea

# 套件中心:
sudo synopkg stop gitea

步骤 2:解压备份归档包

将 gitea-dump-*.zip 上传至群晖,解压到临时目录:

unzip gitea-dump-*.zip -d /tmp/gitea-restore/

解压后包含四个核心模块:

  • repos/:所有 Git 裸仓库目录;
  • gitea-db.sql:全量数据库导出脚本;
  • data/:附件、用户头像与 Git LFS 存储对象;
  • app.ini:主配置文件(包含核心加密 Secret,保持此文件可确保用户密码与 Token 无缝生效)。

步骤 3:还原数据资产与数据库

以 Docker 部署路径为例:

  1. 恢复仓库裸文件:
    cp -R /tmp/gitea-restore/repos/* /volume1/docker/gitea/data/git/repositories/
  2. 恢复系统配置:
    cp /tmp/gitea-restore/app.ini /volume1/docker/gitea/data/gitea/conf/app.ini
  3. 还原 SQLite 数据库:
    sqlite3 /volume1/docker/gitea/data/gitea/gitea.db < /tmp/gitea-restore/gitea-db.sql
  4. 还原 LFS 与静态资源:
    cp -R /tmp/gitea-restore/data/* /volume1/docker/gitea/data/gitea/

步骤 4:重置文件属主权限(核心避坑点)

恢复复制后文件属主可能变为当前操作用户,必须修复权限,否则 Gitea 无法通过 SSH/HTTP 进行写入:

# Docker 部署(对应 docker-compose 中的 UID 1000):
sudo chown -R 1000:1000 /volume1/docker/gitea/data

# 套件中心部署:
sudo chown -R sc-gitea:sc-gitea /var/packages/gitea/var/

步骤 5:启动服务并执行钩子同步

启动服务:

docker start gitea

登录管理员后台,进入 管理后台 -> 应用监控统计 -> 操作管理 (Operations),点击执行两项关键维护:

  1. 重新同步所有仓库的 pre-receive、update 和 post-receive 钩子;
  2. 重新生成 .ssh/authorized_keys 文件(恢复所有用户的 SSH 公钥访问)。

至此,全站仓库、提交历史、镜像配置与权限凭据均已 100% 完整复活。


第五部分:灾难重建实战 — Gitea 备份与完整恢复指南

容器与可执行程序随时可重新安装,但 Git 仓库数据、数据库及核心密钥不可替代。Gitea 原生提供 gitea dump 实现免中断原子备份。

1. 执行全量原子备份

场景 A:群晖套件中心部署 (SynoCommunity 原生套件)

sudo su -s /bin/sh -c "/var/packages/gitea/target/bin/gitea dump -c /var/packages/gitea/var/conf.ini" sc-gitea

以 sc-gitea 服务账号执行,保证文件归属与权限正常,并在当前路径生成包含完整数据库、仓库裸文件与主配置的 gitea-dump-*.zip。

场景 B:Container Manager (Docker) 容器化部署

docker exec -u 1000 -it gitea /app/gitea/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-dump-backup.zip

2. 从零重建 5 步恢复流程

  1. 启动干净实例并立即关停:重新安装 Gitea 生成基础结构后立即停止服务(docker stop gitea 或 synopkg stop gitea);
  2. 解压归档包:unzip gitea-dump-*.zip -d /tmp/gitea-restore/;
  3. 数据覆盖还原:将 repos/* 复制回仓库路径,覆盖恢复 app.ini 密钥,执行 sqlite3 .../gitea.db < gitea-db.sql 导入数据库;
  4. 重置文件属主权限:Docker 执行 sudo chown -R 1000:1000 /volume1/docker/gitea/data,套件执行 sudo chown -R sc-gitea:sc-gitea /var/packages/gitea/var/;
  5. 启动并重建钩子:启动服务,在管理后台点击“重新同步所有仓库的 Hook 钩子”并“重新生成 authorized_keys”。

方案对比与总结

评估维度 纯云端托管 (GitHub) 双轨架构 (GitHub + 群晖 Gitea)
网络依赖性 强依赖外网与云服务状态 内网 2.5G/10G 秒级读写,断网亦能完整作业
数据主权 受制于第三方平台服务条款 存放在私有 RAID 阵列上,掌控权完整归属个人
灾备机制 依赖手动打包或外部爬虫 Gitea 自动拉取镜像 + 双 Remote 实时推送
分支管理粒度 仓库级别公有/私有 灵活精细:敏感草稿留内网,成熟成果发云端

在个人 NAS 上搭建 Gitea 并与本地开发流深度结合,回归了 Git 最初“完全分布式”的设计初衷。让核心代码资产在私有介质与云端生态之间无缝流动,既拥有了云端的协作生态,也握紧了本地的数据主权。

© 版权声明

相关文章

暂无评论

暂无评论...