有的控制器加副本也不会变快
一句话总结
Kyverno 不是单个程序,而是由 admission、background、reports、cleanup 四个控制器各自作为 Deployment 运行的结构。所以高可用(HA)也要按控制器分别决定;升级不能只提高镜像标签;策略在放进集群之前要先用 CLI 测试,放进去之后则用 kyverno_* 指标来观察。本文把安装方法文档、高可用指南、升级文档、CLI 参考、指标参考汇总在一起加以说明。
为什么需要它
准入 Webhook 默认是 fail closed。安装文档对这一风险讲得很详细——如果 API 服务器连不上 Kyverno,创建会命中策略的资源的请求,就会因为无法评估策略而失败。在一个有强制 Pod 以 non-root 运行的策略的集群里,如果 Kyverno 的 Pod 全部挂掉,就一个新 Pod 都创建不了。所以生产环境应该以 HA 方式安装,并且要把 Kyverno 自己的命名空间从 Webhook 中排除(默认配置会排除 kyverno 和 kube-system)。
随之而来的有三个运维问题。要启动多少个哪种控制器,如何升级到新版本,以及如何查看策略实际上拦住了什么。
工作原理
四个控制器与 Helm 安装
安装文档按下面的方式划分控制器。admission controller 是必需的,它接收 API 服务器的 Webhook 回调,处理 validate、mutate、镜像验证和 PolicyException。background controller 负责 generate 和 mutate-existing 规则,reports controller 负责 PolicyReport,cleanup controller 负责 CleanupPolicy。每个控制器都有各自的 ServiceAccount,权限因此是分开的,默认安装中每个控制器各有 1 个副本。
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --create-namespace \
--set admissionController.replicas=3 \
--set backgroundController.replicas=2 \
--set cleanupController.replicas=2 \
--set reportsController.replicas=2
上面是文档中的 HA 安装示例。文档还附有一个“完整”HA 部署的 values 示例,其中四个控制器都是 replicas: 3。也可以用 YAML 清单安装,方法是对已打标签的发布版本的 install.yaml 执行 kubectl create -f,文档明确指出这种方法不支持直接升级。如果需要 PSS(Pod Security Standards)策略集,就另外安装单独的 Chart kyverno/kyverno-policies。
每个控制器的副本所做的事情不同
这是 HA 指南最强调的事实。admission controller 处理 Webhook 请求时不使用领导者选举(leader election),所有副本分担处理请求——副本既用于可用性,也用于吞吐量。只有证书和 Webhook 管理由一个领导者负责。被认可为 HA 的最小副本数是 3。而 reports controller 和 background controller 是有状态服务,使用领导者选举,不管有多少个副本,都只有一个在工作。所以这两者的副本只对可用性有帮助,要提高吞吐量,要增加的不是副本数,而是单个 Pod 的资源(垂直扩展)。安装文档中“副本多并不意味着所有控制器的性能都会提高”这句话就是这个意思。
Webhook 本身的配置也有几个值需要了解。failurePolicy 默认是 Fail,可以按策略修改,也可以用 --forceFailurePolicyIgnore 整体修改。webhookTimeout 默认是 10 秒(1–30 秒)。默认的 resourceFilters 会排除 Event、Node,以及 kube-system、kube-public、kube-node-lease、kyverno 命名空间中的资源。
CRD 的种类
CRD 文档介绍说,用 kubectl explain 可以查看所有类型。升级文档的 v1.19 一节整理了这些种类。
| 种类 | API 组/版本 | 作用 |
|---|---|---|
Policy / ClusterPolicy |
kyverno.io/v1 |
传统的 validate、mutate、generate、verifyImages 策略 |
ValidatingPolicy 等 CEL 策略 |
policies.kyverno.io/v1 |
ValidatingPolicy、MutatingPolicy、GeneratingPolicy、DeletingPolicy、ImageValidatingPolicy |
CleanupPolicy / ClusterCleanupPolicy |
kyverno.io/v2 |
基于调度的清理 |
PolicyException |
kyverno.io/v2(旧版)或 policies.kyverno.io |
例外 |
GlobalContextEntry |
kyverno.io/v2(v2alpha1 已弃用) |
缓存的外部数据 |
PolicyReport / ClusterPolicyReport |
wgpolicyk8s.io/v1alpha2 |
最终报告 |
EphemeralReport / ClusterEphemeralReport |
reports.kyverno.io/v1 |
报告的中间产物 |
UpdateRequest |
内部类型 | generate、mutate-existing 的中间产物 |
从 v1.19 起,CRD 通过名为 kyverno-api 的 Chart 依赖来管理,crds.install 值用来开关它。同一份文档预告,在 v1.19 中 ClusterPolicy、Policy、CleanupPolicy 和旧版 PolicyException 被标记为 deprecated,并在 v1.20 中移除。
升级——为什么不能只提高标签
升级文档的第一句话就是原则:新版本中包括 CRD 在内,需要变更的配套资源很多,所以只提高镜像标签是无法升级的。要从 1.10 之前的版本用 Helm 升级到 1.10 及以上,无法直接升级,必须按照 Chart v2→v3 迁移指南操作。如果要跨过次版本,就必须阅读这期间所有次版本的发布说明。
v1.13 一节是个很好的例子。通配符 view 权限被去掉,查看自定义资源的 mutate、generate 策略和报告因此受到影响;PolicyException 默认在所有命名空间中被允许,这被当作安全问题(CVE-2024-48921)改掉了,所以必须明确指定 features.policyExceptions.namespace 的值;旧的 CRD API 版本被移除,由 Helm hook 自动处理迁移。v1.19 中为了迁移存储版本(storage version),新增了 kyverno migrate --resource policyexceptions.kyverno.io 这样的 CLI 命令。要点只有一个——升级不是替换代码,而是 CRD、权限、默认值一起变化的事件,所以发布说明就是操作手册。
CLI——不需要集群就能测试
Kyverno CLI 是与控制器分开的可执行文件,正如参考中对 --kubeconfig 的说明写着“只有在集群之外运行时才需要”,它不需要集群。在 CI 中,按策略测试指南的做法,用 GitHub Action kyverno/action-install-cli 来安装。核心命令有三个。
kyverno apply policies/ -r resources/ 把策略应用到资源清单并显示结果。当知道策略但不知道资源时——比如检查开发团队 PR 中提交的清单时——就用它。
kyverno test <디렉터리 또는 git 저장소>(占位符为目录或 git 仓库)则相反,它把预先写好策略、资源和预期结果的测试清单(kyverno-test.yaml,可用 -f 更改文件名)与实际结果进行比较。预期结果是 pass、fail、skip,可以用 kyverno create test -p policy.yaml -r resource.yaml --pass 정책이름,규칙이름,리소스이름,네임스페이스,종류(占位符依次为策略名称、规则名称、资源名称、命名空间、种类)生成测试文件。用 --git-branch 测试远程仓库的分支,用 --test-case-selector "policy=..., rule=..., resource=..." 只选取一部分,用 -o junit 这样的选项更改输出格式。
kyverno jp 是加入了 Kyverno 自定义函数的 JMESPath 命令行。用 kyverno jp query -i object.yaml '식'(占位符为表达式)对文件评估表达式,用 kyverno jp function 查看函数列表,像 kyverno jp function truncate 这样查看特定函数的说明,用 kyverno jp parse 查看表达式的语法树。如前面的模块所见,用 kubectl get --raw ... | kyverno jp query "items | length(@)" 预先确认 apiCall 的结果是标准做法。
指标——该看什么
按照监控指南,Helm 安装会为每个控制器创建 metricsService,并在 8000 端口的 /metrics 上提供指标。默认的 Service 类型是 ClusterIP,所以只有集群内的 Prometheus 才能抓取,要从外部抓取就改成 NodePort 或 LoadBalancer。暴露范围通过 kyverno-metrics ConfigMap 来调节——namespaces.include/exclude(exclude 优先)、直方图的 bucketBoundaries,以及在 metricsExposure 中按指标关闭(enabled: false)、去掉标签维度(disabledLabelDimensions)或更改桶(bucket)。文档写明,缩小命名空间范围后,内存使用会明显减少。
指标参考整理的主要指标如下。
| 指标 | 类型 | 看什么 |
|---|---|---|
kyverno_policy_rule_info_total |
Gauge(规则处于活动状态时为 1) | 当前集群中有哪些策略和规则。policy_type(cluster/namespaced)、policy_validation_mode(enforce/audit)、rule_type、status_ready |
kyverno_policy_results |
Counter | 规则执行结果。rule_result(PASS/FAIL)、rule_execution_cause(admission_request/background_scan)、resource_kind |
kyverno_policy_execution_duration_seconds |
Histogram | 单条规则的执行延迟 |
kyverno_admission_review_duration_seconds |
Histogram | 单个请求的整体准入延迟(所有策略合计) |
kyverno_admission_requests_total |
Counter | 准入请求数和 request_allowed |
rate(kyverno_policy_results{resource_kind="Pod", rule_execution_cause="admission_request"}[1m])*60
上面是参考中收录的查询,表示 Pod 请求引发的每分钟规则执行次数。实际工作中先看的有两样——准入延迟直方图是否正在接近 webhookTimeout,以及 rule_result="FAIL" 在哪些策略、命名空间中增加。前者是 fail closed 的 Webhook 让集群停摆之前的预警,后者是策略实际拦住的内容的清单。Grafana 仪表板 JSON 包含在 Chart 中,可以用 grafana.enabled 值部署。
在现场相遇的样子
某个组织说 reports controller 慢,把副本增加到 5 个,却什么都没有变快。因为只有一个领导者在工作。答案不是副本,而是领导者 Pod 的 CPU 和内存,同时通过 kyverno-metrics 的命名空间排除来减轻指标负担。
升级中常见的事故是用 Helm 一次跨过两个次版本,却没有读发布说明。1.13 的权限变更导致针对自定义资源的 generate 策略悄悄停了,是后来通过 kyverno_policy_rule_info_total 的 status_ready="false" 才发现的。
下一项测验要确认什么
测验会考查必需的控制器是什么、副本对吞吐量有帮助的控制器和没有帮助的控制器、升级时为什么不能只提高标签、kyverno test 与 kyverno apply 的区别、kyverno jp 的用途,以及 kyverno_policy_results 的标签。