- 云原生
- 运维
- 可观测性
【免费下载链接】popeye
👀 A Kubernetes cluster resource sanitizer
Popeye 是一款扫描 Kubernetes 生产集群、报告已部署资源配置问题的只读型集群消毒器(Cluster Sanitizer)。v0.3.10 版本的核心变化是:为 PodDisruptionBudget(PDB)新增了专门的 sanitizer 检查,在集群中存在 PDB 资源时,扫描器会报告其使用情况,并提示可能的错误配置。本文以该版本变更日志为主线,结合当前仓库源码,完整讲解 PDB 检查的判定逻辑、错误码体系、真实代码链路、测试验证方式,以及如何在 Spinach 配置中精确排除 PDB 检查,帮助你读懂并运用 Popeye 的 PDB 巡检能力。
一、版本背景与发布说明
v0.3.10 发布日志(对应仓库change_logs/release_v0.3.10.md)记录了该版本的两类信息:
- 新增功能:为 PodDisruptionBudget 增加检查逻辑。原文表述为 "Added check for pbs",即扫描器在集群中存在 PodDisruptionBudget 资源时,会报告其使用情况(usage),并提示可能存在的错误配置(possible misconfiguration)。
- 问题修复:修复了 Issue #34 与 Issue #21 两个缺陷。
说明:发布日志原文中 "PodDisruptionBugdet" 为拼写笔误,实际指 Kubernetes 的 PodDisruptionBudget 资源,下文统一使用 PDB。
当前仓库源码在 internal/glossary.go 中已将PDB定义为"poddisruptionbudgets",并在 internal/scrub/cache.go 中将NewPodDisruptionBudget注册为 PDB 资源的 scrubbing 入口,说明该检查在后续版本中持续保留并演进。
二、PDB 检查的整体工作流
Popeye 对每个资源类型的巡检遵循"预加载(Preload)→ 清扫(Scrub)→ 静态检查(Lint)"的三段式流水线。PDB 检查在源码中同样遵循该链路:
- 预加载:
PodDisruptionBudget清扫器在其Preloads()中声明两类依赖资源——poddisruptionbudgets与pods(见 internal/scrub/pdb.go),即检查 PDB 时必须先把集群中的 Pod 数据也加载进内存数据库,才能做选择器匹配。 - 清扫:
NewPodDisruptionBudget构造清扫器(见 internal/scrub/pdb.go),Lint方法先加载全部资源,再委托给 linter(见 internal/scrub/pdb.go)。 - 静态检查:
internal/lint.PodDisruptionBudget逐条遍历内存数据库中的 PDB 对象,执行checkInUse使用性检查(见下文)。
从整体结构看,v0.3.10 引入的 PDB 检查属于"资源使用性(InUse)"类巡检:它的核心问题是"这个 PDB 真的在保护 Pod 吗?"——即 PDB 的spec.selector是否真的匹配到了集群中实际存在的 Pod。
三、核心检查逻辑:PDB 是否真正"在用"
PDB 检查的主入口位于 internal/lint/pdb.go:
// Lint cleanse the resource. func (p *PodDisruptionBudget) Lint(ctx context.Context) error { txn, it := p.db.MustITFor(internal.Glossary[internal.PDB]) defer txn.Abort() for o := it.Next(); o != nil; o = it.Next() { pdb := o.(*polv1.PodDisruptionBudget) fqn := client.FQN(pdb.Namespace, pdb.Name) p.InitOutcome(fqn) ctx = internal.WithSpec(ctx, SpecFor(fqn, pdb)) p.checkInUse(ctx, pdb) } return nil } func (p *PodDisruptionBudget) checkInUse(ctx context.Context, pdb *polv1.PodDisruptionBudget) { pp, err := p.db.FindPodsBySel(pdb.Namespace, pdb.Spec.Selector) if err != nil || len(pp) == 0 { p.AddCode(ctx, 900, dumpSel(pdb.Spec.Selector)) return } }判定逻辑非常清晰:
- 遍历内存数据库中所有
policy/v1版本的 PDB 对象(源码 import 了k8s.io/api/policy/v1); - 对每个 PDB,调用
db.FindPodsBySel在同一命名空间内按spec.selector查找匹配的 Pod; - 如果
FindPodsBySel返回错误(例如选择器为空、无 Pod 数据),或返回的 Pod 数量为 0,则上报错误码900,并附带dumpSel格式化后的选择器内容。
FindPodsBySel的底层实现在 internal/db/db.go:它会先校验选择器非空,再遍历数据库中的全部 Pod,过滤出命名空间一致且标签匹配选择器的对象。标签匹配由MatchSelector(见 internal/db/db.go)完成,它同时支持matchLabels(等值匹配,见MatchLabels)和matchExpressions(In、NotIn、Exists、DoesNotExist等集合操作,见matchExp/matchSel)两种选择器形式。
实战含义:如果集群中存在一个 PDB,但其
spec.selector匹配不到任何运行中的 Pod(例如标签写错、Pod 已被清理、选择器正则过宽或过窄),Popeye 会将其标记为"未使用",并给出告警。这有助于发现"僵尸 PDB"——看似配置了中断预算、实则没有保护任何工作负载的失效资源。
选择器展示辅助函数
dumpSel用于把metav1.LabelSelector转为人类可读字符串,供错误消息展示,实现在 internal/lint/np.go(该函数被 PDB 与 NetworkPolicy 检查共用):
- 对
matchLabels,按key=value形式拼接(如app=p2); - 对
matchExpressions,按key-operator-values形式拼接(如app-In-test1,test2); - 两类条件同时存在时以
|分隔。
因此 900 号错误消息实际形如:
[POP-900] No pods match pdb selector: app=p2四、PDB 相关的错误码体系
4.1 PDB 自身的错误码(900 / 901)
在 internal/issues/assets/codes.yaml 中定义了两个 PDB 错误码:
| 错误码 | 消息模板 | severity(严重度) | 含义 |
|---|---|---|---|
| 900 | No pods match pdb selector: %s | 2 | PDB 未被使用,选择器匹配不到任何 Pod |
| 901 | MinAvailable (%d) is greater than the number of pods(%d) currently running | 2 | minAvailable配置值大于当前运行的 Pod 数,保护能力配置错误 |
其中:
- severity 2 对应 warn 级别。Popeye 在 internal/rules/level.go 中定义了
Ok → Info → Warn → Error四级严重度,900/901 属于 Warn 警告,表示存在配置隐患而非立即致命的错误。 - 代码 901 目前只在错误码字典中定义(internal/issues/assets/codes.yaml),对应变更日志中"报告可能的错误配置(possible misconfiguration)"的承诺;v0.3.10 实际落地的是 900 号"未使用"检查(
checkInUse)。
4.2 Pod 视角的 PDB 关联检查(200/209)
PDB 检查不只存在于 PDB 自身的 linter 中,Pod linter 也会反向交叉检查 PDB 覆盖情况,二者共同构成完整的 PDB 保护面巡检:
- 代码 206:
No PodDisruptionBudget defined(severity 1,info 级别)——Pod 没有任何 PDB 保护。逻辑见 internal/lint/pod.go:Pod 检查时调用ForLabels在数据库中查找选择器能匹配该 Pod 标签的 PDB,找不到就报 206。 - 代码 209:
Pod is managed by multiple PodDisruptionBudgets (%s)(severity 2)——同一个 Pod 被多个 PDB 同时匹配,属于配置冲突。逻辑见checkForMultiplePdbMatches(internal/lint/pod.go):遍历所有 PDB,用metav1.LabelSelectorAsSelector将选择器转换为标准 selector 并逐一Matches该 Pod 标签,命中超过 1 个 PDB 时上报,并列出按字典序排序后的 PDB 名称列表。
因此,v0.3.10 引入的 PDB 巡检是一套"双视角"体系:
- PDB 视角(900):PDB 的选择器是否匹配到 Pod(防僵尸 PDB);
- Pod 视角(206 / 209):Pod 是否被 PDB 保护、是否被多个 PDB 重复保护(防保护缺失 / 保护冲突)。
五、真实测试用例:PDB 检查如何被验证
仓库在 internal/lint/pdb_test.go 中提供了 PDB 检查的完整测试,使用的测试数据位于 internal/lint/testdata/pol/pdb/1.yaml,其中定义了 5 个 PDB:
- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: pdb1 namespace: default spec: minAvailable: 2 selector: matchLabels: app: p1 - apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: pdb2 namespace: default spec: minAvailable: 1 selector: matchLabels: app: p2测试同时加载core/pod/1.yaml中的 Pod 数据,随后断言:
ii := pdb.Outcome()["default/pdb1"] assert.Equal(t, 0, len(ii)) // pdb1 的 selector 命中 Pod,无问题 ii = pdb.Outcome()["default/pdb2"] assert.Equal(t, 1, len(ii)) assert.Equal(t, `[POP-900] No pods match pdb selector: app=p2`, ii[0].Message) assert.Equal(t, rules.WarnLevel, ii[0].Level) // 900 属于 Warn 级别可见:只有选择器真正命中 Pod 的 PDB(如pdb1)才会判定为正常(Outcome 为空),其余选择器失配的 PDB(pdb2、pdb3、pdb4、pdb4-1)全部上报 900 号 Warn 告警。这与变更日志"报告 PDB 使用情况"的描述完全一致,也印证了 v0.3.10 时代码的判定口径至今仍被测试用例锁定。
六、如何运行 PDB 检查与查看报告
PDB 检查随 Popeye 默认扫描自动启用,无需额外开关。运行方式与常规扫描一致(参见 README.md 的命令行说明):
# 使用当前 kubeconfig 环境扫描整个集群 popeye # 仅扫描指定命名空间 popeye -n fred # 扫描全部命名空间 popeye -A # 指定 kubeconfig 上下文 popeye --context olive # 只跑指定 linter(PDB 的资源名为 poddisruptionbudgets,别名 pdb) popeye -s poddisruptionbudgetsPDB 的 linter 资源名与别名在 README.md 的 Linters 清单中有明确记录:资源名poddisruptionbudgets,别名pdb。Popeye 的 linter 以 Kubernetes 资源复数形式命名,例如 PodDisruptionBudget linter 扫描的是policy/v1/poddisruptionbudgets(见 README.md)。
扫描结果会按资源 FQN(namespace/name形式,如default/pdb1)聚合展示,每条 Issue 包含错误码、消息与严重度。你可以选择控制台(Console)、JSON、HTML 等多种报告输出格式(仓库assets/screens/目录下分别提供了对应截图),也可以将扫描结果保存后分析 PDB 相关的 900/206/209 告警。
七、用 Spinach 配置精确控制 PDB 检查
Popeye 支持通过 Spinach YAML 配置文件(-f选项指定)精细控制各 linter 的行为,包括排除特定的资源或错误码。PDB 相关排除规则示例如下(配置文件语法参考 README.md):
# spinach.yaml popeye: excludes: # 全局排除:任何资源、任何 linter 都跳过 kube-system 等命名空间 global: fqns: [rx:^kube-] # 全局排除 PDB 相关的 900、206、209 错误码 codes: ["900", "206", "209"] # 按 linter 粒度配置 linters: # 仅对 poddisruptionbudgets linter 排除 900 号告警 poddisruptionbudgets: codes: ["900"]配置要点:
- FQN 排除:资源全限定名采用
namespace/name形式,例如排除default/pdb1可写fqns: ["default/pdb1"];支持rx:前缀的正则匹配。 - 错误码排除:可写死错误码(
"900")或使用正则("rx:^90"匹配 900、901 等)。global.codes对所有 linter 生效,linters.poddisruptionbudgets.codes仅对该 linter 生效。 - 注意排除规则的"副作用":正则过宽可能连带排除其他资源,建议仅在明确需要时使用,并定期以"全开"方式运行 Popeye,避免因排除规则掩盖新出现的集群问题(README.md 有同样的提醒)。
八、从 v0.3.10 看 PDB 检查的演进脉络
- v0.3.10 首次引入 PDB 检查,落地"PDB 是否被使用"(代码 900)的判定;
- 后续版本的错误码字典进一步补充了
901(minAvailable大于当前运行 Pod 数)与 Pod 视角的206、209,形成完整的双视角保护面检查; - 当前源码中 PDB 检查已稳定运行在
policy/v1API 版本之上,且与 Pod、NetworkPolicy(共用dumpSel)等检查共享内存数据库基础设施,整体架构一致。
如果你正在治理集群中的 PDB 资源,建议把 900(僵尸 PDB)、206(无保护 Pod)、209(多重保护冲突)三类告警纳入例行巡检,结合 docs/codes.md 的完整错误码表,逐项收敛集群的中断预算配置风险。
相关资源
- v0.3.10 发布日志
- PDB linter 实现
- PDB 清扫器实现
- PDB 测试用例
- PDB 测试数据
- 错误码字典(900/901)
- 错误码总表(PDB 章节)
- Linters 清单与别名(pdb)
- Spinach 配置说明
- 云原生
- 运维
- 可观测性
【免费下载链接】popeye
👀 A Kubernetes cluster resource sanitizer
相关推荐
Kubernetes Pod 中断与 PDB(PodDisruptionBudget)高可用保障实战指南
Kubernetes Pod 中断与 PDB(PodDisruptionBudget)高可用保障实战指南 本文是 kubernetes handbook 中关于
教程云原生容器编排AWS Lambda Base Images安全最佳实践:漏洞修复与合规检查实用技巧
AWS Lambda Base Images安全最佳实践:漏洞修复与合规检查实用技巧 想要确保你的AWS Lambda函数容器镜像安全无忧吗?🚀 作为AWS
如何高效使用BiliTools哔哩哔哩工具箱:完整下载工具配置指南
如何高效使用BiliTools哔哩哔哩工具箱:完整下载工具配置指南 BiliTools哔哩哔哩工具箱是一款功能强大的开源B站视频下载工具,为B站用户提供了一站式
桌面应用音视频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考