Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
基础设施与运维阅读约 12 分钟

去中心化托管:架构、验证与运维权衡

一份技术指南,介绍去中心化 Web 托管的工作原理、如何跨网关和对等节点验证内容可用性,以及该模型在生产环境中失效的场景。

TL;DR

去中心化托管用内容寻址存储和点对点检索网络取代单一源站服务器,因此任何持有数据的节点都可以提供内容。本文解释了从内容标识符到浏览器的检索路径,提供了固定、网关验证和对等节点诊断的可运行流程,并定义了团队在迁移站点前应收集的测量指标。文章还涵盖了中心化托管所隐藏的故障模式,例如未固定内容、网关不一致和可变名称解析。目标是提供可复现的评估方法,而非推荐特定提供商。

范围与去中心化托管的思维模型

去中心化托管是一种交付模型,其中网站文件由独立节点网络存储和提供,而非单一源站服务器。两个核心机制是内容寻址(文件由其字节的加密哈希标识)和点对点检索(客户端向多个节点请求该哈希背后的数据块)。去中心化 Web 托管和去中心化网站托管通常还会在上面增加一个命名层,因为人类需要稳定的 URL,而内容哈希在文件更改时会变化。

实际后果是托管变成了两个独立的问题:持久性(保持至少一个节点愿意存储内容)和解析(将人类可读名称映射到当前内容标识符)。一个站点今天可能完全可检索,明天就可能消失,如果没有节点固定它——这是最常见的运维意外。本文将这两个问题分开处理,并为每个问题提供验证步骤。

  • 内容层:文件被分块、哈希,并通过内容标识符(CID)寻址。
  • 持久层:固定服务或自运行节点保持数据块可用。
  • 解析层:可变名称(DNSLink、链上注册表或命名系统)指向当前 CID。
  • 检索层:网关或本地节点从对等节点获取数据块并重新组装文件。

迁移站点前的假设与前提条件

以下流程假设你已有静态站点构建输出(HTML、CSS、JavaScript、图片),并且机器上装有 curl、Node.js 和 Python 3。还假设你可以运行本地 IPFS 兼容节点或连接固定服务;命令使用 RPC_URL 和 PINNING_API_URL 等占位符,而非虚构的公共端点。如果你的站点依赖服务端渲染、认证 API 或关系数据库,去中心化托管仅覆盖静态交付层,你仍需要在其后部署常规计算资源。

迁移前,按类型和大小清点所有资产,记录每次部署中哪些文件会变化,并决定站点需要可变名称还是可以容忍每次发布使用新 CID。跳过这一步的团队通常会发现问题:他们的构建在每次部署时都会生成带哈希的文件名,这破坏了缓存假设,并使不可变寻址不如预期有用。

  • 一个可以哈希并上传的静态构建目录。
  • 固定服务 API 密钥或具有持久存储的自托管节点。
  • 一个你控制或信任的网关 URL,用于验证。
  • 命名决策:每次发布使用不可变 CID,还是每次部署更新可变记录。

从内容标识符到浏览器的检索路径

当浏览器通过网关请求内容时,网关解析 CID,检查本地块存储,如果块缺失,则通过分布式哈希表和直接提供者记录查询对等节点。然后网关在将字节返回给客户端之前,根据 CID 中嵌入的哈希验证每个块。这种验证是中心化托管默认缺乏的安全属性:恶意对等节点无法在不破坏哈希的情况下替换不同内容。

该路径比 CDN 有更多跳数,因此延迟取决于对等节点可用性和网关缓存。已缓存该 CID 的网关响应迅速;冷 CID 可能需要数秒来发现对等节点。对于去中心化网站托管,这意味着部署后的第一个访问者通常承担发现成本,后续访问者则受益于网关缓存。在得出性能结论之前,测量冷检索和热检索都至关重要。

使用可运行命令固定并发布静态构建

固定是告诉节点无限期保留特定内容的行为。以下 shell 流程发布构建目录,捕获生成的 CID,并通过固定服务 API 固定它。请将占位符替换为你自己的值;不要将生产密钥粘贴到共享 shell 中。

set -euo pipefail

BUILD_DIR="./dist"
PINNING_API_URL="https://your-pinning-service.example/api/v0"
PINNING_TOKEN="$PINNING_TOKEN"

# Add the directory recursively and capture the root CID
ADD_RESPONSE=$(curl -s -X POST "$PINNING_API_URL/add" \
  -H "Authorization: Bearer $PINNING_TOKEN" \
  -F "file=@${BUILD_DIR}" \
  -F "recursive=true")

ROOT_CID=$(printf '%s' "$ADD_RESPONSE" | python3 -c 'import sys,json; print(json.load(sys.stdin)["Hash"])')
echo "Published root CID: $ROOT_CID"

# Pin the root explicitly so it survives garbage collection
curl -s -X POST "$PINNING_API_URL/pin/add?arg=${ROOT_CID}" \
  -H "Authorization: Bearer $PINNING_TOKEN"

echo "$ROOT_CID" > .last-published-cid

验证网关一致性与内容完整性

一次成功获取只能证明某个网关当时拥有该内容。完整性验证需要从至少两个独立网关获取相同 CID 并比较字节。以下脚本从两个网关按 CID 检索文件,计算每个响应的 SHA-256,并报告是否匹配。它还检查 HTTP 状态,以免将返回错误页面的网关误认为内容不匹配。

import hashlib, os, sys, urllib.request

CID = os.environ.get("CID", "")
PATH = os.environ.get("PATH_IN_SITE", "index.html")
GATEWAYS = [
    os.environ.get("GATEWAY_A", ""),
    os.environ.get("GATEWAY_B", ""),
]

