Kubernetes PV数据卷缩容方案及统计PV容量

在 Kubernetes 中,直接对 Persistent Volume (PV) 进行容量缩容(减小容量)通常是不被支持的。这主要是出于数据安全的考虑,因为贸然缩小底层存储设备可能会破坏数据。

理解 PV 缩容的限制

Kubernetes 及其存储生态系统在设计上就偏向于扩容,对缩容则有严格限制,主要原因如下:

  • 数据安全风险:缩容操作可能导致数据被截断或丢失。如果新的容量小于已存储的数据量,后果不堪设想。

  • 底层存储限制:许多底层存储系统(如 AWS EBS、Longhorn 等)并不支持在线缩小卷容量。例如,LVM 的 pvresize 命令也会拒绝缩小已分配空间的物理卷。

  • Kubernetes 机制限制:Kubernetes 的 allowVolumeExpansion 配置项仅控制扩容,并不会开启缩容功能。

替代方案与变通方法

虽然不能直接缩容,但是可以考虑以下替代方案来管理存储资源:

方案核心步骤优点注意事项
重建 PV 和 PVC1. 备份数据
2. 创建新的、容量更小的 PV 和 PVC。
3. 将数据从旧 PV 迁移到新 PV。
4. 更新 Pod 配置以使用新 PVC,并删除旧资源。
可靠且通用,适用于大多数不支持直接缩容的场景。过程繁琐,涉及业务中断数据迁移
调整存储配额通过 Kubernetes 的 ResourceQuota 来限制命名空间未来的存储请求总量,而不是缩小已有卷。无数据风险,有效预防未来存储资源的过度使用。无法释放已分配的存储空间
利用存储供应商特性某些云厂商的存储服务(如 NetApp Trident)可能通过精简配置(Thin Provisioning)等方式,在逻辑上限制容量使用,而无需物理缩容高效利用存储资源依赖特定存储后端,并非普适性解决方案。

总而言之,在 Kubernetes 中直接对 PV 进行缩容操作是不可行的。当需要减少 PV 容量时,数据迁移和PV/PVC重建是目前最主流和安全的做法。通过创建一个容量更小的新PV,然后迁移数据,可以实现存储空间的"缩容"。这个过程的核心步骤包括:准备新PV、迁移数据、更新应用指向新存储,以及清理旧资源。

为了更安全地进行操作,请务必先留意以下关键风险点和前提:

关键项目重要说明
数据备份务必先备份原始数据,以防迁移过程中发生意外丢失。
业务中断数据迁移通常需要停止相关Pod,请规划在业务低峰期进行。
存储类型确保Kubernetes集群和存储系统支持动态供应或静态PV创建。
应用兼容性确保应用能够适应存储卷的切换。

详细操作步骤

1、确认当前PV/PVC状态并备份数据
执行以下命令,记录当前PV(Persistent Volume)和PVC(Persistent Volume Claim)的详细信息,特别是当前的存储容量和访问模式。

kubectl get pv <old-pv-name> -o yaml
kubectl get pvc <old-pvc-name> -n <namespace> -o yaml

务必确保已经对重要数据进行了可靠备份

2、停止使用旧存储的Pod
为了避免数据不一致,需要先停止所有正在使用该PVC的Pod。具体的操作方法取决于工作负载类型:

    • 如果是Deployment,可以将其副本数缩容到0:kubectl scale deployment <deployment-name> --replicas=0 -n <namespace>

    • 如果是StatefulSet,同样缩容到0:kubectl scale statefulset <statefulset-name> --replicas=0 -n <namespace>

    • 如果是直接创建的Pod,则直接删除:kubectl delete pod <pod-name> -n <namespace>

3、创建新的、容量更小的PV
根据存储供应方式,选择静态或者动态方式创建新PV。

  • 静态供应:编写新的PV YAML文件,注意capacity.storage字段设置为目标缩容容量。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: new-small-pv  # 给新PV起个名字
spec:
  capacity:
    storage: 5Gi  # 这里设置你想要的、更小的容量
  accessModes:
    - ReadWriteOnce  # 根据你的需求设置访问模式
  persistentVolumeReclaimPolicy: Retain  # 建议先设置为Retain
  storageClassName: manual  # 指定存储类
  # ... 其他必要字段根据你的存储系统来定

