Kyverno 策略冲突:为何 “Last-Writer-Wins” 是生产缺陷

发布: (2026年2月9日 GMT+8 16:14)
6 分钟阅读
原文: Dev.to

Source: Dev.to

(请提供需要翻译的正文内容,我将为您翻译成简体中文并保留原始的格式、Markdown 语法以及技术术语。)

▶️ 介绍

Kyverno 已成为 Kubernetes 的首选策略引擎,因为它使用起来很简单:

  • 策略使用 YAML 编写
  • 在准入阶段运行
  • 不需要自定义控制器

大多数 Kyverno 事件并非 Kyverno 本身的 bug。
它们是由于对策略执行方式的错误假设导致的。

🤔 几乎所有人都会的假设

大多数团队隐含地假设以下至少一项:

  • 策略会按照它们被应用的顺序执行。
  • 集群范围的策略在命名空间范围的策略之前评估。
  • Kyverno 能确定性地解决冲突。

⚠️ 这些假设都不成立。

Kyverno 提供:

  • 没有策略优先级。
  • 跨策略没有保证的执行顺序。
  • 对变更之间的冲突没有检测。

🧠 Kyverno 实际处理 Admission 请求的方式

对于 单个 admission 请求,Kyverno 会在三个不同的阶段评估策略。这些阶段的顺序是 有保证 的。

阶段会发生什么保证
1️⃣ Mutate执行所有匹配的 mutate 规则。
2️⃣ Validate评估所有匹配的 validate 规则。
3️⃣ Generate处理所有匹配的 generate 规则。

被保证的内容

  • 不同 mutate 策略之间的顺序 – Kyverno 定义哪个 mutate 策略先运行。
  • 属于不同策略的 mutate 规则之间的顺序 – 相对执行顺序未定义。

注意:

  • 单个策略 内,规则会 自上而下 依次处理。
  • 多个策略 之间,顺序 明确未定义

Source:

📌 具体的失败模式

🧪 示例 1 – Mutate 与 Mutate:问题悄然出现

⚠️ 这些示例基于 Kyverno v1.17 编写,策略在同一时间创建。

两个 MutatingPolicy 对象匹配同一个 Pod。

策略 A

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
  name: enforce-non-root
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  mutations:
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: >
          Object{
            spec: Object.spec{
              securityContext: Object.spec.securityContext{
                runAsNonRoot: true
              }
            }
          }

策略 B

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
  name: force-run-as-user-0
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  mutations:
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: >
          Object{
            spec: Object.spec{
              securityContext: Object.spec.securityContext{
                runAsUser: 0
              }
            }
          }

Kyverno 的观察结果

观察点细节
匹配两个策略都匹配同一个 Pod。
操作两者都执行变更。
结果Kyverno 会同时应用这两个补丁,但跨策略的执行顺序 没有 保证。

🧪 示例 2 – Mutate 与 Validate:自我导致的 Admission 失败

场景

一个策略对资源进行变更以强制默认值;另一个策略验证同一字段,假设它处于不同的期望状态。两个策略单独来看都是正确的。

MutatingPolicy – 添加默认标签

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
  name: add-default-app-label
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  mutations:
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: >
          Object{
            metadata: Object.metadata{
              labels: Object.metadata.labels{
                app: "default"
              }
            }
          }

ValidatingPolicy – 要求特定标签值

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: require-app-frontend
spec:
  validationActions:
    - Deny
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  validations:
    - message: "Label app=frontend is required"
      expression: "object.metadata.?labels.app.orValue('') == 'frontend'"

实际发生的情况

  1. 变更 先执行,将 app=default 写入。
  2. 验证 随后执行。
  3. 验证失败,因为缺少必需的标签 app=frontend

Admission 被拒绝 – 即使原始的 Pod 本可以满足验证规则。

📉 为什么规模扩大时会更糟

当你只有一两个策略时,交互关系容易推理。随着策略数量的增长,会出现多个问题:

大规模时的常见问题

  • 多个团队修改相同字段
  • 校验规则编码了不同的假设
  • 作用域静默重叠

典型故障

  • 它们 并不总是立即发生
  • 它们往往 仅在特定工作负载下出现
  • 它们 难以追溯到策略交互,而不是策略本身的逻辑。

为什么会发生

Kyverno 独立评估每个策略。它 不会 推断意图或解决策略之间的冲突,这导致了上述问题。

💡 关键洞察

Kyverno 独立评估策略;它 不会 在策略之间推断或调和意图。当意图分散在多个策略中时,Kyverno 将强制执行 所有 匹配的规则,即使它们的组合效果相互矛盾。

第二部分介绍防止此类冲突的设计模式。

0 浏览
Back to Blog

相关文章

阅读更多 »