news 2026/10/12 1:44:54

kgateway 策略状态与合并上报:基于 Attached 条件与 MergeOrigins 元数据的诊断体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
kgateway 策略状态与合并上报:基于 Attached 条件与 MergeOrigins 元数据的诊断体系
  • API网关
  • 云原生
  • 微服务

【免费下载链接】kgateway

The Cloud-Native API Gateway and AI Gateway

项目地址:https://gitcode.com/gh_mirrors/kg/kgateway
点击查看免费下载

kgateway(The Cloud-Native API Gateway and AI Gateway)的策略 CR 基于 Kubernetes Gateway API 的gwv1.PolicyStatus上报状态。但由于策略的挂载(attachment)机制涉及按优先级应用、合并以及被更高优先级策略覆盖等复杂语义,仅仅一个笼统的状态无法区分"策略已被接受"和"策略已实际生效"。本文围绕设计文档 design/11741-policy-status-and-merge-reporting.md,系统讲解 kgateway 如何通过Accepted与Attached两个自定义 Condition 分离上报策略的接受与挂载状态,并在下发的 Envoy 配置中以MergeOrigins元数据记录每个合并字段的来源策略,为策略合并结果提供可观测性。读完本文,你将掌握 kgateway 策略状态字段的完整语义、三种挂载状态的判定规则与优先级顺序,以及如何从 Kubernetes status 与 Envoy 配置中诊断"已接受但未生效"的策略问题。

背景:为什么需要区分"接受"与"挂载"

kgateway 的各类策略 CR(如 TrafficPolicy、ListenerPolicy、BackendConfigPolicy、DirectResponse 等)都使用gwv1.PolicyStatusAPI 作为状态载体。在 api/v1alpha1/shared/shared_types.go 中可以看到,PolicyStatus由Conditions(条件列表,最多 8 条)与Ancestors(祖先状态条目,最多 16 个)组成:

type PolicyStatus struct { // +optional // +listType=map // +listMapKey=type // +kubebuilder:validation:MaxItems=8 Conditions []metav1.Condition `json:"conditions,omitempty"` // +kubebuilder:validation:MaxItems=16 // +required Ancestors []PolicyAncestorStatus `json:"ancestors"` }

PolicyAncestorStatus通过AncestorRef(对应 spec 中的 ParentRef)、ControllerName(写状态的控制器的域名/路径标识)与Conditions描述策略相对于某个祖先的状态。

设计文档指出,旧版状态 API 存在一个根本性局限:它无法区分"策略被接受"与"策略被挂载"。策略挂载是一个复杂过程:

  • 多个策略可能以优先级顺序依次应用到同一目标资源;
  • 支持合并的策略(如 TrafficPolicy)会与其他策略做合并,最终挂载到目标资源上的可能是多个策略合并后的结果;
  • 优先级较低策略中的部分或全部字段,在最终挂载结果中可能被更高优先级策略覆盖。

这会导致一个令人困惑的场景:策略被接受(Accepted)了,但由于与其他策略冲突而没有被实际挂载。只有"Accepted"而没有"Attached"维度的状态,会让用户看到策略一切正常,实际上配置却完全没有生效。

设计目标与非目标

目标

  • 使用自定义 Condition 分别上报策略的接受状态(acceptance)与挂载状态(attachment)。
  • 使用AttachedCondition 汇总挂载信息。
  • 在下发的 Envoy 配置上增加MergeOrigins元数据,作为诊断用途。

非目标

  • 不在状态 API 中暴露细粒度或高基数(high cardinality)的数据。
  • 不针对策略 CR 上无法解析的targetRefs上报状态。

核心实现:策略状态条件类型与原因

在 api/v1alpha1/shared/policy_types.go 中定义了完整的条件类型与原因常量,这是整个状态报告体系的类型基础:

const ( // PolicyConditionAccepted 表示策略是否被系统接受或被拒绝,以及原因。 // 该条件为 True 的可能原因:* Valid // 该条件为 False 的可能原因:* Pending、* Invalid、* TargetNotFound PolicyConditionAccepted PolicyConditionType = "Accepted" // PolicyConditionAttached 表示策略是否已挂载到目标资源。 // 该条件为 True 的可能原因:* Attached、* Merged // 该条件为 False 的可能原因:* Pending、* Overridden、* TargetNotFound PolicyConditionAttached PolicyConditionType = "Attached" PolicyReasonValid PolicyConditionReason = "Valid" PolicyReasonInvalid PolicyConditionReason = "Invalid" PolicyReasonAttached PolicyConditionReason = "Attached" PolicyReasonMerged PolicyConditionReason = "Merged" PolicyReasonOverridden PolicyConditionReason = "Overridden" PolicyReasonPending PolicyConditionReason = "Pending" PolicyReasonPartiallyValid PolicyConditionReason = "PartiallyValid" PolicyReasonTargetNotFound PolicyConditionReason = "TargetNotFound" )

两类条件的分工如下:

  • Accepted:策略是否被系统接受。True表示有效(Valid);False表示无效(Invalid)、待处理(Pending)或目标不存在(TargetNotFound)。
  • Attached:策略是否真正挂载到目标资源。True的可能原因是Attached(完全挂载)或Merged(合并后挂载);False的原因则是Pending、Overridden(被更高优先级策略完全覆盖)或TargetNotFound。

注意TargetNotFound的语义:当策略的targetRefs指向不存在的资源时,由于缺失的目标没有 Gateway 祖先可供上报,条件会被上报到策略的合成StatusSummary祖先条目上(见下文"状态写入路径")。

挂载状态的判定:Attached / Merged / Overridden

设计文档给出了Attached条件的三态语义:

  • status: "True", reason: Attached:策略挂载到了全部目标,且未与其他策略合并;
  • status: "True", reason: Merged:策略挂载到了全部目标,但在一个或多个目标上与其他策略发生了合并;
  • status: "False", reason: Overridden:策略在一个或多个目标上被更高优先级策略覆盖。

由于一个策略可以挂载多个目标,且每个目标上的挂载状态可能不同,同一个策略可能同时处于多种状态。Attached条件在聚合时遵循严重程度优先级:Overridden>Merged>Attached,即策略同时存在多个关联状态时,以最严重者为准。

源码实现:PolicyAttachmentState 位掩码

在 pkg/pluginsdk/reporter/types.go 中,挂载状态被建模为一个可按位组合的枚举:

type PolicyAttachmentState int const ( // PolicyAttachmentStatePending 表示策略挂载处于待处理状态 PolicyAttachmentStatePending PolicyAttachmentState = iota // PolicyAttachmentStateAttached 表示整个策略被成功挂载 PolicyAttachmentStateAttached PolicyAttachmentState = 1 << iota // PolicyAttachmentStateMerged 表示策略与其他策略合并后被挂载 PolicyAttachmentStateMerged // PolicyAttachmentStateOverridden 表示策略与更高优先级策略冲突并被完全覆盖 PolicyAttachmentStateOverridden ) // Has 检查现有状态是否包含给定状态 func (a PolicyAttachmentState) Has(b PolicyAttachmentState) bool { return a&b != 0 }

同一文件还定义了各状态对应的用户可见消息常量,这些消息会直接出现在 status 条件中:

PolicyAcceptedMsg = "Policy accepted" PolicyAttachedMsg = "Attached to all targets" PolicyMergedMsg = "Merged with other policies in target(s) and attached" PolicyOverriddenMsg = "Overridden due to conflict with higher priority policy in target(s)" PolicyTargetNotFoundMsg = "Policy is not attached to targets that could not be resolved"

在 pkg/reports/policy.go 的addAttachmentCondition函数中,实现了"最严重状态优先"的聚合逻辑:依次检查Overridden、Merged、Attached,命中即设置对应条件,从而保证多个目标上不同状态的并集被收敛为一个最严重的Attached条件:

switch { case attachmentState.Has(reporter.PolicyAttachmentStateOverridden): // status: False, reason: Overridden meta.SetStatusCondition(&existing, metav1.Condition{ Type: string(shared.PolicyConditionAttached), Status: metav1.ConditionFalse, Reason: string(shared.PolicyReasonOverridden), Message: reporter.PolicyOverriddenMsg, }) case attachmentState.Has(reporter.PolicyAttachmentStateMerged): // status: True, reason: Merged ... case attachmentState.Has(reporter.PolicyAttachmentStateAttached): // status: True, reason: Attached ... }

状态写入路径:从翻译到上报

接受状态与挂载状态的报告

在 pkg/kgateway/translator/irtranslator/policy.go 中,两个核心函数分别负责两类状态:

  • reportPolicyAcceptanceStatus:对每个关联 CR 的策略,若存在Errors则上报Accepted=False, reason=Invalid(消息为策略的错误格式化文本);否则上报Accepted=True, reason=Valid(消息为Policy accepted)。
  • reportPolicyAttachmentStatus:对无错误的策略,根据mergeOrigins判断挂载状态:
    • 若mergeOrigins.IsSet()为 false,说明没有发生合并,是直接挂载,上报PolicyAttachmentStateAttached;
    • 否则根据策略引用在MergeOrigins中的出现次数,映射到Overridden(None)、Merged(Partial)、Attached(All)。

在 pkg/kgateway/translator/irtranslator/gateway.go 的监听器插件执行路径中可以看到两者的串联调用:

reportPolicyAcceptanceStatus(reporter, l.PolicyAncestorRef, pols...) policies, mergeOrigins := mergePolicies(pass, pols) // ...应用插件... out.Metadata = addMergeOriginsToFilterMetadata(gk, mergeOrigins, out.GetMetadata()) reportPolicyAttachmentStatus(reporter, l.PolicyAncestorRef, mergeOrigins, pols...)

即在一次合并处理中:先上报接受状态 → 执行策略合并得到mergeOrigins→ 应用插件产出 Envoy 配置 → 把MergeOrigins写入过滤器元数据 → 再上报挂载状态。

状态汇总:BuildPolicyStatus

pkg/reports/policy.go 的BuildPolicyStatus负责把翻译过程中累积的 typed report 转为最终的gwv1.PolicyStatus:为每个祖先条目合并Accepted默认值(缺失时置为False/Pending)、调用addAttachmentCondition追加Attached条件、保留旧条件以维持lastTransitionTime连续性,并保留不由本报告器拥有的外来条件。

未解析 targetRefs 的补充上报

设计文档的非目标声明"不报告未解析 targetRefs 的状态",而仓库中的实现(pkg/kgateway/proxy_syncer/policy_target_status.go)将这类情况收敛为TargetNotFound条件:正向遍历每个策略的显式targetRefs,通过 krt(Kubernetes 运行时转换)驱动的 informer 集合解析目标;无法解析的引用上报到策略的合成StatusSummary祖先条目上(Accepted=False/TargetNotFound),而不是直接使用缺失引用本身作为祖先——后者会因引用不存在对象而浪费祖先配额。

合成祖先的定义位于 pkg/pluginsdk/reporter/types.go:

func PolicyStatusSummaryAncestorRef() gwv1.ParentReference { return gwv1.ParentReference{ Group: new(gwv1.Group(kgateway.GroupName)), Kind: new(gwv1.Kind(PolicyStatusSummaryAncestorName)), Name: PolicyStatusSummaryAncestorName, } }

合并来源追踪:MergeOrigins

数据结构与操作语义

在 pkg/pluginsdk/ir/merge.go 中,MergeOrigins被定义为"策略字段名 → 贡献该字段的策略引用集合"的映射,并附带三个计数枚举:

// MergeOrigins maps policy field names to policy refs that contribute to it // during policy merging type MergeOrigins map[string]sets.Set[string] // MergeOriginsRefCount 用于追踪策略引用在 MergeOrigins 中的状态 type MergeOriginsRefCount int const ( // MergeOriginsRefCountNone 表示该引用未出现在任何字段的 MergeOrigins 中 MergeOriginsRefCountNone MergeOriginsRefCount = iota // MergeOriginsRefCountPartial 表示该引用出现在部分(非全部)字段中 MergeOriginsRefCountPartial // MergeOriginsRefCountAll 表示该引用出现在全部字段中 MergeOriginsRefCountAll )

MergeOrigins提供的操作与合并策略一一对应:

  • SetOne(field, ref, mergeOrigins):浅合并(shallow merge)场景使用——字段被覆盖后,仅记录新的来源(或当 ref 为 nil 时继承来源的MergeOrigins)。
  • Append(field, ref, mergeOrigins):深合并(deep merge)场景使用——字段值是多个策略拼接/并集的结果,因此把来源引用追加到已有集合中。
  • GetRefCount(ref):统计某策略引用出现在多少个字段中,返回None/Partial/All,这正是reportPolicyAttachmentStatus判定Overridden/Merged/Attached的依据。
  • ToProtoStruct():把MergeOrigins转换为 Envoystructpb.Struct,供写入过滤器元数据使用。

策略引用的唯一 ID 由AttachedPolicyRef.ID()生成(见 pkg/pluginsdk/ir/gw.go),其格式为Group + delimiter + Kind + delimiter + Namespace + delimiter + Name,并通过 intern 共享底层字符串以减少内存占用。

各字段的实际合并调用

以 pkg/kgateway/extensions2/plugins/trafficpolicy/merge.go 为例,TrafficPolicy 的合并函数(mergeExtProc、mergeExtAuth、mergeRustformation、mergeJwt、mergeCORS、mergeFaultInjection等约 25 个字段级合并器)在合并每个字段时同步维护MergeOrigins:

  • 深合并路径(AugmentedDeepMerge/OverridableDeepMerge)调用mergeOrigins.Append("extProc", p2Ref, p2MergeOrigins)等,把参与合并的第二个策略追加为该字段的来源;
  • 浅合并路径通过defaultMerge内部的mergeOrigins.SetOne(...)记录字段被哪个策略设置;
  • 特殊场景(如 buffer 与 HTTP upgrade 互斥冲突解决)还会通过delete(mergeOrigins, "buffer")移除被剔除字段的来源记录。

写入 Envoy 元数据

addMergeOriginsToFilterMetadata(见 pkg/kgateway/translator/irtranslator/policy.go)在合并结果非空时,将MergeOrigins以merge.<group>/<kind>为 key 写入 EnvoyMetadata.FilterMetadata:

const mergeMetadataKeyPrefix = "merge." func addMergeOriginsToFilterMetadata(gk schema.GroupKind, mergeOrigins ir.MergeOrigins, metadata *envoycorev3.Metadata) *envoycorev3.Metadata { if !mergeOrigins.IsSet() { return metadata } pb := mergeOrigins.ToProtoStruct() ... metadata.FilterMetadata[mergeMetadataKeyPrefix+gk.String()] = pb return metadata }

这样,下发的 Envoy 监听器配置中就带上了每个合并字段的来源策略信息,供诊断工具分析"某个字段最终来自哪个策略"。设计文档明确表示:将来工具可以利用该元数据深入洞察策略合并结果。

状态示例解读

以下三个示例均取自设计文档,展示了三种典型场景下Gateway祖先条目的完整状态(controllerName为kgateway.dev/kgateway)。

场景一:策略成功挂载到全部目标

status: ancestors: - ancestorRef: group: gateway.networking.k8s.io kind: Gateway name: test namespace: default conditions: - lastTransitionTime: "2025-07-24T18:06:29Z" message: Policy accepted observedGeneration: 1 reason: Valid status: "True" type: Accepted - lastTransitionTime: "2025-07-24T18:06:29Z" message: Attached to all targets observedGeneration: 1 reason: Attached status: "True" type: Attached controllerName: kgateway.dev/kgateway

Accepted=True/Valid表示策略有效;Attached=True/Attached表示策略未经过合并、直接挂载到了所有目标。

场景二:策略与其他策略合并但部分挂载

status: ancestors: - ancestorRef: group: gateway.networking.k8s.io kind: Gateway name: test namespace: default conditions: - lastTransitionTime: "2025-07-24T18:06:29Z" message: Policy accepted observedGeneration: 1 reason: Valid status: "True" type: Accepted - lastTransitionTime: "2025-07-24T18:06:29Z" message: Merged with other policies in target(s) and attached observedGeneration: 1 reason: Merged status: "True" type: Attached controllerName: kgateway.dev/kgateway

Attached=True/Merged说明策略已生效,但其部分字段与更高优先级策略合并——消息Merged with other policies in target(s) and attached与 pkg/pluginsdk/reporter/types.go 中的PolicyMergedMsg完全一致。

场景三:策略被接受但未挂载

status: ancestors: - ancestorRef: group: gateway.networking.k8s.io kind: Gateway name: test namespace: default conditions: - lastTransitionTime: "2025-07-24T18:06:29Z" message: Policy accepted observedGeneration: 1 reason: Valid status: "True" type: Accepted - lastTransitionTime: "2025-07-24T18:06:29Z" message: Overridden due to conflict with higher priority policy in target(s) observedGeneration: 1 reason: Overridden status: "False" type: Attached controllerName: kgateway.dev/kgateway

这是设计文档最想解决的场景:Accepted=True/Valid但Attached=False/Overridden。策略本身合法,却在目标上被更高优先级策略完全覆盖,用户一眼即可看出"策略被接受了但没有生效",而无需逐个比对策略优先级。

状态如何落到 Kubernetes 对象上

策略状态的最终写入由状态同步层完成。在 pkg/kgateway/proxy_syncer/status.go 中可以看到各来源的报告生成函数(如GenerateBackendPolicyReport为挂载到 Backend 上的策略生成Accepted/Attached条件),而 pkg/reports/policy.go 的MergePolicyReports负责跨贡献折叠报告:当ReportMap中某策略已有报告时,把src的祖先条目合并进去;同一祖先在两边都出现时以src的条目替换。

写入端的核心约束(见 pkg/reports/policy.go 的注释)是:其他控制器拥有的祖先条目被有意保留,祖先数量上限(cap)与"仅替换我方条目"逻辑放在写入路径(statussync.MergePolicyAncestorStatuses)执行,该路径才能权威地读取 live 对象——这保证了多控制器共存时状态不被误删,同时BuildPolicyStatus中的排序保证消费方(如 golden-output 翻译测试)拿到确定性输出。

测试与验证

设计文档的 Test Plan 要求通过单元测试、translator 测试与 e2e 测试验证策略 Status API。仓库中的对应测试覆盖包括:

  • pkg/reports/policy_test.go:验证BuildPolicyStatus生成的Attached条件(如Attached=True/Attached、消息Attached to all targets)。
  • pkg/pluginsdk/ir/merge_test.go:覆盖MergeOrigins的Get、SetOne、Append、IsSet、GetRefCount等全部操作方法,包括浅合并覆盖与深合并追加的来源记录行为。
  • pkg/kgateway/proxy_syncer/policy_target_status_test.go 与 pkg/reports/observed_generation_test.go:验证TargetNotFound上报、observedGeneration与状态纯度的行为。
  • pkg/kgateway/extensions2/plugins/trafficpolicy/merge_test.go:验证具体字段合并时MergeOrigins的记录是否符合预期。

总结

kgateway 通过将gwv1.PolicyStatus中的状态拆分为Accepted与Attached两个自定义 Condition,彻底解决了"策略被接受但未生效"难以诊断的问题:

  • Accepted反映策略本身的合法性与目标可解析性;
  • Attached反映策略是否真正挂载到目标资源,并通过Attached、Merged、Overridden三种原因(按Overridden > Merged > Attached的严重程度聚合)描述合并场景下的最终生效状态;
  • 同时,MergeOrigins作为元数据写入下发的 Envoy 配置,记录了每个合并字段的来源策略,为构建"策略合并结果洞察"类工具提供了数据基础。

这套设计让运维人员可以用kubectl get <policy> -o yaml直接读取状态条件,快速区分"配置写错了"(Accepted=False)与"配置被覆盖了"(Accepted=True但Attached=False/Overridden),显著提升了多策略叠加场景下的可观测性与排障效率。

  • API网关
  • 云原生
  • 微服务

【免费下载链接】kgateway

The Cloud-Native API Gateway and AI Gateway

项目地址:https://gitcode.com/gh_mirrors/kg/kgateway
点击查看免费下载
上一篇:Lobe Theme 布局模式深度解析:双列视图与可调节画布比例
下一篇:5分钟快速上手:BRV框架让Android列表开发效率提升300%

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 1:40:28

开源SMU源测量单元解析:从四象限原理到I-V曲线实测与校准

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:39:40

动态库热加载原理与框架设计:从dlopen到安全热替换

"动态库热加载"这个词&#xff0c;做后台服务和客户端开发的朋友应该都不陌生。简单讲&#xff0c;它就是在程序运行期间&#xff0c;把编译好的动态库&#xff08;Linux下的.so、Windows下的.dll、macOS下的.dylib&#xff09;加载进进程&#xff0c;或者用新版本替…

作者头像 李华
网站建设 2026/10/12 1:38:04

油田污水站腐蚀监测与缓蚀剂智能加注闭环系统实战解析

简介&#xff1a;这份PDF是一篇发表于《腐蚀与防护》期刊的专业技术论文&#xff0c;面向油田腐蚀控制、智能加注系统开发及石油生产安全管理相关的工程师与研究人员。文章围绕胜利油田污水回注系统腐蚀加剧的工程难题&#xff0c;详细介绍了基于电化学阻抗的在线腐蚀监测技术、…

作者头像 李华