然后应用这个YAML文件:kubectl apply -f new-pv.yaml

  • 动态供应:创建一个新的PVC,其spec.resources.requests.storage字段指定为缩容后的容量,并确保其绑定的StorageClass能正确工作。Kubernetes会自动为其创建并绑定一个符合要求的PV。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: new-small-pvc
  namespace: your-namespace
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi  # 指定缩容后的容量
  storageClassName: standard  # 指定能动态创建PV的StorageClass

应用这个YAML文件:kubectl apply -f new-pvc.yaml

4、将数据从旧PV迁移到新PV
这是最关键的一步。常见的方法有:

    • 使用kubectl cp命令:如果PV支持文件系统模式,可以启动一个临时的Pod挂载旧PVC,将数据拷贝到本地,然后另一个临时Pod挂载新PVC,再将数据从本地拷贝过去。

    • 使用rsync工具:对于大量数据,rsync效率更高。可能需要启动一个包含rsync的临时Pod来完成。

    • 存储系统快照/克隆:部分高级存储系统(如Longhorn, Portworx)自身支持卷的快照和克隆功能,可以利用这些功能来加速数据迁移。

5、修改Pod配置,指向新的PV/PVC
数据迁移并验证无误后,更新Pod配置模板(例如Deployment或StatefulSet的YAML文件),将其volumes部分指向新的PVC名称

volumes:
- name: my-storage
  persistentVolumeClaim:
    claimName: new-small-pvc  # 这里改为新PVC的名字

然后应用这个更新:kubectl apply -f your-app.yaml

6、重启应用Pod
根据第二步中停止Pod的方式,重新启动应用。

    • 如果是Deployment,将副本数扩展回目标数量:kubectl scale deployment <deployment-name> --replicas=<target-replicas> -n <namespace>

    • 如果是StatefulSet,同样操作:kubectl scale statefulset <statefulset-name> --replicas=<target-replicas> -n <namespace>

    • 如果是直接创建的Pod,则重新创建。

7、验证应用和数据
密切观察新Pod的启动日志和行为,确认应用能够正常访问新PV上的数据,并且功能符合预期。
可以再次使用kubectl get pvkubectl get pvc命令,确认新的PVC new-small-pvc已经处于Bound状态,并且绑定到了正确的PV上。

8、谨慎清理旧PV/PVC
在确认新环境稳定运行一段时间(例如24小时)后,并且确定不再需要旧数据时,再考虑清理旧的PV和PVC。
注意:清理操作是不可逆的,请再次确认数据安全。

kubectl delete pvc <old-pvc-name> -n <namespace>
kubectl delete pv <old-pv-name>

补充建议

  • 预防胜于治疗:在Kubernetes命名空间中,可以通过创建ResourceQuotaLimitRange资源来预防单个PVC请求过大的存储空间,从源头控制存储使用。

  • 理解回收策略:在删除旧PV时,务必了解其persistentVolumeReclaimPolicy(回收策略)。设置为Retain时,删除PVC后PV及相关后端存储资源会被保留;设置为Delete时,则可能会被自动清理。


K8s统计PV实际使用量

在PV卷缩容替代方案实施前,我们通常会统计Kubernetes集群中PV的总使用量,需要明白一点:kubectl get pv 命令本身无法直接提供PV的实际使用量,它主要显示的是PV的配置容量和状态信息。要获取PV的实际使用量,还需要借助其他方法。下面梳理几种可行的方案。

快速查看PV使用情况

如果想快速查看单个PV或PVC的实时使用情况,可以尝试以下方法:

方法操作说明
进入Pod查看1. 找到挂载目标PV的Pod:kubectl get pods -o wide --all-namespaces
2. 进入Pod并查看挂载点使用情况:kubectl exec -it <pod_name> -- df -h 或 `kubectl exec -it <pod_name> -- df -h
grep <mount_point>`优点:简单直接,无需额外工具。
局限:需要Pod处于运行状态,且只能查看单个Pod挂载的PV情况。
使用第三方工具安装 pvcpie 工具:pip install pvcpie,然后执行 pvcpie 命令一个专门用于获取集群中PVC使用信息的Python工具。

