Spaces:
Runtime error
Runtime error
|
Download ARCHITECTURE.md from vftfnn/space-test: direct link, hf CLI and curl.
- Browser
- Download file 11.2 kB
-
https://huggingface.co/spaces/vftfnn/space-test/resolve/main/ARCHITECTURE.md
- Command line
-
hf download hf://spaces/vftfnn/space-test/ARCHITECTURE.md
-
curl -L -o ARCHITECTURE.md https://huggingface.co/spaces/vftfnn/space-test/resolve/main/ARCHITECTURE.md
11.2 kB
零成本生产级 Spring Boot 部署架构
基于 HuggingFace Space Docker + 免费基础设施的完整方案。
架构拓扑
用户
↓
CloudFlare(自定义域名 + DDNS + CDN + WAF)
↓
HuggingFace Space Docker(Spring Boot 容器,2 vCPU / 16GB RAM)
↓
├─→ SQLPub MySQL(免费 500MB 存储 / 100 连接)
└─→ Upstash Redis(免费 10K 命令/天 / 256MB)
关键特性:
- 零费用:HF Space 免费持久 Docker 容器(非 Serverless,24x7 运行);数据库 / Redis / 域名全免费档
- 真实公网 IP + 自定义域名:通过 CloudFlare DDNS 把 Space 的动态
.hf.space域名映射到你的域名 - 生产可用性:500MB MySQL(小应用够用)、Redis 缓存、CloudFlare CDN + WAF
组件清单与注册
| 组件 | 用途 | 免费额度 | 注册入口 |
|---|---|---|---|
| HuggingFace Space | 容器运行时 | 2 vCPU + 16GB RAM 持久容器 | https://huggingface.co/new-space |
| SQLPub MySQL | 关系数据库 | 500MB 存储 / 100 并发连接 | https://console.sqlpub.com/user/login |
| Upstash Redis | 缓存 / Session | 10K 命令/天 / 256MB 内存 | https://console.upstash.com/auth/sign-in |
| CloudFlare | 域名解析 + CDN + DDNS | 无限流量 / 免费 SSL | https://dash.cloudflare.com/ |
详细配置步骤
1. SQLPub MySQL
- 注册 https://console.sqlpub.com/ → 创建数据库(选 MySQL 5.7+)
- 在控制台获取连接信息:
主机:<host>.sqlpub.com 端口:3306 数据库名:<dbname> 用户名:<username> 密码:<password> - JDBC URL(供 Spring Boot 用):
jdbc:mysql://<host>.sqlpub.com:3306/<dbname>?useSSL=true&serverTimezone=UTC
限制:
- 500MB 存储(约 100 万小条记录)
- 100 并发连接(对单容器应用足够)
- 外网访问慢(约 100-300ms 延迟);如需低延迟,考虑付费升级或自建
2. Upstash Redis
- 注册 https://console.upstash.com/ → Create Database → 选 Global(最低延迟)
- 拿连接信息:
UPSTASH_REDIS_REST_URL=https://xxx.upstash.io UPSTASH_REDIS_REST_TOKEN=AXXXxxxxx - Spring Boot 集成:用 Jedis 或 Lettuce 客户端,配置 host/port/password(见下方 application.yml)
限制:
- 10,000 命令/天(约每 8.6 秒一条,对低频应用够用)
- 256MB 内存
- 超额后节流(不会直接拒绝,但会变慢)
优化建议:
- Session 存 Redis 时设 TTL(如 30 分钟),自动过期省配额
- 低频场景(如缓存配置、限流计数器)适合;高频实时计算不适合
3. CloudFlare 域名 + DDNS
3.1 域名托管到 CloudFlare
- 在域名注册商(如 Namesilo / Porkbun)购买域名(约 $2-10/年)
- 登录 https://dash.cloudflare.com/ → Add Site → 输入域名
- 按提示修改域名 NS 记录指向 CloudFlare(如
ns1.cloudflare.com/ns2.cloudflare.com)
3.2 DDNS(动态更新 HF Space IP)
原理:HF Space 的公网地址是 <用户名>-<空间名>.hf.space(动态,可能变化)。通过 CloudFlare API + DDNS 脚本,定期解析 .hf.space 的真实 IP 并更新到你的域名 A 记录,实现自定义域名访问。
参考方案(博客详解):
核心步骤:
- 在 CloudFlare 获取 Zone ID 和 API Token(权限:Zone.DNS Edit)
- 在 HF Space 容器内 or 定时任务服务器(如 GitHub Actions / 自己的服务器)跑 DDNS 脚本:
#!/bin/bash DOMAIN="yourdomain.com" SUBDOMAIN="api" # 最终域名 api.yourdomain.com HF_SPACE_URL="<用户名>-<空间名>.hf.space" # 解析 HF Space 当前 IP CURRENT_IP=$(dig +short "$HF_SPACE_URL" | tail -1) # 调 CloudFlare API 更新 A 记录 curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{\"type\":\"A\",\"name\":\"$SUBDOMAIN\",\"content\":\"$CURRENT_IP\",\"ttl\":120}" - 定时执行(如 cron 每 5 分钟):HF Space IP 变化后自动同步
优势:
- CloudFlare 免费 CDN + DDoS 防护 + 免费 SSL
- 自定义域名(
api.yourdomain.com)比.hf.space更专业
4. HuggingFace Space 环境变量
敏感信息(数据库密码 / Redis token)不能硬编码,用 Space Secrets:
- 进入 Space 设置页 → Repository secrets
- 添加变量(Spring Boot 会通过环境变量读取):
DB_HOST=<host>.sqlpub.com DB_PORT=3306 DB_NAME=<dbname> DB_USER=<username> DB_PASSWORD=<password> REDIS_HOST=<xxx>.upstash.io REDIS_PORT=6379 REDIS_PASSWORD=<token> CF_ZONE_ID=<zone_id> CF_API_TOKEN=<api_token> CF_RECORD_ID=<dns_record_id>
Spring Boot 配置示例
src/main/resources/application.yml:
spring:
application:
name: hf-springboot-demo
# SQLPub MySQL
datasource:
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:test}?useSSL=true&serverTimezone=UTC&characterEncoding=utf8
username: ${DB_USER:root}
password: ${DB_PASSWORD:password}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10 # SQLPub 免费限 100 连接,单容器 10 足够
connection-timeout: 30000
# JPA(可选,如果用 ORM)
jpa:
hibernate:
ddl-auto: update # 生产改 validate
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
# Upstash Redis
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
timeout: 5000
lettuce:
pool:
max-active: 8 # 连接池大小
max-idle: 4
min-idle: 1
server:
port: 7860 # HF Space 要求
address: 0.0.0.0
依赖(pom.xml 新增):
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Spring Data JPA(可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
DDNS 自动更新脚本(容器内定时任务)
src/main/java/com/example/hfspace/task/CloudFlareDDNSTask.java:
package com.example.hfspace.task;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
@Component
public class CloudFlareDDNSTask {
@Value("${cf.zone-id:}")
private String zoneId;
@Value("${cf.api-token:}")
private String apiToken;
@Value("${cf.record-id:}")
private String recordId;
@Value("${hf.space-url:}")
private String hfSpaceUrl;
private final RestTemplate restTemplate = new RestTemplate();
// 每 5 分钟检查一次
@Scheduled(fixedRate = 300000)
public void updateDNS() {
if (zoneId.isEmpty() || hfSpaceUrl.isEmpty()) {
return; // 未配置,跳过
}
try {
// 解析 HF Space 当前 IP
String currentIP = InetAddress.getByName(hfSpaceUrl).getHostAddress();
// 调 CloudFlare API 更新
String url = String.format("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s",
zoneId, recordId);
Map<String, Object> body = new HashMap<>();
body.put("type", "A");
body.put("content", currentIP);
body.put("ttl", 120);
var headers = new org.springframework.http.HttpHeaders();
headers.set("Authorization", "Bearer " + apiToken);
headers.set("Content-Type", "application/json");
var entity = new org.springframework.http.HttpEntity<>(body, headers);
restTemplate.exchange(url, org.springframework.http.HttpMethod.PUT, entity, String.class);
System.out.println("DDNS updated: " + currentIP);
} catch (Exception e) {
System.err.println("DDNS update failed: " + e.getMessage());
}
}
}
启用定时任务(主类加 @EnableScheduling):
@SpringBootApplication
@EnableScheduling
public class HfSpaceApplication {
public static void main(String[] args) {
SpringApplication.run(HfSpaceApplication.class, args);
}
}
部署流程
本地测试:
export DB_HOST=<host>.sqlpub.com export DB_USER=<user> export DB_PASSWORD=<password> export REDIS_HOST=<xxx>.upstash.io export REDIS_PASSWORD=<token> mvn spring-boot:run推送到 HF Space:
git clone https://huggingface.co/spaces/<用户名>/<空间名> # 拷贝所有文件(Dockerfile / pom.xml / src / README.md) git add . git commit -m "add mysql/redis integration + DDNS" git push配置 Space Secrets:进入 Space 设置页添加环境变量(DB_HOST / DB_PASSWORD / REDIS_HOST 等)
验证:
- 访问
https://<用户名>-<空间名>.hf.space/api/hello应返回 JSON - 日志里看到 "DDNS updated: x.x.x.x" 说明定时任务工作
- 几分钟后通过自定义域名
https://api.yourdomain.com/api/hello也能访问
- 访问
限制与成本边界
| 维度 | 免费额度 | 突破方式 |
|---|---|---|
| HF Space CPU/RAM | 2 vCPU + 16GB(持久容器) | 升级 Pro($9/月,8 vCPU + 64GB) |
| MySQL 存储 | 500MB | SQLPub 付费版 or 迁移 PlanetScale / Supabase(也有免费档) |
| Redis 命令数 | 10K/天(≈ 0.12 QPS) | Upstash 付费版 or 自建 Redis(需额外服务器) |
| 域名 | 年付 $2-10 | 续费即可 |
| 流量 | CloudFlare 无限 | N/A |
适用场景:
- 个人项目 / MVP / 低流量 API(< 1K DAU)
- 学习 / 展示 / 开源项目托管
- 不适合:高并发生产(DB/Redis 配额不够)、对数据安全性要求极高的场景(托管服务风险)
进阶优化
- 监控:集成 Sentry(错误追踪)+ Uptime Robot(健康检查)
- CI/CD:GitHub Actions 自动推送到 HF Space(避免手动 git push)
- 多环境:本地 dev 用 H2 内存库,Space 用 MySQL(通过 Spring Profile 切换)
- 备份:定期
mysqldump导出 SQLPub 数据到 GitHub private repo
参考链接
- SQLPub 控制台:https://console.sqlpub.com/
- Upstash Redis:https://console.upstash.com/
- CloudFlare Dashboard:https://dash.cloudflare.com/
- CloudFlare DDNS 博客:https://blog.qjyg.de/post/tech/domain-dpdns/
- HuggingFace Spaces Docs:https://huggingface.co/docs/hub/spaces-sdks-docker