Carpathian Park, Ukraine
引言
对于这种案例,你们的处理思路是怎么样的呢,是否真正的处理过,如果遇到,你们应该怎么处理。
我想大多数人都没有遇到过。
最后有相关的学习群,有兴趣可以加入。
开始
一、Finalizers 机制深度解析
Finalizers 是 Kubernetes 中一种关键元数据字段,其设计初衷是确保资源删除前完成必要的清理操作(如释放外部依赖、删除关联资源)。以下从源码层面解析其工作机制:
- 资源删除流程
- 用户发起删除:执行
kubectl delete
后,资源metadata.deletionTimestamp
被标记,但不会立即删除。 - Finalizers 检查:API Server 检查
metadata.finalizers
列表:- 若列表为空,直接删除资源。
- 若列表非空,资源进入
Terminating
状态,并等待控制器处理。
- 控制器处理:关联控制器(如自定义 Operator)执行清理逻辑,完成后移除自身 Finalizer。
- 资源释放:当所有 Finalizers 被移除,API Server 真正删除资源。
- 用户发起删除:执行
- 源码关键逻辑
Kubernetes 源码(pkg/registry/core/namespace/storage/storage.go
)中处理 Namespace 删除的简化逻辑:
// 检查 Finalizers 是否为空
if len(namespace.Finalizers) == 0 {
// 直接删除
return deleteResource()
} else {
// 标记 deletionTimestamp 并等待
namespace.DeletionTimestamp = &metav1.Time{Time: time.Now()}
updateResource(namespace)
}
- 3. Finalizers 与 GC(垃圾回收)的交互
- Finalizers 优先级高于 Kubernetes 内置的垃圾回收机制。
- 即使子资源(如 Pod、Service)有 OwnerReference,若父资源(如 Namespace)的 Finalizers 未完成,子资源也不会被自动删除。
二、真实生产案例扩展
案例 1:自定义控制器崩溃导致 Finalizers 死锁
- • 背景:某电商平台的自定义「日志归档控制器」负责在 Namespace 删除时清理 S3 存储桶。
- • 故障现象:
- • 删除 Namespace 卡在
Terminating
状态。 - • 控制器日志持续报错
Failed to delete bucket: AccessDenied
。
- • 删除 Namespace 卡在
- • 根因分析:
- • IAM 角色权限配置错误,控制器无法访问 S3。
- • 控制器未实现重试逻辑,首次失败后不再尝试清理。
- • 解决方案:
// 控制器代码改进示例(添加指数退避重试) retryConfig := wait.Backoff{ Steps: 5, Duration: 1 * time.Second, Factor: 2.0, Jitter: 0.1, } err := wait.ExponentialBackoff(retryConfig, func() (bool, error) { err := deleteS3Bucket(bucketName) if err != nil { return false, nil // 继续重试 } return true, nil })
案例 2:跨集群资源依赖未解除
- • 背景:某多云架构中,Namespace 关联了跨集群的联邦资源(如 ClusterRoleBinding)。
- • 故障现象:
- • 删除 Namespace 后,联邦控制平面持续尝试同步资源,Finalizers 无法完成。
- • 根因分析:
- • 联邦控制器未正确处理跨集群资源的清理依赖。
- • 解决方案:
# 手动清理联邦资源 kubefedctl delete federatednamespace my-ns --cluster-context=cluster-ctx # 移除 Finalizers kubectl patch ns my-ns -p '{"metadata":{"finalizers":[]}}' --type=merge
三、系统性解决方案进阶
1. 手动移除 Finalizers(应急场景)
- • 适用场景:控制器不可用且需快速恢复。
- • 详细步骤:
- 导出资源配置:
kubectl get ns my-ns -o json > tmp.json
- 编辑
tmp.json
,清空finalizers
字段:{ "metadata": { "finalizers": [] } }
- 通过 Kubernetes API 强制更新:
注意:若使用curl -X PUT \ -H "Content-Type: application/json" \ --data-binary @tmp.json \ http://localhost:8080/api/v1/namespaces/my-ns/finalize
kubectl proxy
需先启动代理(kubectl proxy --port=8080
)。
- 导出资源配置:
2. 控制器设计的防御性实践
- • 代码健壮性:
- • 最终一致性保证:控制器需处理资源中途删除的场景,避免因资源不存在而崩溃。
- • Finalizers 生命周期管理:
// 添加 Finalizer if !containsString(resource.Finalizers, myFinalizer) { resource.Finalizers = append(resource.Finalizers, myFinalizer) if err := r.Update(ctx, resource); err != nil { return err } } // 清理 Finalizer if containsString(resource.Finalizers, myFinalizer) { resource.Finalizers = removeString(resource.Finalizers, myFinalizer) if err := r.Update(ctx, resource); err != nil { return err } }
- • 优雅终止处理:
在控制器 Pod 的 PreStop Hook 中清理 Finalizers:lifecycle: preStransform: translateY( exec: command: ["/bin/sh", "-c", "curl -X POST http://localhost:8080/cleanup"]
3. 监控与自动化修复
- • Prometheus 告警规则:
- alert: StuckTerminatingNamespace expr: kube_namespace_status_phase{phase="Terminating"} > 0 for: 10m annotations: summary: "Namespace {{ $labels.namespace }} is stuck in Terminating state"
- • 自动化修复脚本:
#!/bin/bash STUCK_NS=$(kubectl get ns --field-selector status.phase=Terminating -o name) for ns in $STUCK_NS; do kubectl get $ns -o json | jq '.metadata.finalizers = []' > tmp.json kubectl replace --raw "/api/v1/namespaces/${ns#*/}/finalize" -f tmp.json done
四、预防措施与最佳实践
- Finalizers 最小化原则
- 仅在绝对必要时添加 Finalizers,清理完成后立即移除。
- 避免跨资源依赖(如 Namespace 依赖另一个集群的资源)。
- 控制器混沌测试
- 使用 Chaos Mesh 模拟控制器崩溃场景:
apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: name: controller-crash spec: action: pod-failure duration: "10m" selector: labelSelector: "app": "my-controller"
- 使用 Chaos Mesh 模拟控制器崩溃场景:
- 定期审计与清理
- 扫描所有
Terminating
资源:kubectl get ns,po,svc --all-namespaces --field-selector=metadata.deletionTimestamp!=""
- 扫描所有
五、Kubernetes 生态工具推荐
工具 | 作用 | 使用场景 |
kubectl-neat | 清理资源配置中的冗余字段 | 手动操作前查看纯净配置 |
kube-score | 检查资源配置合理性(含 Finalizers 风险) | 预发环境卡点检测 |
kubectl-watch | 实时监控资源状态变化 | 观察 Finalizers 移除过程 |
总结
Finalizers 机制是 Kubernetes 的“安全锁”,但其对控制器逻辑的强依赖也使其成为潜在的风险点。通过以下策略构建防御体系:
- 代码防御:控制器需实现重试、超时和优雅终止逻辑。
- 监控兜底:实时告警 + 自动化修复脚本。
- 混沌验证:定期模拟故障,验证系统容错能力。
理解 Finalizers 的底层逻辑,结合最佳实践与工具链,方能在大规模 Kubernetes 集群中游刃有余。
结语
以上就是我们今天的内容,希望可以帮助到大家。