Skip to content

CNClaim 重复绑定同一 Pod + Claim 绑定已不存在的 Pod (prod freetier-01) #589

Description

@xzxiong

关联

现象

2026-05-14 18:00 ~ 2026-05-15 10:00 时间窗口内,prod freetier-01 集群出现两类异常:

1. 两个 CNClaim 绑定同一个 Pod

019503e7-a833-7d33-b069-7f1c1fdc4d0e-normal-6rh6f   cn-8fqpx   Bound   13h
019584b6-c2e4-7e8b-a2fb-491bbf9424a7-normal-rjj66   cn-8fqpx   Bound   36h

#581 相同模式:两个 claim 同时标记 Bound 到 cn-8fqpx

2. CNClaim 绑定到已不存在的 Pod

Issue 时间点的 CN Pod 列表中,以下 Pod 后续全部被删除(NotFound),但 claim 状态仍为 Bound:

  • cn-8fqpx — 被 6rh6frjj66 同时绑定
  • cn-5fl29 — issue 创建时 phase=Idle,后来不存在
  • cn-7lrb7 — 已不存在
  • cn-bxqrq — 已不存在(sys claim 绑定的 pod)

Operator 日志时间线

Phase 1: 约 21:46 — 密集 reconcile + scale 循环

operator 进入高频 reconcile loop(每秒 2-4 次),持续 scale cnset replicas=13。此时 cn-8fqpx 已出现 conflict error(两个 controller 竞争 patch Pod annotation):

21:48:34 cnstore/controller.go:454 "error sync stats" pod=cn-8fqpx error="object has been modified"

此 conflict error 从 21:48 持续到 02:09,说明 cn-8fqpx 一直被两个 claim 并发操作。

Phase 2: 22:04 — CN does not exist 错误

22:04:00 cnclaim/controller.go:280 "error keep bound CN working" claim=t5wzs error="CN does not exist"
22:04:00 cnclaim/controller.go:280 "error keep bound CN working" claim=ob-sys-klfb4 error="CN does not exist"
22:04:00 cnclaim/controller.go:280 "error keep bound CN working" claim=shared-pkdj4 error="CN does not exist"

多个 claim 报告 CN store 不存在 → 触发迁移。

Phase 3: 22:14 — 迁移卡住(每秒重试)

claim h2mvc 迁移 cn-glhd9 → cn-qnl95 卡在 migrate.go:41 持续重试(22:14:47 ~ 22:15:28),每秒一次 log。22:15:02 才开始 draining source pod。

Phase 4: 02:19 — Pod 被删除,claim 被 finalize

02:19:52 cnstore/controller.go:101 "call HAKeeper to remove CN store" pod=cn-8fqpx
02:19:52 reconciler.go:370 "resource finalizing complete, remove finalizer" pod=cn-8fqpx
02:19:52 cnclaim/controller.go:280 "error keep bound CN working" claim=6rh6f error="CN does not exist"
02:20:24 cnclaim/controller.go:341 "finalize CNClaim" claim=6rh6f
02:22:00 cnclaim/controller.go:341 "finalize CNClaim" claim=rjj66

Pod cn-8fqpx 被 HAKeeper 移除后,绑定它的两个 claim 都被 finalize 删除。

根因分析

Bug 1: 重复绑定(与 #581 相同)

selectCN() (controller.go:137-176) 选择 Idle Pod 时:

  • 仅通过 label selector 筛选 phase=Idle 的 Pod
  • 不检查是否有其他 CNClaim 的 spec.podName 已指向该 Pod
  • 依赖 ctx.Update(pod) 的乐观并发控制(OCC)防止双绑,但如果另一个 claim 通过 migration 绑定(不走 selectCN),则 OCC 无法防护

Bug 2: Claim 绑定不存在的 Pod

Sync() (controller.go:249-260) 在 Pod NotFound 时:

  • 如果 BoundTime 存在且距今 < waitCacheTimeout,则 requeue 等待
  • 超时后设置 phase=Lost

但实际观察:Pod 早已删除,claim 仍显示 Bound 而非 Lost。可能原因:

  1. waitCacheTimeout 期间 CNClaimSet controller 重建了 claim,新 claim 继承了已删除 Pod 的 spec.podName
  2. 或者 claim 在 Lost 后又被某个 reconcile 路径刷回 Bound

Bug 3: 迁移 reconcile 风暴

迁移过程中(migrate.go:36-75),每次 reconcile 失败都打印完整的 migrate log 并 resync(10s interval),但实际日志显示每秒都在重试,说明有其他事件不断触发 reconcile,导致 operator CPU 负载激增。

当前状态

手工介入修复。通过人工删除异常 claim、修正 Pod label 等操作恢复绑定关系,operator 未能自愈。

修复建议

  1. selectCN 增加双重校验:选中 Idle Pod 后,检查集群中是否有其他 CNClaim 的 spec.podName 指向该 Pod
  2. Finalize 时检查其他 claim 引用Finalize() 回收 Pod 前,确认无其他 CNClaim 引用同一 Pod
  3. 迁移目标排除已占用 Pod:migrate 选择 target 时排除已被其他 claim 的 spec.podName 占用的 Pod
  4. Webhook 拦截重复绑定:admission webhook 拒绝将 Pod 的 claimed-by label 设为与已有 claim 不同的值

复现条件

  • 多个 CNClaim 同时需要 Pod(如 scale-down 后重建)
  • 迁移过程中源 Pod 被释放变为 Idle,被新 claim 抢占
  • operator 高频 reconcile(每秒多次)加剧竞态窗口

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions