Spaces:
Paused
Paused
HuggingRun: 通用接口 + HF 限制文档 + APP_PORT + ubuntu-desktop 示例
Browse files- .env.example +2 -1
- DEPLOY.md +54 -0
- DESIGN.md +2 -1
- README.md +42 -35
- docs/HF_LIMITATIONS.md +73 -0
- docs/plans/2025-03-03-ubuntu-desktop-design.md +26 -0
- scripts/sync_hf.py +7 -2
- ubuntu-desktop/Dockerfile +47 -0
- ubuntu-desktop/README.md +14 -0
- ubuntu-desktop/start-desktop.sh +40 -0
.env.example
CHANGED
|
@@ -16,5 +16,6 @@ RUN_CMD=
|
|
| 16 |
# Sync interval in seconds
|
| 17 |
SYNC_INTERVAL=60
|
| 18 |
|
| 19 |
-
# Port
|
| 20 |
PORT=7860
|
|
|
|
|
|
| 16 |
# Sync interval in seconds
|
| 17 |
SYNC_INTERVAL=60
|
| 18 |
|
| 19 |
+
# Port your app listens on (HF exposes only this one port; default 7860)
|
| 20 |
PORT=7860
|
| 21 |
+
APP_PORT=7860
|
DEPLOY.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 部署 HuggingRun 到 Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
## 1. 创建 Space
|
| 4 |
+
|
| 5 |
+
在 [huggingface.co/new-space](https://huggingface.co/new-space) 新建 Space:
|
| 6 |
+
|
| 7 |
+
- **SDK**: 选 **Docker**
|
| 8 |
+
- **Space name**: 例如 `HuggingRun`
|
| 9 |
+
- 创建后得到 `https://huggingface.co/spaces/<你的用户名>/HuggingRun`
|
| 10 |
+
|
| 11 |
+
## 2. 推送代码
|
| 12 |
+
|
| 13 |
+
在本地项目目录执行(把 `tao-shen` 换成你的 HF 用户名):
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
cd /Users/tao.shen/HuggingRun
|
| 17 |
+
git remote remove origin 2>/dev/null || true
|
| 18 |
+
git remote add origin https://huggingface.co/spaces/<你的用户名>/HuggingRun
|
| 19 |
+
git push -u origin main
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
若 HF 需要认证,先执行:
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
pip install -q huggingface_hub && huggingface-cli login
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
或使用 Git 凭证(HF 支持 Personal Access Token 作为密码)。
|
| 29 |
+
|
| 30 |
+
## 3. 本地验证(可选)
|
| 31 |
+
|
| 32 |
+
确保 Docker 已启动后:
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
docker build -t huggingrun:local .
|
| 36 |
+
docker run --rm -p 7860:7860 -e PORT=7860 huggingrun:local
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
浏览器打开 http://localhost:7860 应看到默认 demo(访问计数)。Ctrl+C 停止。
|
| 40 |
+
|
| 41 |
+
## 4. 难例测试(FastAPI + SQLite)
|
| 42 |
+
|
| 43 |
+
在 Space 的 **Settings → Repository secrets** 中增加:
|
| 44 |
+
|
| 45 |
+
- `RUN_CMD` = `uvicorn app.fastapi_sqlite:app --host 0.0.0.0 --port 7860`
|
| 46 |
+
- (可选)`HF_TOKEN`、`AUTO_CREATE_DATASET` = `true` 以开启持久化
|
| 47 |
+
|
| 48 |
+
保存后 Space 会重新构建。打开 App 查看首页和 `/api`,重启 Space 后访问次数应保持(若已配 HF 持久化)。
|
| 49 |
+
|
| 50 |
+
## 完成标准
|
| 51 |
+
|
| 52 |
+
- [ ] Space 创建并推送代码成功
|
| 53 |
+
- [ ] 默认 demo 在 Space 中可访问(7860)
|
| 54 |
+
- [ ] 设置 `RUN_CMD` 后 FastAPI+SQLite 难例可访问且(可选)重启后数据仍在
|
DESIGN.md
CHANGED
|
@@ -33,7 +33,8 @@
|
|
| 33 |
|
| 34 |
## 完成标准(迭代开发)
|
| 35 |
|
|
|
|
| 36 |
- [ ] 本地 `docker build` 与 `docker run` 成功,默认 demo 在 7860 响应。
|
| 37 |
- [ ] 持久化:重启容器后,写入 `PERSIST_PATH` 的内容仍在(通过 HF Dataset 恢复)。
|
| 38 |
-
- [ ]
|
| 39 |
- [ ] 代码推送到 HF Space,且在该 Space 中成功运行并通过验证。
|
|
|
|
| 33 |
|
| 34 |
## 完成标准(迭代开发)
|
| 35 |
|
| 36 |
+
- [ ] **通用工具**:README 与 docs/HF_LIMITATIONS.md 明确“通用接口 + HF 限制应对”;entrypoint/sync 可配置 PERSIST_PATH、APP_PORT、RUN_CMD。
|
| 37 |
- [ ] 本地 `docker build` 与 `docker run` 成功,默认 demo 在 7860 响应。
|
| 38 |
- [ ] 持久化:重启容器后,写入 `PERSIST_PATH` 的内容仍在(通过 HF Dataset 恢复)。
|
| 39 |
+
- [ ] 示例最小化:FastAPI+SQLite、Ubuntu 桌面等仅演示“用通用工具”的用法,不增加额外通用逻辑。
|
| 40 |
- [ ] 代码推送到 HF Space,且在该 Space 中成功运行并通过验证。
|
README.md
CHANGED
|
@@ -19,57 +19,64 @@ tags:
|
|
| 19 |
|
| 20 |
**Run anything on Hugging Face.**
|
| 21 |
|
| 22 |
-
|
| 23 |
|
| 24 |
-
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
-
- **Persistent**: Sync a directory (e.g. `/data`) to a private HF Dataset so state survives restarts.
|
| 28 |
-
- **One Dockerfile**: Parameterized with `BASE_IMAGE` and `RUN_CMD`; duplicate the Space and configure.
|
| 29 |
|
| 30 |
-
|
|
|
|
| 31 |
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
| `HF_DATASET_REPO` | Optional | Dataset repo for backup (e.g. `your-name/HuggingRun-data`). Default: `{SPACE_ID}-data` |
|
| 40 |
-
| `RUN_CMD` | Optional | Command to run (e.g. `python3 my_app.py`). Empty = default demo on port 7860 |
|
| 41 |
|
| 42 |
-
|
| 43 |
|
| 44 |
-
|
| 45 |
-
- **Auto**: Set `AUTO_CREATE_DATASET=true` and `HF_TOKEN`; a private dataset is created on first run.
|
| 46 |
|
| 47 |
-
|
| 48 |
|
| 49 |
-
|
| 50 |
|
| 51 |
-
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|----------|---------|-------------|
|
| 55 |
-
| `RUN_CMD` | (default demo) | Command to run (e.g. `uvicorn app:app --host 0.0.0.0 --port 7860`) |
|
| 56 |
-
| `PERSIST_PATH` | `/data` | Directory to sync to the HF Dataset |
|
| 57 |
-
| `HF_TOKEN` | — | HF token for persistence |
|
| 58 |
-
| `HF_DATASET_REPO` | `{SPACE_ID}-data` | Dataset repo ID |
|
| 59 |
-
| `AUTO_CREATE_DATASET` | `false` | Create dataset repo if missing |
|
| 60 |
-
| `SYNC_INTERVAL` | `60` | Seconds between uploads |
|
| 61 |
-
| `PORT` | `7860` | Port for default demo (HF expects 7860) |
|
| 62 |
|
| 63 |
-
|
| 64 |
|
| 65 |
-
|
| 66 |
|
| 67 |
-
|
| 68 |
-
- `RUN_CMD=uvicorn app.fastapi_sqlite:app --host 0.0.0.0 --port 7860`
|
| 69 |
-
2. (Optional) Set `HF_TOKEN` and `AUTO_CREATE_DATASET=true` for persistence so the SQLite DB in `/data` survives restarts.
|
| 70 |
-
3. Rebuild the Space. Open the app: you’ll see a visit counter and `/api` returning JSON. Restart the Space and refresh — the count persists.
|
| 71 |
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
## License
|
| 75 |
|
|
|
|
| 19 |
|
| 20 |
**Run anything on Hugging Face.**
|
| 21 |
|
| 22 |
+
HuggingRun 是面向 Hugging Face Spaces 的**通用部署接口**:用同一套工具解决 HF 上的持久化、单端口、网络等限制,让任意 Docker 应用都能一键部署、重启后状态保留。
|
| 23 |
|
| 24 |
+
- **通用工具优先**:主要维护的是通用层(持久化同步、单入口、可配置端口)。示例仅演示“最少配置”用法。
|
| 25 |
+
- **HF 限制与应对**:见 [docs/HF_LIMITATIONS.md](docs/HF_LIMITATIONS.md)(持久化、单端口、出站网络、DNS 等)。
|
| 26 |
|
| 27 |
+
## 通用接口:你只需做两件事
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
1. **Duplicate 本 Space**,在 Settings 里配好 **Secrets / Variables**(见下表)。
|
| 30 |
+
2. 设置 **`RUN_CMD`** 为你要跑的进程(例如 `uvicorn app:app --host 0.0.0.0 --port 7860`);不设则运行内置 demo。
|
| 31 |
|
| 32 |
+
| 配置项 | 必填 | 说明 |
|
| 33 |
+
|--------|------|------|
|
| 34 |
+
| `HF_TOKEN` | 需持久化时 | HF 写权限 Token,用于把 `PERSIST_PATH` 同步到 Dataset |
|
| 35 |
+
| `HF_DATASET_REPO` | 可选 | 备份用 Dataset 仓库,默认 `{SPACE_ID}-data` |
|
| 36 |
+
| `AUTO_CREATE_DATASET` | 可选 | `true` 时自动创建私有 Dataset |
|
| 37 |
+
| `RUN_CMD` | 可选 | 要执行的命令;空则跑默认 demo(7860 端口) |
|
| 38 |
+
| `PERSIST_PATH` | 可选 | 持久化目录,默认 `/data` |
|
| 39 |
+
| `APP_PORT` / `PORT` | 可选 | 应用监听端口,默认 `7860`(HF 只暴露此端口) |
|
| 40 |
|
| 41 |
+
## 通用工具提供了什么
|
| 42 |
|
| 43 |
+
- **持久化**:启动时从 HF Dataset 恢复 `PERSIST_PATH`,运行中定时上传,退出时再上传一次。解决 HF 无本地持久盘的问题。
|
| 44 |
+
- **单端口约定**:应用只需监听 `APP_PORT`(默认 7860);多端口服务需自己在容器内做反向代理。
|
| 45 |
+
- **统一入口**:同一 entrypoint 先做恢复与同步,再 `exec` 你的 `RUN_CMD`,便于任意镜像复用。
|
|
|
|
|
|
|
| 46 |
|
| 47 |
+
详见 [docs/HF_LIMITATIONS.md](docs/HF_LIMITATIONS.md)。
|
| 48 |
|
| 49 |
+
## 示例(最小用法)
|
|
|
|
| 50 |
|
| 51 |
+
示例仅展示“用通用工具、最少配置”的用法,维护量保持在最低。
|
| 52 |
|
| 53 |
+
### 默认 demo
|
| 54 |
|
| 55 |
+
不设 `RUN_CMD`:容器会跑一个 7860 端口的简单 HTTP 页,并把计数存到 `/data`,用于验证持久化。
|
| 56 |
|
| 57 |
+
### FastAPI + SQLite
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
+
在 Secrets 里设:
|
| 60 |
|
| 61 |
+
- `RUN_CMD=uvicorn app.fastapi_sqlite:app --host 0.0.0.0 --port 7860`
|
| 62 |
|
| 63 |
+
(可选)设 `HF_TOKEN` 与 `AUTO_CREATE_DATASET=true`,重启后 SQLite 数据仍在。
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
+
### Ubuntu 桌面(noVNC)
|
| 66 |
+
|
| 67 |
+
见 [ubuntu-desktop/](ubuntu-desktop/):使用同一套通用脚本(同步 + entrypoint),仅 `RUN_CMD` 改为启动 XFCE + noVNC。作为“用通用工具跑复杂应用”的示例,不做额外封装。
|
| 68 |
+
|
| 69 |
+
## 环境变量速查
|
| 70 |
+
|
| 71 |
+
| 变量 | 默认 | 说明 |
|
| 72 |
+
|------|------|------|
|
| 73 |
+
| `RUN_CMD` | 默认 demo | 要执行的命令 |
|
| 74 |
+
| `PERSIST_PATH` | `/data` | 同步到 HF Dataset 的目录 |
|
| 75 |
+
| `HF_TOKEN` | — | 持久化用 Token |
|
| 76 |
+
| `HF_DATASET_REPO` | `{SPACE_ID}-data` | Dataset 仓库 |
|
| 77 |
+
| `AUTO_CREATE_DATASET` | `false` | 是否自动创建 Dataset |
|
| 78 |
+
| `SYNC_INTERVAL` | `60` | 同步间隔(秒) |
|
| 79 |
+
| `PORT` / `APP_PORT` | `7860` | 应用监听端口 |
|
| 80 |
|
| 81 |
## License
|
| 82 |
|
docs/HF_LIMITATIONS.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Spaces 限制与 HuggingRun 应对
|
| 2 |
+
|
| 3 |
+
本文档整理 [Hugging Face 官方文档](https://huggingface.co/docs/hub/en/spaces-overview) 中的 Spaces 限制,以及 HuggingRun 通用工具如何通过技术手段应对。
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## 1. 持久化存储(无本地持久盘)
|
| 8 |
+
|
| 9 |
+
| 官方说明 | HuggingRun 方案 |
|
| 10 |
+
|----------|-----------------|
|
| 11 |
+
| 免费 Space 磁盘为 **非持久**:重启/停止后内容丢失。付费持久盘方案已不再对新用户开放。 | **Dataset 同步**:将 `PERSIST_PATH`(默认 `/data`)定时同步到私有 HF Dataset 仓库;启动时先拉取再启动应用。设置 `HF_TOKEN` + `AUTO_CREATE_DATASET=true` 或 `HF_DATASET_REPO` 即可。 |
|
| 12 |
+
|
| 13 |
+
参考:[Disk usage on Spaces](https://huggingface.co/docs/hub/main/en/spaces-storage)、[Dataset storage](https://huggingface.co/docs/hub/main/en/spaces-storage#dataset-storage)
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## 2. 单端口暴露
|
| 18 |
+
|
| 19 |
+
| 官方说明 | HuggingRun 方案 |
|
| 20 |
+
|----------|-----------------|
|
| 21 |
+
| 仅 **一个端口** 对外暴露,默认 `7860`,可在 README 的 `app_port` 中修改。内部可开多端口,但外部只能通过这一端口访问。 | 应用需监听 `app_port`(默认 7860)。多端口服务需在 **容器内自建反向代理**(如 Nginx/Caddy)在 7860 上统一入口,再转发到内部各端口;或使用 HuggingRun 的 `APP_PORT` 与 `RUN_CMD` 约定应用只监听该端口。 |
|
| 22 |
+
|
| 23 |
+
参考:[Docker Spaces](https://huggingface.co/docs/hub/en/spaces-sdks-docker)
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## 3. 出站网络
|
| 28 |
+
|
| 29 |
+
| 官方说明 | HuggingRun 方案 |
|
| 30 |
+
|----------|-----------------|
|
| 31 |
+
| 出站请求仅允许 **80、443、8080**。其他端口会被拦截。 | 应用只发起 HTTP/HTTPS 或 8080 出站即可。若依赖其他端口,需使用可经 443 转发的代理或 API。 |
|
| 32 |
+
|
| 33 |
+
参考:Spaces Overview — Networking
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## 4. DNS / 域名解析
|
| 38 |
+
|
| 39 |
+
| 现象 | HuggingRun 方案 |
|
| 40 |
+
|------|-----------------|
|
| 41 |
+
| 部分环境存在 DNS 解析失败或特定域名不可达(如部分 API 域名)。 | 可选:在自定义 `RUN_CMD` 前通过 **DoH(DNS over HTTPS)** 或自定义 resolver 绕过;或使用系统/语言层代理。HuggingRun 通用层可预留 `DNS_OVER_HTTPS=1` 等开关,由启动脚本在需要时配置。 |
|
| 42 |
+
|
| 43 |
+
参考:[Issue with URL Resolution/Blocked on Hugging Face Spaces](https://discuss.huggingface.co/t/issue-with-url-resolution-blocked-on-hugging-face-spaces/95982)
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## 5. 资源与休眠
|
| 48 |
+
|
| 49 |
+
| 官方说明 | HuggingRun 方案 |
|
| 50 |
+
|----------|-----------------|
|
| 51 |
+
| 免费:2 vCPU、16 GB 内存、50 GB 非持久磁盘。长时间无访问会 **Sleep**,需付费硬件才可长期运行。 | 无法通过工具绕过;建议在 README 中说明。持久化通过 Dataset 保证重启/休眠后 **状态可恢复**。 |
|
| 52 |
+
|
| 53 |
+
---
|
| 54 |
+
|
| 55 |
+
## 6. Cookie / iframe
|
| 56 |
+
|
| 57 |
+
| 官方说明 | HuggingRun 方案 |
|
| 58 |
+
|----------|-----------------|
|
| 59 |
+
| Space 运行在 `*.hf.space`,若被嵌入 iframe,会受浏览器跨域 Cookie 限制。 | 与通用部署接口无直接关系;若应用依赖 Cookie,需在应用层处理(如禁用 XSRF 或使用 Token)。 |
|
| 60 |
+
|
| 61 |
+
参考:[Cookie limitations in Spaces](https://huggingface.co/docs/hub/spaces-cookie-limitations)
|
| 62 |
+
|
| 63 |
+
---
|
| 64 |
+
|
| 65 |
+
## 总结:HuggingRun 通用接口提供的应对
|
| 66 |
+
|
| 67 |
+
- **持久化**:`PERSIST_PATH` + HF Dataset 同步(启动恢复 + 定时上传 + 退出上传)。
|
| 68 |
+
- **单端口**:约定 `APP_PORT`(默认 7860),应用或自建反向代理只暴露该端口。
|
| 69 |
+
- **出站**:文档约束(仅 80/443/8080);复杂场景用代理或 API。
|
| 70 |
+
- **DNS**:可选 DoH/自定义 resolver,由启动脚本在通用层按需开启。
|
| 71 |
+
- **资源/休眠**:文档说明;依赖持久化保证“重启后状态保留”。
|
| 72 |
+
|
| 73 |
+
用户只需:**设置 `RUN_CMD`(及可选 `HF_TOKEN`、`PERSIST_PATH` 等)**,即可在 HF 上部署任意兼容 Docker 的应用,由通用工具统一处理上述限制。
|
docs/plans/2025-03-03-ubuntu-desktop-design.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ubuntu 桌面版 on HuggingRun 设计
|
| 2 |
+
|
| 3 |
+
**目标**: 在 HuggingRun 上部署最新版 Ubuntu 桌面(浏览器内 noVNC 完整桌面),打通常用功能,重启后状态完整保留。
|
| 4 |
+
|
| 5 |
+
## 方案
|
| 6 |
+
|
| 7 |
+
- **基础镜像**: Ubuntu 24.04 LTS
|
| 8 |
+
- **桌面**: XFCE(轻量,适合 2 vCPU / 16GB)
|
| 9 |
+
- **显示**: Xvfb 虚拟显示 + TigerVNC + noVNC(noVNC 监听 7860,满足 HF Spaces)
|
| 10 |
+
- **持久化**: 桌面用户 HOME 放在 `PERSIST_PATH`(默认 `/data/desktop-home`),由现有 sync_hf.py 同步到 HF Dataset;启动时先恢复再挂载/HOME 指向该目录
|
| 11 |
+
- **入口**: 独立 `ubuntu-desktop/` 目录,自有 Dockerfile;entrypoint 先执行 sync 恢复,再启动 Xvfb → 桌面 → VNC → noVNC
|
| 12 |
+
|
| 13 |
+
## 完成标准(迭代开发)
|
| 14 |
+
|
| 15 |
+
- [ ] `ubuntu-desktop/` 可独立构建并运行,浏览器访问 7860 看到完整 XFCE 桌面
|
| 16 |
+
- [ ] 桌面功能可用:文件管理器、终端、浏览器(Firefox)、文本编辑器
|
| 17 |
+
- [ ] 设置 HF_TOKEN + AUTO_CREATE_DATASET 后,重启 Space 后桌面状态(桌面文件、配置、已装软件状态)保留,无报错
|
| 18 |
+
- [ ] 周期性同步与退出时同步正常,无遗漏
|
| 19 |
+
|
| 20 |
+
## 实现要点
|
| 21 |
+
|
| 22 |
+
1. **Dockerfile.ubuntu-desktop**: FROM ubuntu:24.04,装 python3、huggingface_hub、XFCE、TigerVNC、noVNC、Firefox;复制 HuggingRun scripts;用户 uid 1000;HOME 指向持久化目录
|
| 23 |
+
2. **entrypoint_desktop**: 恢复 `/data` → 创建并绑定 `/data/desktop-home` 为桌面 HOME → 启动 sync 后台 → 启动 Xvfb、dbus、XFCE、x11vnc/tigervnc、noVNC(监听 7860)
|
| 24 |
+
3. **PERSIST_PATH**: 使用 `/data`,`/data/desktop-home` 存桌面主目录;sync 继续上传/下载整个 `/data`
|
| 25 |
+
|
| 26 |
+
日期: 2025-03-03
|
scripts/sync_hf.py
CHANGED
|
@@ -27,10 +27,12 @@ os.environ.setdefault("HF_HUB_UPLOAD_TIMEOUT", "600")
|
|
| 27 |
from huggingface_hub import HfApi, snapshot_download
|
| 28 |
|
| 29 |
# ── Configuration ───────────────────────────────────────────────────────────
|
|
|
|
| 30 |
|
| 31 |
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 32 |
PERSIST_PATH = Path(os.environ.get("PERSIST_PATH", "/data"))
|
| 33 |
RUN_CMD = os.environ.get("RUN_CMD", "")
|
|
|
|
| 34 |
SYNC_INTERVAL = int(os.environ.get("SYNC_INTERVAL", "60"))
|
| 35 |
AUTO_CREATE_DATASET = os.environ.get("AUTO_CREATE_DATASET", "false").lower() in ("true", "1", "yes")
|
| 36 |
# In dataset we store under this path_in_repo subfolder
|
|
@@ -167,10 +169,13 @@ class GenericSync:
|
|
| 167 |
return None
|
| 168 |
print(f"[HuggingRun] Running: {cmd}")
|
| 169 |
try:
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
| 171 |
cmd,
|
| 172 |
shell=True,
|
| 173 |
-
env=
|
| 174 |
stdout=sys.stdout,
|
| 175 |
stderr=sys.stderr,
|
| 176 |
)
|
|
|
|
| 27 |
from huggingface_hub import HfApi, snapshot_download
|
| 28 |
|
| 29 |
# ── Configuration ───────────────────────────────────────────────────────────
|
| 30 |
+
# See docs/HF_LIMITATIONS.md for how HuggingRun addresses HF Spaces limits.
|
| 31 |
|
| 32 |
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 33 |
PERSIST_PATH = Path(os.environ.get("PERSIST_PATH", "/data"))
|
| 34 |
RUN_CMD = os.environ.get("RUN_CMD", "")
|
| 35 |
+
APP_PORT = os.environ.get("APP_PORT", os.environ.get("PORT", "7860")) # Single port exposed by HF
|
| 36 |
SYNC_INTERVAL = int(os.environ.get("SYNC_INTERVAL", "60"))
|
| 37 |
AUTO_CREATE_DATASET = os.environ.get("AUTO_CREATE_DATASET", "false").lower() in ("true", "1", "yes")
|
| 38 |
# In dataset we store under this path_in_repo subfolder
|
|
|
|
| 169 |
return None
|
| 170 |
print(f"[HuggingRun] Running: {cmd}")
|
| 171 |
try:
|
| 172 |
+
env = os.environ.copy()
|
| 173 |
+
env.setdefault("PORT", APP_PORT)
|
| 174 |
+
env.setdefault("APP_PORT", APP_PORT)
|
| 175 |
+
process = subprocess.Popen(
|
| 176 |
cmd,
|
| 177 |
shell=True,
|
| 178 |
+
env=env,
|
| 179 |
stdout=sys.stdout,
|
| 180 |
stderr=sys.stderr,
|
| 181 |
)
|
ubuntu-desktop/Dockerfile
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ubuntu 24.04 Desktop on HuggingRun — noVNC on 7860, persistence via /data
|
| 2 |
+
FROM ubuntu:24.04
|
| 3 |
+
|
| 4 |
+
ENV DEBIAN_FRONTEND=noninteractive
|
| 5 |
+
|
| 6 |
+
# System + Python (for sync)
|
| 7 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
+
ca-certificates curl python3 python3-pip python3-venv \
|
| 9 |
+
&& pip3 install --no-cache-dir --break-system-packages huggingface_hub \
|
| 10 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
# Desktop stack: Xvfb, XFCE, dbus, x11vnc, Firefox
|
| 13 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 14 |
+
xvfb \
|
| 15 |
+
xfce4 xfce4-goodies \
|
| 16 |
+
dbus-x11 \
|
| 17 |
+
x11vnc \
|
| 18 |
+
firefox \
|
| 19 |
+
procps \
|
| 20 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 21 |
+
|
| 22 |
+
# noVNC (web client on 7860)
|
| 23 |
+
RUN apt-get update && apt-get install -y --no-install-recommends git \
|
| 24 |
+
&& git clone --depth 1 https://github.com/novnc/noVNC.git /opt/noVNC \
|
| 25 |
+
&& git clone --depth 1 https://github.com/novnc/websockify /opt/noVNC/utils/websockify \
|
| 26 |
+
&& rm -rf /var/lib/apt/lists/* /opt/noVNC/.git
|
| 27 |
+
|
| 28 |
+
# HF Spaces run as user 1000
|
| 29 |
+
RUN useradd -m -u 1000 user
|
| 30 |
+
ENV HOME=/home/user
|
| 31 |
+
RUN mkdir -p /data && chown user:user /data
|
| 32 |
+
|
| 33 |
+
# HuggingRun scripts (build context = repo root)
|
| 34 |
+
COPY scripts /scripts
|
| 35 |
+
COPY ubuntu-desktop/start-desktop.sh /opt/start-desktop.sh
|
| 36 |
+
RUN chmod +x /scripts/entrypoint.sh /opt/start-desktop.sh
|
| 37 |
+
|
| 38 |
+
ENV PERSIST_PATH=/data
|
| 39 |
+
ENV RUN_CMD="/opt/start-desktop.sh"
|
| 40 |
+
ENV DESKTOP_HOME=/data/desktop-home
|
| 41 |
+
ENV DISPLAY=:99
|
| 42 |
+
ENV VNC_PORT=5901
|
| 43 |
+
ENV NOVNC_PORT=7860
|
| 44 |
+
|
| 45 |
+
USER user
|
| 46 |
+
EXPOSE 7860
|
| 47 |
+
ENTRYPOINT ["/scripts/entrypoint.sh"]
|
ubuntu-desktop/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ubuntu 桌面示例
|
| 2 |
+
|
| 3 |
+
本目录是 **HuggingRun 通用工具** 的一个示例:在 HF 上跑 Ubuntu + XFCE + noVNC,使用与主仓库相同的持久化与 entrypoint,仅 `RUN_CMD` 不同。
|
| 4 |
+
|
| 5 |
+
- **通用工具**:根目录的 `scripts/sync_hf.py`、`scripts/entrypoint.sh` 和主 Dockerfile 的 entrypoint。
|
| 6 |
+
- **本示例**:`Dockerfile` 在此目录,构建时从仓库根 COPY `scripts/`,并设置 `RUN_CMD=/opt/start-desktop.sh`;`start-desktop.sh` 启动 Xvfb + XFCE + x11vnc + noVNC(监听 7860),桌面 HOME 放在 `PERSIST_PATH/desktop-home`,由通用同步脚本持久化。
|
| 7 |
+
|
| 8 |
+
## 最小用法
|
| 9 |
+
|
| 10 |
+
1. 新建 Space 或 Duplicate HuggingRun,将 **Dockerfile** 换成本目录的 `Dockerfile` 内容(或从仓库根构建:`docker build -f ubuntu-desktop/Dockerfile .`)。
|
| 11 |
+
2. 设置 Secrets:`HF_TOKEN`、可选 `AUTO_CREATE_DATASET=true`。
|
| 12 |
+
3. 构建并运行,浏览器打开 Space 即可看到 noVNC 桌面;重启后状态由通用持久化保留。
|
| 13 |
+
|
| 14 |
+
维护重点在通用层;本示例仅做最小封装。
|
ubuntu-desktop/start-desktop.sh
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Start Ubuntu desktop: Xvfb + XFCE + x11vnc + noVNC on 7860
|
| 3 |
+
# HOME is set to persistent dir by caller (sync/entrypoint). Here we ensure and use it.
|
| 4 |
+
set -e
|
| 5 |
+
|
| 6 |
+
export PERSIST_PATH="${PERSIST_PATH:-/data}"
|
| 7 |
+
export DESKTOP_HOME="${DESKTOP_HOME:-$PERSIST_PATH/desktop-home}"
|
| 8 |
+
export DISPLAY="${DISPLAY:-:99}"
|
| 9 |
+
export VNC_PORT="${VNC_PORT:-5901}"
|
| 10 |
+
export NOVNC_PORT="${NOVNC_PORT:-7860}"
|
| 11 |
+
|
| 12 |
+
mkdir -p "$DESKTOP_HOME"
|
| 13 |
+
export HOME="$DESKTOP_HOME"
|
| 14 |
+
|
| 15 |
+
# Ensure minimal XFCE dirs
|
| 16 |
+
mkdir -p "$HOME/.config" "$HOME/.local/share" "$HOME/Desktop"
|
| 17 |
+
|
| 18 |
+
# Start Xvfb
|
| 19 |
+
Xvfb "$DISPLAY" -screen 0 1280x720x24 -ac +extension GLX +render -noreset &
|
| 20 |
+
XVFB_PID=$!
|
| 21 |
+
sleep 2
|
| 22 |
+
|
| 23 |
+
# Start dbus for session (user session only; no system dbus as we run as non-root)
|
| 24 |
+
dbus-daemon --session
|
| 25 |
+
|
| 26 |
+
# Start XFCE (lightweight)
|
| 27 |
+
startxfce4 &
|
| 28 |
+
DESKTOP_PID=$!
|
| 29 |
+
sleep 3
|
| 30 |
+
|
| 31 |
+
# x11vnc: share display :99 on port 5901
|
| 32 |
+
x11vnc -display "$DISPLAY" -rfbport "$VNC_PORT" -forever -shared -noxdamage -bg
|
| 33 |
+
|
| 34 |
+
# noVNC: web on 7860, proxy to VNC
|
| 35 |
+
if [ -x /opt/noVNC/utils/novnc_proxy ]; then
|
| 36 |
+
/opt/noVNC/utils/novnc_proxy --listen "$NOVNC_PORT" --vnc "localhost:$VNC_PORT"
|
| 37 |
+
else
|
| 38 |
+
# Fallback: websockify + simple http serve
|
| 39 |
+
(cd /opt/noVNC && ./utils/novnc_proxy --listen "$NOVNC_PORT" --vnc "localhost:$VNC_PORT")
|
| 40 |
+
fi
|