一、当你的Kubernetes集群开始"发福"

每次查看集群监控大盘时,总能看到那些默默消耗存储资源的"僵尸"——三个月前测试环境使用的数据库PV、半年前活动项目遗留的配置文件、开发人员忘记清理的临时PVC。某头部电商的运维团队曾发现,其生产集群中38%的PVC处于Bound状态但实际已不再使用,这些资源每年造成数百万元的云存储浪费。

二、资源清理三部曲原理揭秘

2.1 PV/PVC绑定状态解密

PV与PVC的关系就像租房合约:PVC是租客的申请(PersistentVolumeClaim),PV是签约的房源。常见的僵尸资源包括:

  • 没有PVC绑定的Available状态PV(无人租赁的空房)
  • 被删除工作负载持有的Bound状态PVC(租客已搬离但未退租)
# 使用kubectl查询僵尸PVC(技术栈:原生kubectl)
kubectl get pvc -A -o jsonpath='{range .items[?(@.status.phase=="Bound")]}{.metadata.namespace}{"|"}{.metadata.name}{"|"}{.status.volumeName}{"\n"}{end}' > active-pvcs.txt

# 对比当前正在运行的Pod挂载情况
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"|"}{.spec.volumes[*].persistentVolumeClaim.claimName}{"\n"}{end}' | sort -u > used-pvcs.txt

# 使用comm命令找出差值(示例输出注释)
comm -23 <(sort active-pvcs.txt) <(sort used-pvcs.txt) > orphan-pvcs.list

2.2 ConfigMap的幽灵陷阱

一个电商平台的微服务架构中,经常会产生各种环境特定的配置文件:

# 测试环境专属配置(技术栈:Kubernetes YAML)
apiVersion: v1
kind: ConfigMap
metadata:
  name: payment-service-test-config
data:
  database.url: "jdbc:mysql://test-db:3306/payment"
  feature.toggle: "AB_TEST_ENABLED=false"

当对应的Deployment被删除后,这些ConfigMap就成为"数字废墟"。通过标签关联性检查可以精准定位:

# 查找未被引用的ConfigMap(带注释说明)
kubectl get cm -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{"\n"}{end}' > all-cm.list
kubectl get deployments,statefulsets,daemonsets -A -o jsonpath='{range .items[*]}{.spec.template.spec.volumes[*].configMap.name}{"\n"}{end}' | sort -u > used-cm.list
join -v1 -t/ <(sort all-cm.list) <(sort used-cm.list) > orphan-cm.list

三、实战清理武器库

3.1 手动精准清除(适合小规模集群)

危险操作前必须执行的防护措施:

# 为所有待删除资源创建备份快照
kubectl get pv -o yaml > pv-backup-$(date +%Y%m%d).yaml
kubectl get pvc -A -o yaml > pvc-backup-$(date +%Y%m%d).yaml

渐进式删除策略示例:

# 分阶段删除确认(带保护性等待)
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
  echo "Processing namespace: $ns"
  kubectl delete $(cat orphan-pvcs.list) -n $ns --dry-run=client
  read -p "确认删除上述PVC?(y/n)" confirm
  if [ "$confirm" = "y" ]; then
    kubectl delete $(cat orphan-pvcs.list) -n $ns
    sleep 10 # 等待资源释放
  fi
done

3.2 自动化清理机器人(适合大中型集群)

使用Kubernetes批处理Job实现智能清理:

// 使用client-go实现自动化清理(技术栈:Golang)
func main() {
    config, _ := rest.InClusterConfig()
    clientset, _ := kubernetes.NewForConfig(config)
    
    // 获取所有未被引用的ConfigMap
    orphanCMs := findOrphanConfigMaps(clientset)
    
    // 安全删除逻辑
    for _, cm := range orphanCMs {
        if cm.Annotations["backup/status"] != "archived" {
            createBackup(cm)
            clientset.CoreV1().ConfigMaps(cm.Namespace).Delete(
                context.TODO(), cm.Name, metav1.DeleteOptions{})
        }
    }
}

// 关联性检查算法核心
func isReferenced(cm *v1.ConfigMap) bool {
    // 检查所有工作负载的Volume挂载
    // 检查Pod环境变量引用
    // 检查InitContainer配置引用
    return false
}

四、技术方案对比雷达图

  • 手动清理:操作灵活但效率低下,适合<50节点集群
  • 定时脚本:维护成本中等,需处理异常情况
  • Operator方案:学习曲线陡峭,适合有成熟运维体系的团队

五、血泪经验总结

5.1 防患未然的五个锦囊

  1. 资源标签规范化:要求所有PV/PVC必须包含creator=声明

    metadata:
      labels:
        environment: "dev"
        owner: "payment-team"
        created-by: "terraform"
    
  2. Finalizers防御术:关键PV设置防误删保护

    kubectl patch pv pv-001 -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}'
    
  3. 存储类回收策略黄金法则:

    StorageClass回收策略与业务场景对应表
    
    | 业务类型   | 回收策略       | 数据重要性 |
    |------------|----------------|------------|
    | 生产数据库 | Retain         | 高         |
    | CI/CD缓存  | Delete         | 低         |
    | 日志存储   | Recycle        | 中         |
    

5.2 你必须知道的八个陷阱

  1. StatefulSet管理的PVC不能直接删除
  2. Pre-binding模式的PV需要特殊处理
  3. 动态供应的PV删除顺序陷阱
  4. ConfigMap被InitContainer间接引用
  5. 跨命名空间的Secret引用问题
  6. 节点维护时的临时Volume挂接
  7. CSI驱动的特殊删除逻辑
  8. 存储配额计算的时间延迟