抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

这次做的是把一个原本面向本地运行的项目 StarRailCopilot 改造成可以部署到 Hugging Face Spaces 的 Docker Space 版本。

项目本身不是普通 Web 项目,它依赖本地配置、日志、截图目录,还带有更新器和自动化相关逻辑。直接丢到 Space 里跑,会遇到几个问题:

  • Space 容器是临时环境,重启后本地文件可能丢失。
  • 原项目默认更偏本地桌面/模拟器环境,WebUI 只是其中一部分。
  • Hugging Face Space 仓库如果直接塞完整上游项目,后续同步上游会很难维护。
  • Space 的构建和运行日志不一定能完整反映 GitHub 分支里的状态。

最后采用的方案是:GitHub fork + HF Space 轻量 wrapper + PostgreSQL 配置持久化

整体方案

部署结构大概是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
上游仓库 LmeSzinc/StarRailCopilot


自己的 fork:imHansiy/StarRailCopilot

├─ hf-space-docker 分支:只放 Docker Space 相关改造


Hugging Face Space:jankesw/starrailcopilot

├─ 轻量 Dockerfile
├─ 构建时 clone GitHub fork 的 hf-space-docker 分支
└─ 运行 WebUI

这样做的好处是很明显的:

  • Space 仓库保持很小,只负责拉代码和构建。
  • 真正的改造集中在 fork 的 hf-space-docker 分支。
  • 以后同步上游时,冲突范围比较可控。
  • Space 每次重建都会从 GitHub 分支拉最新代码。

Docker Space 改造

Hugging Face Docker Space 需要在 README front matter 里声明:

1
2
3
4
5
---
title: StarRailCopilot
sdk: docker
app_port: 7860
---

核心入口使用 hf_space_entrypoint.py,启动时做几件事:

  1. 生成适合 Space 的 config/deploy.yaml
  2. 禁用自动更新、自动安装依赖、ADB 自动连接等本地环境行为。
  3. 强制 WebUI 监听 0.0.0.0:${PORT:-7860}
  4. 如果设置了 SRC_WEBUI_PASSWORD,给 WebUI 加密码。

这一步的关键不是写一个复杂 Dockerfile,而是把原项目启动时默认假设的“本地桌面环境”收束成“只启动 WebUI 服务”。

为什么不用直接上传完整项目到 Space

一开始很容易想到把整个项目直接上传到 Hugging Face Space 仓库。

但这个做法后面会比较麻烦:

  • Space 仓库会变得很大。
  • 上游更新时很难区分哪些是原项目变化,哪些是部署改造。
  • 如果每次都在 Space 仓库里改源码,fork 的价值会变小。

所以更合适的方式是:Space 仓库只做 wrapper。

wrapper 的 Dockerfile 类似这样:

1
2
3
4
5
6
ARG SRC_REPO=https://github.com/imHansiy/StarRailCopilot.git
ARG SRC_REF=hf-space-docker

RUN git clone --depth 1 --branch "${SRC_REF}" "${SRC_REPO}" /tmp/starrailcopilot \
&& cp -a /tmp/starrailcopilot/. /app/ \
&& rm -rf /tmp/starrailcopilot

这样 Space 实际部署的就是 fork 分支里的代码。

持久化问题

Space 默认文件系统不是可靠持久化存储。配置、日志、截图如果只放在容器内部,重启后可能丢失。

我尝试过 Hugging Face 的 persistent storage API:

1
api.request_space_storage("user/space", storage="small")

但实际调用时返回了 404 Not Found。这类能力现在和账号、Space 设置、计费流程相关,不一定能直接通过旧 API 开通。

所以后面改成了 PostgreSQL 保存配置。

PostgreSQL 配置同步

这里没有把整个项目状态都塞进数据库,只同步 config/

原因很简单:

  • 配置文件体积小,适合放数据库。
  • 日志和截图会持续增长,不适合直接塞进 PG。
  • 真正需要跨重启保留的是用户配置,而不是所有运行痕迹。

Space 里通过 Secret 设置:

1
2
SRC_DATABASE_URL=postgresql://user:password@host:5432/dbname?sslmode=require
SRC_PG_NAMESPACE=starrailcopilot

入口脚本启动时会:

  1. 检查是否存在 SRC_DATABASE_URL
  2. 自动创建表 src_space_files
  3. 从数据库恢复 config/ 文件。
  4. WebUI 运行期间每 60 秒同步一次配置。
  5. 进程退出前再同步一次。

表结构很简单:

1
2
3
4
5
6
7
8
CREATE TABLE IF NOT EXISTS src_space_files (
namespace TEXT NOT NULL,
path TEXT NOT NULL,
content BYTEA NOT NULL,
sha256 TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (namespace, path)
);

这里用 namespace 是为了以后同一个数据库能放多个 Space 或多个环境的配置。

遇到的坑

1. Space wrapper 和 fork 分支的 Dockerfile 不是一回事

这里踩过一个小坑。

我在 fork 分支的 Dockerfile 里加了 psycopg,但后来发现实际 Space 构建用的是 wrapper Dockerfile。wrapper clone 代码后安装的是 fork 分支里的 requirements.txt

所以正确做法是把运行依赖加到 requirements.txt

1
psycopg[binary]>=3.2,<4

这样 wrapper 不需要频繁改,Space 构建时自然会安装 PG 驱动。

2. Hugging Face API 有时会被网络环境影响

过程中出现过几次 TLS EOF 或连接 reset:

1
2
SSL: UNEXPECTED_EOF_WHILE_READING
Recv failure: Connection was reset

这个一般不是代码问题,而是当前网络/代理到 Hugging Face 或 GitHub 的连接不稳定。

处理方式就是:

  • GitHub 推送失败就重试 git push
  • HF Python SDK 上传失败时,换 Invoke-RestMethod 直接调 API。
  • 重启 Space 用:
1
POST https://huggingface.co/api/spaces/{repo_id}/restart?factory=true

3. 更新器页面取不到 git 历史会崩

WebUI 里有一个“更新器”页面,它会读取本地和 upstream 的 git commit 历史。

但 Space 是浅克隆环境,而且 wrapper 构建后的 git remote 状态和普通本地仓库不一样。结果页面拿到:

1
(None, None, None, None)

然后 PyWebIO 的 put_table()None 当成行数据渲染,触发:

1
TypeError: 'NoneType' object is not iterable

修复方式是让更新器 UI 对缺失 git 历史做容错:

1
2
3
4
def commit_row(label, commit):
if not commit or all(value is None for value in commit):
return [label, "-", "-", "-", "-"]
return [label, *commit]

详细历史为空时也显示占位行,而不是传空的 None 进去。

安全注意事项

部署过程中最需要注意的是密钥不要写进仓库。

这些都应该放在 Hugging Face Space Secret 里:

  • HF_TOKEN
  • SRC_DATABASE_URL
  • SRC_WEBUI_PASSWORD
  • 其他第三方服务密钥

如果密钥已经在聊天记录、日志或终端输出里出现过,比较稳妥的做法是直接轮换。

还有一个点:数据库连接串里一般带有密码,博客、README 和 issue 里都不要贴真实值。示例里用占位符即可。

最终效果

最后部署出来的访问地址是:

1
https://jankesw-starrailcopilot.hf.space/

当前实现的能力:

  • Docker Space 可以正常启动 WebUI。
  • Space 从 GitHub fork 的 hf-space-docker 分支构建。
  • 配置文件通过 PostgreSQL 持久化。
  • 缺失 HF persistent storage 时也能保留主要配置。
  • 更新器页面在 Space 环境下不会因为 git history 缺失崩溃。

总结

这次改造的核心不是“把项目塞进 Hugging Face Spaces”,而是把部署边界整理清楚:

  • 上游项目继续保持上游项目的样子。
  • fork 分支只承载部署适配。
  • Space 仓库只做 wrapper。
  • 持久化用外部 PostgreSQL 承接。
  • 所有敏感配置走 Secret。

这个结构后续维护起来会舒服一些。上游有更新时,先同步 fork,再看 hf-space-docker 分支的少量部署文件是否冲突;Space 侧只需要重建即可。

如果只是临时跑 WebUI,直接 Docker Space 就够了。
如果希望长期可用,配置持久化这一步基本绕不开。

评论




站点访问量 Loading… 站点访客数 Loading…