搭建监控系统获取全面数据

对于生产环境或需要长期、全面监控的场景,建议搭建监控系统:

1.部署Metrics Server与监控工具

    • 确保集群已安装 Metrics Server

    • 部署 Prometheus 来收集和存储指标数据

    • 部署 Grafana 进行数据可视化

2.利用Prometheus指标查询
Prometheus可以收集PV的实际使用量指标,例如 kubelet_volume_stats_used_bytes (卷中已使用的字节数) 和 kubelet_volume_stats_capacity_bytes (卷的总容量字节数)。可以使用PromQL查询语言进行统计。例如,查询所有PV的已用空间和容量:

kubelet_volume_stats_used_bytes
kubelet_volume_stats_capacity_bytes

3.配置Grafana仪表板

在Grafana中,可以基于Prometheus的数据源创建仪表板,通过图表直观展示PV的总使用量、使用率趋势等。

云平台或特定发行版的集成监控

如果使用的是特定云平台的Kubernetes服务(如Azure AKS)或特定发行版(如Red Hat OpenShift),它们通常提供了集成的监控方案:

  • Azure AKS:Azure Monitor的容器见解功能可以自动收集PV使用情况指标(如 pvUsedBytes),并提供了预配置的工作簿进行查看。

  • Red Hat OpenShift:集成了Prometheus,可以直接使用相关的PV监控指标

最佳实践与建议

  • 明确需求:根据需求(临时检查 vs. 长期监控)选择合适的方法。

  • 数据准确性:请注意,某些监控工具(如 kubectl top)提供的指标可能专为Kubernetes自动扩缩容决策优化,与操作系统工具(如 top)的结果可能不完全一致。

  • 配置告警:在监控系统基础上,为PV使用率配置告警,以便在空间不足时及时收到通知。

统计所有PV的配置容量总量,用kubectl get pv命令结合一些简单的文本处理工具就能做到。下面是一个汇总表格,列出了几种常用的方法:

方法命令特点
JSONPath提取kubectl get pv -o jsonpath='{.items[*].spec.capacity.storage}'直接提取容量值,但结果在同一行,需后续处理
JSONPath循环kubectl get pv -o jsonpath='{range .items[*]}{.spec.capacity.storage}{"\n"}{end}'每个PV容量单独一行,方便后续处理
自定义列kubectl get pv -o custom-columns=NAME:.metadata.name,CAPACITY:.spec.capacity.storage输出表格,直观显示每个PV的容量
自定义列(仅容量)kubectl get pv -o custom-columns=CAPACITY:.spec.capacity.storage --no-headers仅输出容量列,无表头,适合直接处理

重要提醒

  • 配置容量 vs 实际使用量kubectl get pv命令显示的是PV的配置容量(申请或初始设置的大小),而不是当前的实际数据使用量。要查看实际使用量,通常需要进入Pod内部使用df -h等命令,或借助集群监控系统(如Prometheus)。

  • 单位一致性:确保所有PV的容量单位一致(例如,都是Gi或都是Mi),或者在处理时进行了适当的单位转换,否则求和结果可能不准确。

  • 动态供应PV:对于通过StorageClass动态供应的PV,其配置容量通常由对应的PersistentVolumeClaim (PVC) 指定。

K8s统计PV配置容量

当PV的容量单位不一致时(比如同时存在Gi、Ki、Mi等),需要先统一单位再累加。以下是几种处理这种情况的方法:

方法一:使用awk进行单位转换和累加

