关联 Issue
背景
2026-05-17 IDC 环境多节点同时重启导致 LogSet 3 副本全部短暂不可用(~1 分钟)。matrixone-operator 重启后观察到 majority failure,等待 storeFailureTimeout(10m)后触发 Repair 逻辑。但由于节点已恢复而 Pod 未能重新 schedule,operator 最终执行了 PVC 强制重建,导致 WAL 全部丢失。后续 DN 从 S3 恢复时部分 block 元数据缺失,CN 触发 panic 形成 crash loop。
当前 Operator 处理逻辑分析
Repair 路径 (pkg/controllers/logset/controller.go:217)
func (r *WithResources) Repair(ctx *recon.Context[*v1alpha1.LogSet]) error {
minorityLimit := (*ctx.Obj.Spec.InitialConfig.LogShardReplicas) / 2
// 安全阀 1: majority 故障时等待人工介入
if len(ctx.Obj.Status.FailedStores) > minorityLimit {
ctx.Log.Info("majority failure might happen, wait for human intervention")
return nil
}
// 安全阀 2: 已 failover 数量达到 minority 上限
if len(r.sts.Spec.ReserveOrdinals) >= minorityLimit {
ctx.Log.Info("failover limit has reached, only minority failover can be safely automated")
return nil
}
// 执行 failover: 通过 ReserveOrdinals 跳过故障 ordinal → kruise sts 创建新 Pod + 新 PVC
candidate := toRepair[0]
ordinal, _ := util.PodOrdinal(candidate.PodName)
r.sts.Spec.ReserveOrdinals = util.Upsert(r.sts.Spec.ReserveOrdinals, ordinal)
ctx.Update(r.sts)
}
PVC 生命周期
// pkg/controllers/logset/sts.go:142
func syncStatefulSetSpec(ls *v1alpha1.LogSet, sts *kruisev1.StatefulSet) {
case v1alpha1.PVCRetentionPolicyDelete:
sts.Spec.PersistentVolumeClaimRetentionPolicy = &kruisev1.StatefulSetPersistentVolumeClaimRetentionPolicy{
WhenDeleted: kruisev1.DeletePersistentVolumeClaimRetentionPolicyType,
WhenScaled: kruisev1.DeletePersistentVolumeClaimRetentionPolicyType, // scale-in 即删 PVC
}
}
问题链条
- 多节点同时重启 → 3/3 log pod 不可用 →
len(FailedStores)=3 > minorityLimit(1) → 进入 "wait for human intervention"
- 节点 ~1min 后恢复 Ready,但 Pod 未能自动恢复(或 operator 自身重启导致状态不准确)
- Operator 再次 reconcile 时状态更新,FailedStores 降为可处理范围后逐个 failover
- ReserveOrdinals 机制跳过故障 ordinal → kruise 创建新 ordinal Pod → PVCRetentionPolicy=Delete + WhenScaled=Delete → 旧 PVC 被删除
- 新 PVC 为空盘 → WAL 丢失 → 后续 DN/CN 从 S3 恢复不完整
S3 Bucket Reclaim Job(关联问题)
当 s3Reclaim feature gate 启用且 s3RetentionPolicy=Delete 时,集群删除还会触发一个 aws-cli Job 清理 S3 数据(pkg/controllers/bucketclaim/controller.go)。在 PVC 重建场景下,如果集群被判定不可恢复而整体重建,S3 数据可能也被一并清理,彻底断绝恢复可能。
问题总结
| 问题 |
根因 |
影响 |
| 节点短暂中断后 PVC 被删除 |
Repair 逻辑未验证旧 PVC/PV 是否仍可用 |
WAL 丢失,元数据不一致 |
| majority 故障无超时升级 |
"wait for human intervention" 是无限等待,无 alert/event 通知 |
人工可能不知道需要介入 |
| storeFailureTimeout 默认 10min 太短 |
节点重启场景下 Pod 恢复可能需要更长时间(拉镜像、PVC attach) |
过早触发 failover |
| failover 后 PVC 被立即删除 |
WhenScaled: Delete 配合 ReserveOrdinals 跳过 ordinal 等价于 scale-in |
无法回退 |
优化建议
P0: Repair 前增加 PVC/Node 可用性检查
func (r *WithResources) Repair(ctx *recon.Context[*v1alpha1.LogSet]) error {
// ... existing checks ...
candidate := toRepair[0]
pod := getPod(candidate.PodName)
// NEW: 检查 Pod 所在 Node 是否已恢复 Ready
if node := getNode(pod.Spec.NodeName); node != nil && isNodeReady(node) {
ctx.Log.Info("node recovered but pod still not ready, extending wait",
"pod", candidate.PodName, "node", pod.Spec.NodeName)
return recon.ErrReSync("wait for pod recovery on recovered node", reSyncAfter)
}
// NEW: 检查旧 PVC 是否仍然 Bound 且可用
pvc := getPVC(candidate.PodName)
if pvc != nil && pvc.Status.Phase == corev1.ClaimBound {
ctx.Log.Info("PVC still bound, pod may recover without failover",
"pvc", pvc.Name, "pod", candidate.PodName)
// 可以尝试删除 Pod 触发 reschedule 而非 failover
}
// ... existing failover logic ...
}
P1: Failover 时 PVC 保护策略
引入 failoverPVCRetentionPolicy 字段或将 failover 场景下的 PVC 从 Delete 改为 Retain:
// 方案 A: failover 前将旧 PVC annotation 保护
func retainPVCBeforeFailover(ctx, pvcName string) error {
pvc := getPVC(pvcName)
if pvc != nil {
pvc.Annotations["matrixorigin.io/failover-retain"] = "true"
}
}
// 方案 B: LogSet spec 新增字段
type LogSetSpec struct {
// FailoverPVCRetentionPolicy controls PVC behavior during failover
// Default: Retain (safer, requires manual cleanup)
FailoverPVCRetentionPolicy *PVCRetentionPolicy `json:"failoverPVCRetentionPolicy,omitempty"`
}
P2: Majority 故障通知机制
func (r *WithResources) Repair(ctx *recon.Context[*v1alpha1.LogSet]) error {
if len(ctx.Obj.Status.FailedStores) > minorityLimit {
// NEW: emit event + condition 供告警系统感知
ctx.Event(ctx.Obj, "Warning", "MajorityFailure",
fmt.Sprintf("%d/%d stores failed, manual intervention required",
len(ctx.Obj.Status.FailedStores), ctx.Obj.Spec.Replicas))
ctx.Obj.Status.SetCondition(metav1.Condition{
Type: "MajorityFailure",
Status: metav1.ConditionTrue,
Reason: "QuorumLost",
Message: "Majority of log stores failed, waiting for human intervention",
})
return nil
}
}
P3: 增加 storeFailureTimeout 推荐值与 Backoff
- 对于节点级故障(多 Pod 同时 down),timeout 应至少 20-30 分钟
- 考虑引入 exponential backoff:首次 failover 后延长后续 failover 的等待时间
- 在 webhook defaulter 中基于 replicas 数量推算推荐值
P4: S3 Bucket Reclaim 安全性
- 当检测到 PVC 重建导致 WAL 丢失时,S3 数据应绝对保护不被清理
- 建议在 BucketClaim 上增加 protectedUntil annotation,在 PVC 重建后设置一个保护期(如 7 天),期间不允许 S3 清理 Job 执行
复现条件
- LogSet replicas: 3, logShardReplicas: 3
- storeFailureTimeout: 10m0s
- pvcRetentionPolicy: Delete
- storageClass: directpv-ssd(或其他 local-volume)
- 多节点同时短暂不可用(断电重启、网络分区后恢复)
期望行为
- 节点恢复后,operator 应优先等待 Pod 自恢复而非立即 failover
- 即使 failover,旧 PVC 应保留而非立即删除(至少在确认新 Pod 数据完整性后再清理)
- Majority failure 时应发出 K8s Event / Condition 供外部告警感知
- S3 数据在 PVC 重建场景下应有保护期
相关代码
| 文件 |
关键逻辑 |
| pkg/controllers/logset/controller.go:217 |
Repair — failover 入口 |
| pkg/controllers/logset/controller.go:108 |
Observe — StoresFailedFor 触发 Repair |
| pkg/controllers/logset/sts.go:142 |
syncStatefulSetSpec — PVCRetentionPolicy 配置 |
| pkg/controllers/common/failover.go:35 |
CollectStoreStatus — 计算 AvailableStores/FailedStores |
| api/core/v1alpha1/logset_types.go:57 |
StoreFailureTimeout 定义与默认值 |
| api/core/v1alpha1/logset_helpers.go:34 |
setDefaultRetentionPolicy — 默认 Delete |
| pkg/controllers/bucketclaim/controller.go:64 |
BucketClaim Finalize — S3 清理 Job |
| pkg/webhook/logset_webhook.go:182 |
S3 immutable 校验 |
环境信息
- Operator 版本: submodule at commit (third/matrixone-operator)
- 事故集群: IDC mo-benchmark-moi (idc-unit)
- MatrixOne 版本: v3.0.11-6a0394c4f-2026-05-11
关联 Issue
背景
2026-05-17 IDC 环境多节点同时重启导致 LogSet 3 副本全部短暂不可用(~1 分钟)。matrixone-operator 重启后观察到 majority failure,等待 storeFailureTimeout(10m)后触发 Repair 逻辑。但由于节点已恢复而 Pod 未能重新 schedule,operator 最终执行了 PVC 强制重建,导致 WAL 全部丢失。后续 DN 从 S3 恢复时部分 block 元数据缺失,CN 触发 panic 形成 crash loop。
当前 Operator 处理逻辑分析
Repair 路径 (pkg/controllers/logset/controller.go:217)
PVC 生命周期
问题链条
len(FailedStores)=3 > minorityLimit(1)→ 进入 "wait for human intervention"S3 Bucket Reclaim Job(关联问题)
当 s3Reclaim feature gate 启用且 s3RetentionPolicy=Delete 时,集群删除还会触发一个 aws-cli Job 清理 S3 数据(pkg/controllers/bucketclaim/controller.go)。在 PVC 重建场景下,如果集群被判定不可恢复而整体重建,S3 数据可能也被一并清理,彻底断绝恢复可能。
问题总结
优化建议
P0: Repair 前增加 PVC/Node 可用性检查
P1: Failover 时 PVC 保护策略
引入 failoverPVCRetentionPolicy 字段或将 failover 场景下的 PVC 从 Delete 改为 Retain:
P2: Majority 故障通知机制
P3: 增加 storeFailureTimeout 推荐值与 Backoff
P4: S3 Bucket Reclaim 安全性
复现条件
期望行为
相关代码
环境信息