if not CID or not all(GATEWAYS):
    sys.exit("Set CID, GATEWAY_A, and GATEWAY_B")

def fetch(base):
    url = f"{base.rstrip('/')}/ipfs/{CID}/{PATH}"
    req = urllib.request.Request(url, headers={"User-Agent": "hosting-verify/1.0"})
    with urllib.request.urlopen(req, timeout=30) as resp:
        body = resp.read()
        return resp.status, hashlib.sha256(body).hexdigest(), len(body)

results = []
for gw in GATEWAYS:
    try:
        status, digest, size = fetch(gw)
        results.append((gw, status, digest, size))
        print(f"{gw} status={status} sha256={digest} bytes={size}")
    except Exception as exc:
        print(f"{gw} ERROR {exc}")

if len(results) == 2 and results[0][2] == results[1][2]:
    print("MATCH: gateways returned identical bytes")
else:
    print("MISMATCH or incomplete: investigate before trusting the deployment")

托管评估的结果表流程

将每次验证运行记录在表格中,以便区分冷热行为。不要凭记忆填写数字;运行流程并粘贴实际观察结果。一份可辩护的评估表应包含这些列,并且每对网关至少五行,在不同时间段采集。

运行CID网关冷/热HTTP 状态SHA-256 匹配首字节时间 (ms)总字节数备注
1<CID><GATEWAY_A>冷<status><yes/no><measured><measured>固定后首次获取
2<CID><GATEWAY_B>冷<status><yes/no><measured><measured>独立网关
3<CID><GATEWAY_A>热<status><yes/no><measured><measured>重复获取
4<CID><GATEWAY_B>热<status><yes/no><measured><measured>重复获取
5<CID><GATEWAY_A>热<status><yes/no><measured><measured>24 小时后

只有当同一发布的 CID 列在各行中完全相同时,表格才有意义。如果你的部署流水线每次构建都生成新 CID,请分别标记每个发布,并在发布内比较,而非跨发布比较。

诊断未固定内容和对等节点发现失败

最常见的去中心化托管事故是内容已添加但从未固定,因此在添加节点垃圾回收其块存储后变得不可用。第二常见的是网关无法为某个 CID 找到对等节点,因为内容仅存储在 NAT 后的节点上且没有提供者记录。两者对用户的表现相同:超时或网关返回 504。

按此顺序诊断:确认 CID 已在至少一个常开节点上固定,向固定服务查询固定状态,然后尝试通过本地节点而非网关检索。如果本地节点能检索到内容但网关不能,问题在于网关侧缓存或对等连接,而非持久性。如果两者都无法检索,内容实际上已丢失,必须重新添加并重新固定。

  • 在假设网络故障之前,通过固定 API 检查固定状态。
  • 通过本地节点检索,以区分持久性和网关行为。
  • 确认 URL 中的 CID 与你固定的 CID 匹配;一个字符差异就会改变哈希。
  • 对于可变名称,确认名称记录指向预期 CID 且未过期。

可变名称、缓存与部署语义

不可变 CID 在完整性方面非常出色,但对于会变化的站点则不太方便。可变命名层通过存储每次部署时更新的指针来解决这个问题。权衡是指针引入了具有自身缓存和传播延迟的解析步骤,客户端缓存旧指针的时间可能比预期更长。当你更新名称记录时,请从新客户端和之前解析过旧值的客户端验证解析。

缓存与内容寻址的交互方式也会让团队感到意外:由于 CID 不可变,网关可以积极缓存它们,这对重复访问者有利,但意味着回滚到之前的 CID 可能会提供过时的网关缓存。规划部署时,让回滚目标指向已知良好的 CID,并通过上述相同的网关一致性流程进行验证。

与中心化托管相比的局限性和权衡

去中心化托管并非适用于所有工作负载的即插即用替代品。动态服务器逻辑、认证会话和数据库支持的页面仍需要常规计算资源,去中心化层通常作为静态交付层位于前端。性能也比调优良好的 CDN 更难以预测,因为检索取决于对等节点可用性和网关缓存,因此延迟敏感的应用应测量冷启动行为,而不是假设热缓存数字。

在运维上,该模型转移了责任:你必须自行监控固定状态、名称解析和网关一致性,因为没有单一提供商仪表板能覆盖所有三项。好处是抗审查和构造上的完整性验证;成本是持久性是你执行的策略,而非你购买的特性。团队应在迁移前明确决定自己需要哪些。

  • 持久性的可靠性取决于你的固定策略和监控。
  • 冷检索延迟可能显著高于热检索。
  • 可变命名增加了传播延迟和缓存复杂性。
  • 服务端逻辑仍需要常规基础设施。

生产托管评估的后续步骤

从一个静态站点开始,运行固定、网关一致性和结果表流程一周后再做决定。将固定状态和名称解析作为受监控的检查项,而非一次性手动步骤。如果你的应用还依赖读取链状态或提供 API 响应,请将静态托管层与可靠的 RPC 访问配对;OnFinality API 服务和专用节点选项覆盖计算侧,而网络目录可帮助你确认需要哪些链。具体到端点选择,RPC Assistant 关于区块链节点托管和多链 RPC 端点的指南是对本托管方法的补充,而非替代。

最后,记录你的回滚路径:最后已知良好的 CID、名称记录更新流程,以及值班工程师应运行的验证命令。只有当新工程师仅凭该文档就能复现验证步骤时,去中心化托管部署才算生产就绪。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始