kubectl get pv -o custom-columns=CAPACITY:.spec.capacity.storage --no-headers | awk '
{
    # 提取数字和单位
    if ($1 ~ /Gi$/) {
        gsub(/Gi/, "", $1)
        sum += $1 * 1024 * 1024  # Gi → Ki
    }
    else if ($1 ~ /Mi$/) {
        gsub(/Mi/, "", $1)
        sum += $1 * 1024  # Mi → Ki
    }
    else if ($1 ~ /Ki$/) {
        gsub(/Ki/, "", $1)
        sum += $1  # Ki保持不变
    }
    else if ($1 ~ /G$/) {
        gsub(/G/, "", $1)
        sum += $1 * 1000 * 1000  # G → K (十进制)
    }
    else if ($1 ~ /M$/) {
        gsub(/M/, "", $1)
        sum += $1 * 1000  # M → K (十进制)
    }
    else if ($1 ~ /K$/) {
        gsub(/K/, "", $1)
        sum += $1  # K保持不变
    }
    else {
        # 没有单位,假设是字节
        sum += $1 / 1024  # 字节 → Ki
    }
}
END {
    # 输出结果,可以选择合适的单位
    if (sum >= 1024*1024*1024) {
        printf "总容量: %.2f Ti\n", sum / (1024*1024*1024)
    } else if (sum >= 1024*1024) {
        printf "总容量: %.2f Gi\n", sum / (1024*1024)
    } else if (sum >= 1024) {
        printf "总容量: %.2f Mi\n", sum / 1024
    } else {
        printf "总容量: %.2f Ki\n", sum
    }
}'

方法二:使用jq进行更精确的处理

kubectl get pv -o json | jq '
[.items[].spec.capacity.storage | 
 capture("(?<value>[0-9.]+)(?<unit>[A-Za-z]+)") |
 {
   value: (.value | tonumber),
   factor: (if .unit == "Ti" then 1024*1024*1024*1024
           elif .unit == "Gi" then 1024*1024*1024
           elif .unit == "Mi" then 1024*1024
           elif .unit == "Ki" then 1024
           elif .unit == "T" then 1000*1000*1000*1000
           elif .unit == "G" then 1000*1000*1000
           elif .unit == "M" then 1000*1000
           elif .unit == "K" then 1000
           else 1 end)
 } | .value * .factor] | add' | awk '
{
    total_ki = $1 / 1024  # 转换为KiB基数
    if (total_ki >= 1024*1024*1024) {
        printf "总容量: %.2f Ti\n", total_ki / (1024*1024*1024)
    } else if (total_ki >= 1024*1024) {
        printf "总容量: %.2f Gi\n", total_ki / (1024*1024)
    } else if (total_ki >= 1024) {
        printf "总容量: %.2f Mi\n", total_ki / 1024
    } else {
        printf "总容量: %.2f Ki\n", total_ki
    }
}'

方法三:简化的单位转换脚本

如果经常需要这个功能,可以创建一个可重用的脚本:

#!/bin/bash
# pv-total-capacity.sh

calculate_pv_capacity() {
    kubectl get pv -o custom-columns=CAPACITY:.spec.capacity.storage --no-headers | awk '
    function to_kib(value, unit) {
        switch(unit) {
            case "Ti": return value * 1024 * 1024 * 1024 * 1024
            case "Gi": return value * 1024 * 1024 * 1024
            case "Mi": return value * 1024 * 1024
            case "Ki": return value * 1024
            case "T": return value * 1000 * 1000 * 1000 * 1000
            case "G": return value * 1000 * 1000 * 1000
            case "M": return value * 1000 * 1000
            case "K": return value * 1000
            default: return value  # 假设已经是字节
        }
    }
    
    {
        if (match($0, /([0-9.]+)([A-Za-z]*)/, parts)) {
            value = parts[1]
            unit = parts[2]
            sum_kib += to_kib(value, unit) / 1024
        }
    }
    END {
        # 输出各种单位的表示
        printf "总容量统计:\n"
        printf "  %d Bytes\n", sum_kib * 1024
        printf "  %.2f KiB\n", sum_kib
        printf "  %.2f MiB\n", sum_kib / 1024
        printf "  %.2f GiB\n", sum_kib / (1024*1024)
        printf "  %.2f TiB\n", sum_kib / (1024*1024*1024)
    }'
}

calculate_pv_capacity

使用建议

  1. 推荐使用方法一的awk脚本,它简单且功能完备

  2. 如果环境中有jq工具,方法二可以提供更精确的解析

  3. 对于生产环境,建议将方法三保存为脚本文件,方便重复使用

这些方法都能正确处理混合单位的情况,并输出易于理解的总容量统计结果。可以根据实际需求选择最适合的方法。

参考:Kubernetes 环境下华为云盘与 PV 数据目录缩容方案分析与实施

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/6556.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注