ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

软件定义 CDN 不是一组 NGINX:如何划分控制平面、数据平面与验证平面?

软件定义 CDN 不是一组 NGINX:如何划分控制平面、数据平面与验证平面? CDN 切换源站是一项常见的运维操作。旧源站需要维护、流量要迁移到新机房、进行蓝绿迁移或故障恢复或者把内容交付切换到新版上游处理链路。在这些变更中客户端使用的 URL 可以保持不变同一个地址需要开始返回预期的新版本内容。假设现在就有这样一次变更。对于某个交付地址我们需要从旧源站O_A切换到新源站O_B并开始返回新版本清单文件manifestM_B而不是旧版本M_A。控制器生成配置c1——也就是把上游切换到新源站O_B的配置——并发送给边缘节点。边缘节点应用配置后返回执行报告applied(c1)true这份报告的含义是对这个执行节点而言要求的配置已经被接受并生效。随后探测客户端沿着同一条交付路径发起普通请求。HTTP 状态码是 200但响应体仍然是旧版本M_A。边缘节点的报告完全可能是正确的c1的确已经应用。问题出现在下一步——系统把“配置已经应用”解释成了另一个结论客户端已经拿到了预期的新版本M_B。那么真正完成的到底是哪一步谁又有权宣布这次金丝雀验证步骤canary step已经成功下面沿着这条链路继续分析从确定期望状态到实际观测再到允许执行的修正动作以及修正后的再次验证。这是一个有明确前提的工程场景用来说明职责边界它不是实际测试记录或事故日志也不是某个具体 NGINX 部署的复盘。1. 配置已经变了缓存里的响应却没有变我们已经知道两份点播VOD清单文件的内容M_A是旧版本M_B是变更后预期的新版本。两者的完整响应体不同。重要的是期望值M_B在验证之前就已由发布任务确定而不是从缓存节点自己的报告里推导出来。这次只检查一条隔离的金丝雀验证路径一个边缘缓存节点E、一个 URLU、一套普通请求配置H以及选定媒体输出版本renditionR0所对应的清单文件。客户端观测点P位于边缘节点的缓存之后。这里的金丝雀验证只表示一个受限的检查步骤并不意味着已经允许把变更扩展到整个网络。服务所有者要求返回M_B由新源站O_B替代旧源站O_A。URL 和请求配置保持不变。但缓存中已经存在旧版本M_A。切换上游以后旧响应仍可被查找到响应选择规则没有变化缓存依然可以把它返回给客户端。边缘节点已经应用c1切换到新源站O_B的配置但客户端仍然拿到M_A——旧版本清单文件。偏差正是在这里出现的。这个请求配置对应的缓存键没有错误。这里没有再叠加语言变体混用、配置实际应用失败或其他故障也不是上游根本没有切换成功。我们只讨论一个机制配置已经生效但旧的可复用响应仍然能够被交付。还要注意相对于发布目标而言的“旧版本”不一定已经在 HTTP 语义上过期stale。RFC 9111 对已存储响应的复用规定了相应条件目标 URI、请求方法、Vary指定的请求字段以及其他适用约束都需要满足。满足这些条件时仍处于 HTTP 新鲜期fresh的响应可以不经过重新验证而继续复用但新鲜度不会覆盖no-cache等其他限制。[1, §§4–4.2]为什么只检查“文件还在不在”不够NGINX 官方文档给出的默认代理缓存键是proxy_cache_key $scheme$proxy_host$request_uri;其中$proxy_host与proxy_pass所确定的被代理服务器名称和端口有关。如果改变的是$proxy_host的值而表达式其他部分不变计算出的缓存键也会改变。旧文件即使仍在磁盘上也不代表新的请求还能命中它。[2,proxy_cache_key;$proxy_host]这并不意味着“任何后端切换都会改变$proxy_host”。它说明的是没有检查具体配置就不能把本文的场景说成 NGINX 的“默认行为”。本文需要的不是单纯的“旧文件还在”而是旧响应仍然能够被当前查找规则选中并复用。到这里推理中缺少的依据就很清楚了。c1 已应用回答的是配置执行问题客户端拿到 M_B回答的是实际交付问题。即使配置与当前任务的关联完全正确第一个事实也不会自动变成第二个事实。配置已生效不等于交付正确。Config applied ≠ delivery correct.这只是控制契约的开始不是结束。系统还需要取得决策依据并确定下一步允许执行什么动作。2. 三个平面不是三个进程而是三类责任在这里划分控制平面、数据与交付平面、验证平面不是按“有几个服务”来分而是看谁负责回答什么问题。平面在本文场景中的责任产出控制平面control plane授权变更目标将目标与配置、验证关联起来根据依据单独授权修正动作并决定是否通过金丝雀步骤的验收。变更意图、期望状态和控制决策。数据与交付平面data / delivery plane应用c1通过边缘节点和缓存处理普通客户端请求。执行报告与实际 HTTP 响应二者不是同一个结果。验证平面verification plane获取指定路径上的响应检查观测结果是否适用并将响应体与期望值比较。对这次交付的有限评估不是修改基础设施的授权。在控制平面内服务所有者给出I_g已授权的变更意图其中g是意图的版本。控制器据此形成D_g期望交付状态预期的M_B、目标源站O_B、检查范围以及观测、动作和验收的条件。c1只是执行这个目标时使用的一个配置制品不等于整个D_g。边缘节点返回的执行报告必须关联到具体的c1、节点和任务版本。否则即使看到“应用成功”也不能确定报告属于当前变更。但即便关联正确它仍然只是执行层面的报告。验证时控制器提供预期结果并指定一个普通客户端请求。探测客户端probe不会只去问边缘节点“现在是不是正确”而是沿着交付路径获取缓存之后的实际响应体。随后由评估责任方assessment authority这一逻辑角色将适用的观测结果与预期结果比较。这里区分观测结果observation、评估assessment和决策decision观测记录实际得到了什么评估说明它支持什么结论决策则确定下一步允许做什么。评估结论返回控制器但结论本身不授权缓存清理、回滚或扩大部署范围。动作授权和当前步骤的验收仍由控制器在自身权限内决定。验证比较“看到的结果”和“应该得到的结果”状态协调reconciliation则结合当前变更意图、执行状态与评估结论确定下一步允许执行的动作。评估返回控制器动作执行后再次观测反馈才形成闭环。还记得 Kubernetes 吗它的控制器模式controller pattern也有类似思路先观测当前状态再与期望状态比较然后决定下一步动作。这里只使用一个有限类比。Kubernetes 没有定义本文的三个 CDN 平面没有替我们规定验收条件也不能证明某个 CDN 项目已经实现了这套控制结构。[4,Controller pattern]这些逻辑角色可以运行在同一个进程中。职责分开也不意味着基础设施故障一定彼此独立。本文要求的独立性更窄期望值来自权威任务所确定的期望状态实际响应体来自客户端交付路径比较的两边不能都取自同一份节点自报信息。但这里马上出现下一个问题什么样的观测结果才有资格代表我们真正要检查的交付状态边缘节点的appliedtrue已经被证明不足以回答这个问题。在比较实际结果与期望结果之前必须先确定观测应满足哪些条件。什么样的观测结果可以用于评估S是这次的检查对象选定资源、请求配置、媒体输出版本以及通过边缘节点E到客户端观测点P的路径。要验证的属性Q_delivery很窄指定的普通 GET 请求得到完整的 HTTP 200 响应响应体与预先确定的M_B精确相等。参考内容M_B与实际获得的响应体必须以同一种预先约定的表示形式representation进行比较。请求配置H固定必要的请求头和内容编码content encoding。我们不使用Range不发送条件请求也不对响应做额外转换。探测客户端发起普通 GET将完整响应体与预先确定的M_B比较。观测范围只覆盖这个指定请求、它的完整响应体以及实际接收响应的时间区间。t_obs表示这次观测完成的时刻。这里检查的是清单文件的交付不是清单中所有媒体分片的获取或播放。HTTP 200 也不能替代响应体比较。RFC 9110 分别规定了请求与响应的关联以及消息的完整性。GET 返回 200 时响应内容表示目标资源但这个语义本身不会替我们完成与外部参考M_B的一致性检查。[3, §§6.1, 7.5, 15.3.1]C_check规定观测结果在什么条件下可以用于评估。观测必须属于当前g / D_g / S响应完整请求发生在相应配置应用或修正动作之后且从t_obs计算的观测年龄处于允许范围内还必须确认没有变更使这份观测失去适用性。时间戳很新并不自动意味着依据充分。重复发送旧报告也不会刷新原始观测时间。这里涉及的时钟可相互比较。任务当前有效且不存在会使观测失效的变更这两点都已得到确认而不是从系统没有报告变化推断出来。从获得支持正向评估的观测结果到作出决策相关上下文保持不变。本文不设定适用于所有系统的统一有效期。请求是否确实经过目标边缘节点也需要单独建立依据。相同的响应体或某个任意响应头不能证明请求经过了指定路径。直接请求新源站O_B或只为探测客户端绕过缓存检查的都是另一条路径。对于满足C_check的观测如果实际得到的是M_A旧版本而不是M_B预期的新版本就可以对这个响应给出MISMATCH不匹配。如果响应属于另一个任务、另一条路径或者缺少必要的绑定信息这份观测就不适用于当前检查。在没有其他充分依据的情况下评估结果才是UNKNOWN未知而不是MISMATCH。现在我们有了比appliedtrue更具体的事实客户端实际拿到M_A而预期的是M_B。在上述适用条件下这足以确认交付不匹配却仍不足以自动修改 CDN。接下来的问题是谁有权根据这个MISMATCH授权动作动作完成后又需要什么新的依据才能通过这一步的验收3. 从 MISMATCH 到允许执行的动作再到新的结果先区分三个环节。客户端拿到M_A是一个观测事实。确认这份观测适用于当前检查再将响应体与预期M_B比较才得到交付评估在这里评估结论是MISMATCH。至于为什么返回旧版本则需要单独诊断。在本文场景中另有诊断依据确认旧缓存条目的复用正是这次不匹配的原因。比较说明交付是否符合预期诊断支持修正动作的选择二者不能互相替代。将a1定向修正动作定义为在边缘节点E上仅使旧的U/H缓存条目不再被复用U/H对应前面选定的 URL 和请求配置。这是对动作所需效果的描述不是某个厂商 API 的名称。允许执行a1之前控制器需要确认当前I_g仍然有效自己拥有修改这条记录的权限所有者没有批准保留旧结果的例外缓存复用这一原因已经建立且动作影响范围明确。同时配置c1继续有效目标源站仍是O_B。这条隔离的金丝雀路径允许进行新的缓存填充fill。全局清理、修改 URL、单纯等待 TTL 到期或只让探测客户端绕过缓存都不是同一个a1。如果权限不存在、原因不确定或影响范围不清楚一个MISMATCH本身不能授权自动清理。验收关口保持未通过状态问题交回所有者处理。停止交付、回滚或向全网扩散修正动作也不能由这个评估自动推出。不能藏在“清理缓存”背后的条件在选定的执行序列中不存在来自旧源站O_A的尚未完成的旧填充能在a1之后重新让M_A进入可复用状态。这是本次序列的条件不是通用保证更不是说缓存清理purge天生就能阻止所有并发的旧填充。甚至“使缓存失效”invalidate也不一定意味着物理删除文件。RFC 9111 允许删除已存储响应或要求下次复用前必须重新验证。但相关章节讨论的是修改资源的 HTTP 请求所引起的失效处理没有规定本文管理动作a1的产品语义。[1, §4.4]对于真实产品仍需单独确认哪个机制实现所需效果适用哪些并发条件对旧填充有什么具体保证。本文没有建立这样的通用产品保证。后来看到一次正确的M_B也不能反过来证明旧填充永远不会恢复旧对象。客户端实际得到了什么与动作条件是否满足是两个不同的依据问题。在下面的正向序列中a1已经实现规定的效果。这是该场景中单独给定的事实不是从任意执行确认消息推导出的结果。即使动作已经完成仍需要一个新的普通客户端请求和新的评估。清掉缓存就立刻宣布成功就像重启服务后仅仅因为命令执行成功就直接把故障单关闭。动作完成不等于结果已经被验证。到这里三种责任已经分开appliedtrue不证明交付正确MISMATCH表明偏差却不自行授权修正修正完成后还需要新的观测结果。现在转向实际决策系统到底要满足哪些条件才能通过这个金丝雀步骤的验收什么条件允许通过金丝雀步骤的验收C_canary金丝雀步骤的验收规则在验收之前确定。在本次序列中它要求当前任务有效配置c1确实完成应用动作a1按规定完成指定的修正后请求所产生的观测通过C_check并支持正向评估以及控制器当前仍有通过该步骤验收的权限。C_canary的范围比完整实现I_g整个变更意图更窄它只验收这一个选定的交付检查步骤。它不证明实际请求必定经过O_B不表示所有边缘节点或媒体分片都已验证也不允许立即扩大部署范围。这是本文对有限场景设定的验收契约不是 HTTP 或 Kubernetes 给出的通用规则。现在展开所有九个步骤。任务版本、检查对象和预期M_B在序列中保持一致。下表描述的是有明确前提的场景不是已执行网络实验的结果。步骤发生了什么当前能够得出的结论T0边缘节点E中已有旧版本M_A。当前I_g / D_g、C_check、C_canary以及定向动作的权限已经确定。预期是M_B但确定目标并不证明交付。T1边缘节点确实应用c1上游切到O_B旧条目仍可复用。节点返回applied(c1)true。配置确实应用金丝雀步骤尚未通过验收。T2普通请求q_-修正前的检查请求按指定路径和请求配置取得 HTTP 200 与完整M_A形成观测结果E_-。通过C_check后评估结论为A_- MISMATCH。T3控制器检查当前任务和范围单独的诊断确认缓存复用是原因。验收尚未通过不匹配本身不授权修正。T4定向修正动作的条件全部满足。控制器授权a1原来的负向评估不变。T5边缘节点按规定完成a1并报告完成。c1继续有效目标源站仍为O_B。动作已完成但尚无新的客户端响应依据。T6新的普通请求q_修正后的检查请求走同一路径。在本次序列中新的填充提供M_B客户端取得 HTTP 200 与完整响应体。形成独立的观测结果E_保留自身的请求和观测时间。T7评估责任方按C_check检查E_的适用性再与同一个M_B比较。A_ MATCH匹配该评估可用于当前决策。T8控制器再次检查意图版本g的当前有效性、验收权限和全部C_canary条件。通过这一个金丝雀步骤的验收依据包括执行情况和新的E_ / A_。正向结论不是从“清理完成所以应该好了”推出来的。T6 中实际取得M_B是一个新的观测事实E_通过C_check检查与同一参考M_B比较才支持A_ MATCH。控制器还要把这份评估与其他验收条件一起检查。正确的结果声明需要保留这个范围例如对当前I_g依据C_canary通过一个金丝雀步骤的验收在c1已应用、a1已获授权并按规定完成后新的普通请求q_沿指定交付路径取得完整M_B。决策依据包括E_ / A_并保留其实际范围和观测时间。这不表示所有边缘请求都拿到了M_B、所有媒体分片都已验证或整个 CDN 已验证也不表示仅凭响应体一致就证明实际使用了O_B。关于路由或源站身份的断言需要单独的支持依据。后续成功不会改写E_-修正前的观测结果此前的不匹配仍是历史事实。已经发生的验收也保留为历史决策。但要继续用旧的正向观测支持“当前交付仍然正确”它就必须继续满足C_check。重复转发报告不会延长依据的有效期反过来有效期结束也不会追溯撤销过去的验收。4. 两个可以直接检查控制闭环的方法我们已经明确了金丝雀步骤的验收条件。接下来把这些规则落到两项系统检查上。第一项检查系统有没有把“配置已应用”误当成“客户端已拿到正确内容”第二项检查系统有没有把“修正动作已完成”误当成“修正结果已验证”检查一从变更意图一直追到客户端响应取当前I_g / D_g已授权的意图与期望状态、真实应用的c1以及仍可复用的旧版本M_A。顺着链路检查参考M_B的来源、执行报告关联的配置制品与节点和任务版本以及普通请求q_-的完整响应体从哪里取得、对应观测是否满足C_check。预期结果是applied(c1)true但这个响应的评估是MISMATCH金丝雀步骤不通过验收。不要只看内部记录还要检查主要界面状态UI和 API 响应。“配置已应用”不能在另一个层面变成“M_B已成功交付”。再做负向替换用直接请求源站、另一套请求配置或另一个任务版本取得的响应替换当前观测。这份观测不能支持对当前检查对象S的MISMATCH。在没有其他充分依据的情况下评估结果应保持UNKNOWN。检查的重点不只是能否比较响应体而是评估有没有绑定到正确的对象、范围和交付路径。检查二从授权动作一直追到新的验收依据沿用同一序列从A_- MISMATCH开始。先检查诊断是否成立、动作权限当前是否有效以及a1影响的缓存条目是否明确。动作完成后继续追踪新的普通请求q_、新的观测结果E_、新的评估A_以及控制器单独作出的验收决策。满足C_canary时预期结果是新响应得到MATCH随后才有单独的金丝雀验收决策。API 和界面仍应保留对同一任务及决策依据的关联。“缓存已清理完成”的执行报告不能替代E_。同样“响应体已正确”也不能替代动作权限、任务当前有效性或验收权限的检查。撤销执行a1的权限系统就不应自动清理。用旧执行报告或绕过缓存的响应替代E_也不应据此通过本次验收。这些是检查契约的方法不是本文已经完成的运行时测试。这两项检查给出了实际的职责划分标准执行报告说明执行客户端响应体支持对指定交付属性的评估诊断支持原因判断控制决策则授权下一步或通过验收。如果平台把这些不同依据压缩成一个无条件的绿色“成功”丢失的就不只是一个字段而是控制语义。标题里的“不是一组 NGINX”不是否定 NGINX 作为执行组件。真正的问题是平台在执行器周围定义了哪些控制责任。软件定义 CDN 的变更闭环不应停在“配置已下发”或“一次探测看起来正常”。在本文的有限场景中系统需要把当前变更意图、执行、独立观测、范围明确的评估以及单独获得授权的动作和决策连接起来。这样“配置已生效”才不会被错误地等同于“交付结果符合预期”。参考资料资料截点为2026 年 10 月 1 日。本文沿用这些资料所支持的有限表述产品文档不等于对某个具体二进制版本、配置或运行场景的验证。[1]R. Fielding, M. Nottingham, J. Reschke.HTTP Caching. RFC 9111, STD 98, June 2022. §§4–4.2; §4.4.[2]NGINX.ngx_http_proxy_module.proxy_cache_key;$proxy_host.[3]R. Fielding, M. Nottingham, J. Reschke.HTTP Semantics. RFC 9110, STD 97, June 2022. §6.1; §7.5; §15.3.1.[4]Kubernetes Documentation.Controllers.Controller pattern.
返回列表