0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Kubernetes存储管理功能的落地实践

马哥Linux运维 来源:马哥Linux运维 2026-02-26 14:45 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

Kubernetes存储管理:PV、PVC与StorageClass

一、概述

1.1 背景介绍

容器本身是无状态的,Pod重启后容器内的数据全部丢失。数据库、消息队列、文件存储这类有状态服务跑在K8s上,必须解决持久化存储问题。Kubernetes通过PersistentVolume(PV)、PersistentVolumeClaim(PVC)和StorageClass三层抽象来管理存储。

实际生产中踩过的坑:开发团队直接在Pod里用hostPath挂载宿主机目录,Pod漂移到其他节点后数据就丢了。还有团队手动创建了100个PV,每次扩容都要运维手动操作,效率极低。StorageClass的动态供给机制就是解决这个问题的。

本文覆盖静态供给、动态供给、本地存储、NFS、Ceph RBD等常见存储方案,基于Kubernetes 1.28.x版本。

1.2 技术特点

存储与计算解耦:PV是集群级别的存储资源,PVC是用户对存储的申请,两者通过绑定关系关联,Pod只需要引用PVC

动态供给:StorageClass配合Provisioner自动创建PV,无需运维手动干预

访问模式控制:ReadWriteOnce(单节点读写)、ReadOnlyMany(多节点只读)、ReadWriteMany(多节点读写),不同存储后端支持的模式不同

回收策略:Retain(保留数据)、Delete(删除数据)、Recycle(已废弃),控制PVC删除后PV的处理方式

1.3 适用场景

场景一:数据库(MySQL、PostgreSQL)持久化存储,要求高IOPS和数据安全

场景二:日志、文件上传等共享存储,多个Pod需要同时读写同一份数据

场景三:AI训练数据集存储,大容量、高吞吐量读取

场景四:StatefulSet有状态应用,每个Pod需要独立的持久化卷

1.4 环境要求

组件 版本要求 说明
Kubernetes 1.24+ CSI驱动在1.13 GA,本文使用1.28
NFS Server NFSv4 共享存储方案,需要独立NFS服务器
Ceph 16.x+ (Pacific) 分布式存储方案,生产推荐
nfs-subdir-external-provisioner 4.0+ NFS动态供给控制器
ceph-csi 3.9+ Ceph CSI驱动
存储节点磁盘 SSD/HDD按需 数据库用SSD,归档用HDD

二、详细步骤

2.1 准备工作

2.1.1 存储基础概念

PV、PVC、StorageClass的关系:

用户视角:Pod → PVC(我需要10Gi存储)
        ↓ 绑定
管理员视角:  PV(这块10Gi的存储可以用)
        ↑ 创建
自动化视角:  StorageClass + Provisioner(自动创建PV)
# 查看集群现有的StorageClass
kubectl get sc

# 查看PV和PVC
kubectl get pv
kubectl get pvc -A

# 查看CSI驱动
kubectl get csidrivers

2.1.2 存储方案选型

存储方案 访问模式 性能 适用场景 运维复杂度
hostPath RWO 高(本地磁盘) 测试环境,单节点
Local PV RWO 高(本地SSD) 数据库,对IO敏感
NFS RWO/ROX/RWX 共享文件存储
Ceph RBD RWO 数据库,块存储
CephFS RWO/ROX/RWX 中高 共享文件存储
云厂商EBS/云盘 RWO 云环境数据库
Longhorn RWO/RWX 轻量级分布式存储

2.1.3 搭建NFS服务器

NFS是最简单的共享存储方案,适合中小规模集群:

# NFS服务器(192.168.1.50)上执行
apt-get install -y nfs-kernel-server

# 创建共享目录
mkdir -p /data/nfs/k8s
chown nobody:nogroup /data/nfs/k8s
chmod 755 /data/nfs/k8s

# 配置NFS导出
cat > /etc/exports << 'EOF'
/data/nfs/k8s  192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
EOF

# 生效配置
exportfs -ra

# 启动NFS服务
systemctl restart nfs-kernel-server
systemctl enable nfs-kernel-server

# 验证
showmount -e localhost

K8s所有Worker节点安装NFS客户端:

# Ubuntu
apt-get install -y nfs-common

# CentOS
yum install -y nfs-utils

# 验证能挂载
mount -t nfs 192.168.1.50:/data/nfs/k8s /mnt
ls /mnt
umount /mnt

注意:no_root_squash允许客户端以root身份写入,生产环境如果安全要求高,改为root_squash并通过initContainer设置目录权限。

2.2 核心配置

2.2.1 静态PV/PVC(手动创建)

适合存储资源固定、数量少的场景:

# 文件:nfs-pv-static.yaml
apiVersion:v1
kind:PersistentVolume
metadata:
name:nfs-pv-data-01
labels:
 type:nfs
 env:production
spec:
capacity:
 storage:50Gi
accessModes:
 -ReadWriteMany
persistentVolumeReclaimPolicy:Retain
storageClassName:""
nfs:
 server:192.168.1.50
 path:/data/nfs/k8s/data-01
---
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:app-data-pvc
namespace:production
spec:
accessModes:
 -ReadWriteMany
resources:
 requests:
  storage:50Gi
storageClassName:""
selector:
 matchLabels:
  type:nfs
  env:production
# 先在NFS服务器上创建目录
mkdir -p /data/nfs/k8s/data-01

# 创建PV和PVC
kubectl apply -f nfs-pv-static.yaml

# 验证绑定状态
kubectl get pv nfs-pv-data-01
# STATUS应该是Bound

kubectl get pvc app-data-pvc -n production
# STATUS应该是Bound

注意:静态PV的storageClassName设为空字符串"",PVC也要设为空字符串,这样PVC只会绑定没有StorageClass的PV。如果不设置这个字段,PVC会尝试使用默认StorageClass进行动态供给。

2.2.2 NFS动态供给(StorageClass)

安装nfs-subdir-external-provisioner,实现NFS的动态PV创建:

# 使用Helm安装
helm repo add nfs-subdir-external-provisioner 
 https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/

helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner 
 --namespace kube-system 
 --setnfs.server=192.168.1.50 
 --setnfs.path=/data/nfs/k8s 
 --setstorageClass.name=nfs-client 
 --setstorageClass.defaultClass=false
 --setstorageClass.reclaimPolicy=Retain 
 --setstorageClass.archiveOnDelete=true

# 验证StorageClass创建成功
kubectl get sc nfs-client

手动创建StorageClass(不用Helm的方式):

# 文件:nfs-storageclass.yaml
apiVersion:storage.k8s.io/v1
kind:StorageClass
metadata:
name:nfs-client
annotations:
 storageclass.kubernetes.io/is-default-class:"false"
provisioner:cluster.local/nfs-provisioner
parameters:
archiveOnDelete:"true"
reclaimPolicy:Retain
volumeBindingMode:Immediate
allowVolumeExpansion:true
mountOptions:
-hard
-nfsvers=4.1
-timeo=600
-retrans=3

参数说明

archiveOnDelete: "true":PVC删除时不删除NFS上的数据,而是重命名目录加archived-前缀,防止误删

reclaimPolicy: Retain:PVC删除后PV保留,需要手动清理。生产环境必须用Retain,用Delete会直接删数据

volumeBindingMode: Immediate:PVC创建后立即绑定PV。Local PV要用WaitForFirstConsumer

allowVolumeExpansion: true:允许PVC扩容,NFS支持在线扩容

2.2.3 Local PV(本地持久卷)

适合数据库等对IO性能敏感的场景,数据存储在节点本地SSD上:

# 文件:local-storageclass.yaml
apiVersion:storage.k8s.io/v1
kind:StorageClass
metadata:
name:local-ssd
provisioner:kubernetes.io/no-provisioner
volumeBindingMode:WaitForFirstConsumer
reclaimPolicy:Retain
# 文件:local-pv.yaml
apiVersion:v1
kind:PersistentVolume
metadata:
name:local-pv-worker01-ssd01
spec:
capacity:
 storage:100Gi
accessModes:
 -ReadWriteOnce
persistentVolumeReclaimPolicy:Retain
storageClassName:local-ssd
local:
 path:/data/local-volumes/ssd01
nodeAffinity:
 required:
  nodeSelectorTerms:
   -matchExpressions:
     -key:kubernetes.io/hostname
      operator:In
      values:
       -k8s-worker-01
# 在对应节点上创建目录
mkdir -p /data/local-volumes/ssd01

# 如果是独立SSD,挂载到该目录
# mkfs.xfs /dev/sdb
# mount /dev/sdb /data/local-volumes/ssd01
# echo '/dev/sdb /data/local-volumes/ssd01 xfs defaults 0 0' >> /etc/fstab

kubectl apply -flocal-storageclass.yaml
kubectl apply -flocal-pv.yaml

警告:Local PV的数据和节点绑定,节点故障数据就丢了。生产环境用Local PV必须配合应用层的数据复制(如MySQL主从、Redis Sentinel)来保证数据安全。

2.2.4 Ceph RBD存储(生产推荐)

Ceph提供块存储(RBD)和文件存储(CephFS),通过ceph-csi驱动接入K8s:

# 前提:已有Ceph集群,获取以下信息
# - Ceph Monitor地址:192.168.1.60:6789,192.168.1.61:6789,192.168.1.62:6789
# - Ceph集群ID:通过 ceph fsid 获取
# - 管理员密钥:通过 ceph auth get-key client.admin 获取

# 在Ceph集群上创建K8s专用pool和用户
ceph osd pool create k8s-rbd 128 128
ceph osd pool applicationenablek8s-rbd rbd
rbd pool init k8s-rbd

ceph auth get-or-create client.k8s-rbd 
 mon'profile rbd'
 osd'profile rbd pool=k8s-rbd'
 -o /etc/ceph/ceph.client.k8s-rbd.keyring

在K8s中配置ceph-csi:

# 安装ceph-csi(使用Helm)
helm repo add ceph-csi https://ceph.github.io/csi-charts

helm install ceph-csi-rbd ceph-csi/ceph-csi-rbd 
 --namespace ceph-csi 
 --create-namespace 
 --setcsiConfig[0].clusterID= 
 --setcsiConfig[0].monitors[0]=192.168.1.60:6789 
 --setcsiConfig[0].monitors[1]=192.168.1.61:6789 
 --setcsiConfig[0].monitors[2]=192.168.1.62:6789

创建Secret和StorageClass:

# 文件:ceph-rbd-secret.yaml
apiVersion:v1
kind:Secret
metadata:
name:ceph-rbd-secret
namespace:ceph-csi
type:kubernetes.io/rbd
stringData:
userID:k8s-rbd
userKey:
---
apiVersion:v1
kind:Secret
metadata:
name:ceph-rbd-admin-secret
namespace:ceph-csi
type:kubernetes.io/rbd
stringData:
userID:admin
userKey:
# 文件:ceph-rbd-storageclass.yaml
apiVersion:storage.k8s.io/v1
kind:StorageClass
metadata:
name:ceph-rbd
annotations:
 storageclass.kubernetes.io/is-default-class:"true"
provisioner:rbd.csi.ceph.com
parameters:
clusterID:
pool:k8s-rbd
imageFormat:"2"
imageFeatures:layering
csi.storage.k8s.io/provisioner-secret-name:ceph-rbd-secret
csi.storage.k8s.io/provisioner-secret-namespace:ceph-csi
csi.storage.k8s.io/controller-expand-secret-name:ceph-rbd-secret
csi.storage.k8s.io/controller-expand-secret-namespace:ceph-csi
csi.storage.k8s.io/node-stage-secret-name:ceph-rbd-secret
csi.storage.k8s.io/node-stage-secret-namespace:ceph-csi
reclaimPolicy:Retain
allowVolumeExpansion:true
volumeBindingMode:Immediate
mountOptions:
-discard
kubectl apply -f ceph-rbd-secret.yaml
kubectl apply -f ceph-rbd-storageclass.yaml

# 验证
kubectl get sc ceph-rbd

2.2.5 PVC扩容

# 确认StorageClass支持扩容
kubectl get sc ceph-rbd -o jsonpath='{.allowVolumeExpansion}'
# 输出:true

# 编辑PVC,修改storage大小
kubectl patch pvc mysql-data -n database -p'{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'

# 查看扩容状态
kubectl get pvc mysql-data -n database
# 如果显示 FileSystemResizePending,需要Pod重启后才能完成文件系统扩容
# Ceph RBD支持在线扩容,不需要重启Pod

kubectl get events -n database --field-selector involvedObject.name=mysql-data

注意:PVC只能扩容不能缩容。NFS支持在线扩容,Ceph RBD支持在线扩容,Local PV不支持扩容。

2.3 启动和验证

2.3.1 使用PVC的Pod

# 文件:app-with-pvc.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:app-with-storage
namespace:default
spec:
replicas:3
selector:
 matchLabels:
  app:app-storage
template:
 metadata:
  labels:
   app:app-storage
 spec:
  containers:
   -name:app
    image:nginx:1.24
    volumeMounts:
     -name:shared-data
      mountPath:/usr/share/nginx/html
     -name:logs
      mountPath:/var/log/nginx
    resources:
     requests:
      cpu:100m
      memory:128Mi
  volumes:
   -name:shared-data
    persistentVolumeClaim:
     claimName:app-shared-pvc
   -name:logs
    persistentVolumeClaim:
     claimName:app-logs-pvc
---
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:app-shared-pvc
namespace:default
spec:
accessModes:
 -ReadWriteMany
storageClassName:nfs-client
resources:
 requests:
  storage:10Gi
---
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:app-logs-pvc
namespace:default
spec:
accessModes:
 -ReadWriteMany
storageClassName:nfs-client
resources:
 requests:
  storage:20Gi
kubectl apply -f app-with-pvc.yaml

# 验证PVC绑定
kubectl get pvc -n default
# NAME       STATUS  VOLUME                   CAPACITY  ACCESS MODES  STORAGECLASS
# app-shared-pvc  Bound  pvc-xxxx-xxxx               10Gi    RWX      nfs-client
# app-logs-pvc   Bound  pvc-yyyy-yyyy               20Gi    RWX      nfs-client

# 验证Pod挂载
kubectlexec-it $(kubectl get pod -l app=app-storage -o jsonpath='{.items[0].metadata.name}') -- df -h /usr/share/nginx/html

2.3.2 StatefulSet的volumeClaimTemplates

StatefulSet的每个Pod自动创建独立的PVC:

# 文件:redis-statefulset.yaml
apiVersion:apps/v1
kind:StatefulSet
metadata:
name:redis
namespace:default
spec:
serviceName:redis
replicas:3
selector:
 matchLabels:
  app:redis
template:
 metadata:
  labels:
   app:redis
 spec:
  containers:
   -name:redis
    image:redis:7.2
    ports:
     -containerPort:6379
    volumeMounts:
     -name:redis-data
      mountPath:/data
    resources:
     requests:
      cpu:200m
      memory:256Mi
volumeClaimTemplates:
 -metadata:
   name:redis-data
  spec:
   accessModes:["ReadWriteOnce"]
   storageClassName:ceph-rbd
   resources:
    requests:
     storage:10Gi
kubectl apply -f redis-statefulset.yaml

# 验证:每个Pod有独立的PVC
kubectl get pvc -l app=redis
# redis-data-redis-0  Bound  pvc-aaa  10Gi  RWO  ceph-rbd
# redis-data-redis-1  Bound  pvc-bbb  10Gi  RWO  ceph-rbd
# redis-data-redis-2  Bound  pvc-ccc  10Gi  RWO  ceph-rbd

注意:StatefulSet删除后PVC不会自动删除,需要手动清理。这是设计如此,防止误删数据。

三、示例代码和配置

3.1 完整配置示例

3.1.1 MySQL主从 + Ceph RBD完整存储方案

# 文件:mysql-storage-complete.yaml
# MySQL主库 - 使用Ceph RBD高性能块存储
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:mysql-master-data
namespace:database
spec:
accessModes:
 -ReadWriteOnce
storageClassName:ceph-rbd
resources:
 requests:
  storage:100Gi
---
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:mysql-master-logs
namespace:database
spec:
accessModes:
 -ReadWriteOnce
storageClassName:ceph-rbd
resources:
 requests:
  storage:50Gi
---
apiVersion:apps/v1
kind:Deployment
metadata:
name:mysql-master
namespace:database
spec:
replicas:1
strategy:
 type:Recreate
selector:
 matchLabels:
  app:mysql
  role:master
template:
 metadata:
  labels:
   app:mysql
   role:master
 spec:
  containers:
   -name:mysql
    image:mysql:8.0.35
    ports:
     -containerPort:3306
    env:
     -name:MYSQL_ROOT_PASSWORD
      valueFrom:
       secretKeyRef:
        name:mysql-secret
        key:root-password
    args:
     ---innodb-buffer-pool-size=2G
     ---innodb-log-file-size=512M
     ---innodb-flush-method=O_DIRECT
     ---innodb-io-capacity=2000
     ---innodb-io-capacity-max=4000
    resources:
     requests:
      cpu:"2"
      memory:4Gi
     limits:
      cpu:"4"
      memory:8Gi
    volumeMounts:
     -name:mysql-data
      mountPath:/var/lib/mysql
     -name:mysql-logs
      mountPath:/var/log/mysql
     -name:mysql-config
      mountPath:/etc/mysql/conf.d
    livenessProbe:
     exec:
      command:
       -mysqladmin
       -ping
       --h
       -localhost
     initialDelaySeconds:30
     periodSeconds:10
    readinessProbe:
     exec:
      command:
       -mysql
       --h
       -localhost
       --e
       -"SELECT 1"
     initialDelaySeconds:10
     periodSeconds:5
  volumes:
   -name:mysql-data
    persistentVolumeClaim:
     claimName:mysql-master-data
   -name:mysql-logs
    persistentVolumeClaim:
     claimName:mysql-master-logs
   -name:mysql-config
    configMap:
     name:mysql-config

注意:MySQL的Deployment用strategy: Recreate而不是RollingUpdate,因为RBD卷是RWO模式,不能同时被两个Pod挂载。RollingUpdate会先创建新Pod再删旧Pod,新Pod挂载会失败。

3.1.2 存储容量监控脚本

#!/bin/bash
# PVC使用率监控脚本
# 文件:/opt/scripts/pvc-usage-monitor.sh
# 通过kubelet的metrics获取PVC使用率

set-euo pipefail

ALERT_THRESHOLD=85
LOG_FILE="/var/log/pvc-monitor.log"

echo"[$(date)] Starting PVC usage check...">>"${LOG_FILE}"

# 获取所有节点的kubelet metrics中的volume信息
fornodein$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}');do
# 通过API proxy访问kubelet metrics
 kubectl get --raw"/api/v1/nodes/${node}/proxy/stats/summary"2>/dev/null | 
  jq -r'.pods[]? | select(.volume != null) | .podRef.namespace + "/" + .podRef.name + " " + (.volume[]? | select(.pvcRef != null) | .pvcRef.name + " " + (.usedBytes|tostring) + " " + (.capacityBytes|tostring))'2>/dev/null | 
 whilereadns_pod pvc used capacity;do
  if[ -n"${capacity}"] && ["${capacity}"!="0"];then
    usage_pct=$((used * 100 / capacity))
    used_gi=$((used / 1073741824))
    cap_gi=$((capacity / 1073741824))

   if["${usage_pct}"-ge"${ALERT_THRESHOLD}"];then
    echo"[ALERT]${ns_pod}PVC:${pvc}Usage:${usage_pct}% (${used_gi}Gi/${cap_gi}Gi)">>"${LOG_FILE}"
   fi
  fi
 done
done

echo"[$(date)] PVC usage check completed.">>"${LOG_FILE}"

3.2 实际应用案例

案例一:多Pod共享NFS存储实现文件上传

场景描述:Web应用有3个副本,用户上传的文件需要所有副本都能访问。用NFS的RWX模式实现共享存储。

实现代码

# 文件:file-upload-app.yaml
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:upload-files-pvc
namespace:production
spec:
accessModes:
 -ReadWriteMany
storageClassName:nfs-client
resources:
 requests:
  storage:100Gi
---
apiVersion:apps/v1
kind:Deployment
metadata:
name:file-upload-api
namespace:production
spec:
replicas:3
selector:
 matchLabels:
  app:file-upload-api
template:
 metadata:
  labels:
   app:file-upload-api
 spec:
  containers:
   -name:api
    image:file-upload-api:v1.2.0
    ports:
     -containerPort:8080
    volumeMounts:
     -name:uploads
      mountPath:/app/uploads
    resources:
     requests:
      cpu:200m
      memory:256Mi
  volumes:
   -name:uploads
    persistentVolumeClaim:
     claimName:upload-files-pvc

运行结果

# 3个Pod都挂载了同一个NFS卷
kubectlexecfile-upload-api-xxx -- ls /app/uploads
# 在任一Pod中上传的文件,其他Pod都能看到

案例二:PV数据迁移(从NFS迁移到Ceph RBD)

场景描述:业务初期用NFS存储MySQL数据,随着数据量增长NFS的IO性能成为瓶颈,需要迁移到Ceph RBD。

实现步骤

创建新的Ceph RBD PVC

启动数据迁移Job

切换应用到新PVC

验证后清理旧PVC

# 文件:data-migration-job.yaml
apiVersion:batch/v1
kind:Job
metadata:
name:pvc-data-migration
namespace:database
spec:
template:
 spec:
  containers:
   -name:migrator
    image:ubuntu:22.04
    command:
     -/bin/bash
     --c
     -|
       apt-get update && apt-get install -y rsync
       echo "Starting data migration..."
       rsync -avz --progress /source/ /destination/
       echo "Migration completed. Verifying..."
       diff -r /source/ /destination/ && echo "Verification passed" || echo "Verification FAILED"
    volumeMounts:
     -name:source-vol
      mountPath:/source
      readOnly:true
     -name:dest-vol
      mountPath:/destination
    resources:
     requests:
      cpu:"1"
      memory:1Gi
  volumes:
   -name:source-vol
    persistentVolumeClaim:
     claimName:mysql-data-nfs
   -name:dest-vol
    persistentVolumeClaim:
     claimName:mysql-data-ceph
  restartPolicy:Never
backoffLimit:3
# 1. 停止MySQL写入(设为只读)
kubectlexec-it mysql-master-xxx -n database -- mysql -e"SET GLOBAL read_only=1;"

# 2. 创建目标PVC
kubectl apply -f mysql-data-ceph-pvc.yaml

# 3. 执行迁移Job
kubectl apply -f data-migration-job.yaml
kubectl logs -f job/pvc-data-migration -n database

# 4. 修改MySQL Deployment引用新PVC
kubectl patch deployment mysql-master -n database --type='json'
 -p='[{"op":"replace","path":"/spec/template/spec/volumes/0/persistentVolumeClaim/claimName","value":"mysql-data-ceph"}]'

# 5. 验证MySQL正常启动
kubectl get pods -n database -l app=mysql,role=master
kubectlexec-it mysql-master-xxx -n database -- mysql -e"SHOW DATABASES;"

# 6. 取消只读
kubectlexec-it mysql-master-xxx -n database -- mysql -e"SET GLOBAL read_only=0;"

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 性能优化

NFS挂载参数调优:默认NFS挂载参数在高并发写入时性能差,生产环境必须调整。hard模式保证数据一致性,nfsvers=4.1支持并行IO,timeo=600避免网络抖动导致超时。

# StorageClass中设置mountOptions
mountOptions:
-hard
-nfsvers=4.1
-timeo=600
-retrans=3
-rsize=1048576
-wsize=1048576

实测调整rsize/wsize从默认的32KB到1MB后,大文件顺序写入吞吐量从80MB/s提升到350MB/s。

Ceph RBD性能调优:RBD镜像默认特性包含exclusive-lock、object-map、fast-diff,生产环境建议全部开启。Ceph OSD的bluestore_cache_size默认1GB,SSD节点建议调到4GB。

# 查看RBD镜像特性
rbd info k8s-rbd/csi-vol-xxx

# Ceph OSD调优(在Ceph节点执行)
ceph configsetosd bluestore_cache_size_ssd 4294967296
ceph configsetosd osd_op_num_threads_per_shard_ssd 2

Local PV使用XFS文件系统:相比ext4,XFS在大文件和高并发场景下性能更好,MySQL的InnoDB引擎在XFS上的随机写性能提升约15%。

mkfs.xfs -f /dev/sdb
mount -o noatime,nodiratime /dev/sdb /data/local-volumes/ssd01

4.1.2 安全加固

PV访问权限控制:通过SecurityContext设置Pod的fsGroup,确保挂载的卷只有指定用户组能访问。

spec:
securityContext:
 runAsUser:1000
 runAsGroup:1000
 fsGroup:1000
containers:
 -name:app
  volumeMounts:
   -name:data
    mountPath:/data

加密存储:Ceph RBD支持LUKS加密,数据在磁盘上是加密的,即使磁盘被盗也无法读取数据。

# StorageClass中启用加密
parameters:
encrypted:"true"
encryptionKMSID:vault-kms

限制PVC大小:通过LimitRange限制namespace中PVC的最大容量,防止用户申请过大的存储。

apiVersion:v1
kind:LimitRange
metadata:
name:storage-limits
namespace:production
spec:
limits:
 -type:PersistentVolumeClaim
  max:
   storage:500Gi
  min:
   storage:1Gi

4.1.3 高可用配置

HA方案一:Ceph RBD三副本存储,任一OSD节点故障数据不丢失,RBD卷自动恢复

HA方案二:NFS服务器用DRBD + Pacemaker做主备高可用,VIP自动漂移

备份策略:Ceph RBD使用快照功能定期备份,NFS使用rsync同步到备份服务器。VolumeSnapshot是K8s原生的快照机制:

apiVersion:snapshot.storage.k8s.io/v1
kind:VolumeSnapshot
metadata:
name:mysql-data-snapshot
namespace:database
spec:
volumeSnapshotClassName:ceph-rbd-snapclass
source:
 persistentVolumeClaimName:mysql-master-data

4.2 注意事项

4.2.1 配置注意事项

警告:存储配置错误可能导致数据丢失,修改前务必备份数据。

注意reclaimPolicy: Delete会在PVC删除时同时删除PV和后端存储数据。生产环境必须用Retain,宁可手动清理也不要自动删除。

注意Local PV的volumeBindingMode必须设为WaitForFirstConsumer,否则PVC可能绑定到一个节点的PV上,但Pod被调度到另一个节点,导致挂载失败。

注意NFS的no_root_squash选项允许客户端以root身份操作文件,安全风险高。生产环境用root_squash,通过initContainer以root身份设置目录权限后,主容器以非root用户运行。

4.2.2 常见错误

错误现象 原因分析 解决方案
PVC一直Pending 没有匹配的PV或StorageClass的Provisioner未运行 kubectl describe pvc 查看事件;检查Provisioner Pod状态
Pod挂载卷失败,报Multi-Attach error RWO卷被多个节点的Pod同时挂载 确认旧Pod已完全终止;检查是否有残留的VolumeAttachment
NFS挂载超时 NFS服务器不可达或防火墙阻断 检查NFS服务器状态;确认2049端口可访问;Worker节点安装nfs-common
Ceph RBD挂载报rbd: map failed Ceph集群不健康或认证失败 检查Ceph集群状态ceph -s;验证Secret中的key是否正确
PVC扩容后容量没变 文件系统未扩展,需要Pod重启 删除Pod触发重建,kubelet会自动扩展文件系统
StatefulSet缩容后PVC残留 设计如此,PVC不随Pod删除 手动删除不需要的PVC:kubectl delete pvc

4.2.3 兼容性问题

版本兼容:CSI驱动版本需要和K8s版本匹配,ceph-csi 3.9.x支持K8s 1.24-1.28

平台兼容:NFS在所有Linux发行版上都支持;Ceph RBD需要内核4.x+;CephFS需要内核4.17+

组件依赖:VolumeSnapshot需要安装snapshot-controller和对应的CSI驱动支持

五、故障排查和监控

5.1 故障排查

5.1.1 日志查看

# 查看CSI驱动日志
kubectl logs -n ceph-csi -l app=ceph-csi-rbd-nodeplugin --tail=50
kubectl logs -n ceph-csi -l app=ceph-csi-rbd-provisioner --tail=50

# 查看NFS Provisioner日志
kubectl logs -n kube-system -l app=nfs-subdir-external-provisioner --tail=50

# 查看kubelet的卷挂载日志
journalctl -u kubelet | grep -i"volume|mount|pvc"| tail -30

# 查看PVC事件
kubectl describe pvc  -n 

# 查看VolumeAttachment
kubectl get volumeattachment

5.1.2 常见问题排查

问题一:PVC Pending,事件显示no persistent volumes available

# 诊断命令
kubectl describe pvc  -n 
kubectl get pv --show-labels
kubectl get sc

解决方案

静态供给:检查是否有可用的PV,PV的容量、accessModes、storageClassName是否和PVC匹配

动态供给:检查StorageClass是否存在,Provisioner Pod是否Running

检查PV的状态是否为Available(已绑定的PV不能再绑定其他PVC)

问题二:Pod卡在ContainerCreating,报FailedMount

# 诊断命令
kubectl describe pod  -n  | grep -A 10"Events"
kubectl get volumeattachment | grep 

# 检查节点上的挂载情况
kubectl debug node/ -it --image=ubuntu:22.04 -- 
 mount | grep 

解决方案

NFS挂载失败:确认Worker节点安装了nfs-common,NFS服务器可达

RBD挂载失败:检查Ceph集群健康状态,确认Secret配置正确

Multi-Attach错误:等待旧Pod完全终止,或手动删除残留的VolumeAttachment

问题三:存储性能差,IO延迟高

症状:应用响应慢,数据库查询超时

排查

# 在Pod内测试IO性能
kubectlexec-it  -- bash
# 安装fio
apt-get update && apt-get install -y fio

# 随机读写测试
fio --name=randwrite --ioengine=libaio --iodepth=32 --rw=randwrite 
 --bs=4k --direct=1 --size=1G --numjobs=4 --runtime=60 
 --filename=/data/fio-test --group_reporting

# 顺序写测试
fio --name=seqwrite --ioengine=libaio --iodepth=32 --rw=write 
 --bs=1M --direct=1 --size=1G --numjobs=1 --runtime=60 
 --filename=/data/fio-test --group_reporting

解决

NFS性能差:调整rsize/wsize到1MB,升级到NFSv4.1

Ceph RBD性能差:检查OSD磁盘是否为SSD,调整bluestore_cache_size

考虑迁移到Local PV获取本地磁盘性能

5.1.3 调试模式

# 查看CSI驱动详细日志
kubectl logs -n ceph-csi  -c csi-rbdplugin --tail=200

# 手动测试Ceph连接
kubectl run ceph-test --image=quay.io/ceph/ceph:v18 --rm -it -- bash
ceph -s --conf /etc/ceph/ceph.conf

# 查看节点上的块设备
kubectl debug node/ -it --image=ubuntu:22.04 -- lsblk

# 查看挂载点
kubectl debug node/ -it --image=ubuntu:22.04 -- df -h

5.2 性能监控

5.2.1 关键指标监控

# PVC使用率(需要kubelet metrics)
kubectl get --raw"/api/v1/nodes//proxy/stats/summary"| 
 jq'.pods[].volume[]? | select(.pvcRef != null) | {pvc: .pvcRef.name, used: .usedBytes, capacity: .capacityBytes}'

# Ceph集群状态
ceph -s
ceph osd pool stats k8s-rbd

# NFS服务器IO
nfsstat -s
iostat -x 1 5

5.2.2 监控指标说明

指标名称 正常范围 告警阈值 说明
PVC使用率 <70% >85% 超过85%需要扩容或清理数据
Ceph集群健康状态 HEALTH_OK HEALTH_WARN WARN需要排查,ERR需要立即处理
Ceph OSD延迟 <10ms >50ms OSD延迟高影响所有RBD卷性能
NFS服务器IOPS 按硬件 接近硬件上限 IOPS饱和需要扩容NFS或迁移到分布式存储
卷挂载失败次数 0 >0 任何挂载失败都需要排查
PV Available数量 >5 <2 静态供给时可用PV不足需要提前创建

5.2.3 Prometheus监控规则

# 文件:storage-alerts.yaml
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:storage-alerts
namespace:monitoring
spec:
groups:
 -name:kubernetes-storage
  rules:
   -alert:PVCUsageHigh
    expr:|
      kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.85
    for:10m
    labels:
     severity:warning
    annotations:
     summary:"PVC{{ $labels.persistentvolumeclaim }}usage exceeds 85%"
     description:"Namespace:{{ $labels.namespace }}"

   -alert:PVCUsageCritical
    expr:|
      kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.95
    for:5m
    labels:
     severity:critical
    annotations:
     summary:"PVC{{ $labels.persistentvolumeclaim }}usage exceeds 95%"

   -alert:PVCPending
    expr:|
      kube_persistentvolumeclaim_status_phase{phase="Pending"} == 1
    for:15m
    labels:
     severity:warning
    annotations:
     summary:"PVC{{ $labels.persistentvolumeclaim }}has been Pending for 15 minutes"

   -alert:PVAvailableLow
    expr:|
      count(kube_persistentvolume_status_phase{phase="Available"}) < 2
          for:10m
          labels:
            severity:warning
          annotations:
            summary:"Less than 2 PVs available in the cluster"

        -alert:VolumeAttachmentFailed
          expr:|
            increase(storage_operation_errors_total[5m]) > 0
    for:5m
    labels:
     severity:warning
    annotations:
     summary:"Volume operation errors detected"

5.3 备份与恢复

5.3.1 备份策略

#!/bin/bash
# Ceph RBD快照备份脚本
# 文件:/opt/scripts/rbd-snapshot-backup.sh

set-euo pipefail

POOL="k8s-rbd"
BACKUP_PREFIX="scheduled-backup"
KEEP_SNAPSHOTS=24

# 获取pool中所有RBD镜像
forimagein$(rbd ls${POOL});do
 SNAP_NAME="${BACKUP_PREFIX}-$(date +%Y%m%d-%H%M%S)"

# 创建快照
 rbd snap create"${POOL}/${image}@${SNAP_NAME}"
echo"[$(date)] Created snapshot:${POOL}/${image}@${SNAP_NAME}"

# 清理过期快照(保留最近24个)
 rbd snap ls"${POOL}/${image}"| grep"${BACKUP_PREFIX}"| 
  head -n -${KEEP_SNAPSHOTS}| awk'{print $2}'| 
 whilereadold_snap;do
   rbd snap rm"${POOL}/${image}@${old_snap}"
  echo"[$(date)] Removed old snapshot:${POOL}/${image}@${old_snap}"
 done
done

5.3.2 恢复流程

停止服务:缩容使用该PVC的Deployment/StatefulSet到0副本

恢复数据:从快照恢复RBD镜像或从备份rsync数据

验证完整性:挂载卷检查数据完整性

重启服务:恢复Deployment/StatefulSet副本数

六、总结

6.1 技术要点回顾

要点一:生产环境reclaimPolicy必须设为Retain,Delete策略会在PVC删除时直接删除后端存储数据

要点二:Local PV的volumeBindingMode必须用WaitForFirstConsumer,避免PV和Pod调度到不同节点

要点三:NFS适合共享文件存储(RWX),Ceph RBD适合数据库块存储(RWO),根据业务需求选型

要点四:StorageClass + Provisioner实现动态供给,消除手动创建PV的运维负担

要点五:PVC只能扩容不能缩容,容量规划时预留30%余量,配合监控在85%使用率时告警

6.2 进阶学习方向

CSI驱动开发:理解CSI接口规范,为自研存储系统开发K8s CSI驱动

学习资源:CSI规范

实践建议:从ceph-csi源码入手,理解Provisioner和NodePlugin的工作流程

Rook-Ceph Operator:用Rook在K8s中自动化部署和管理Ceph集群,降低Ceph运维复杂度

学习资源:Rook官方文档

实践建议:测试环境用Rook部署3节点Ceph集群,体验自动化运维

VolumeSnapshot和数据保护:基于CSI的快照机制实现应用一致性备份

6.3 参考资料

Kubernetes存储官方文档- PV/PVC/SC完整说明

Ceph CSI项目- Ceph CSI驱动

NFS Provisioner- NFS动态供给

Longhorn项目- 轻量级分布式存储

附录

A. 命令速查表

# PV操作
kubectl get pv                  # 查看所有PV
kubectl get pv -o wide              # 查看PV详情(含StorageClass)
kubectl describe pv           # PV详细信息
kubectl delete pv            # 删除PV(Retain策略下数据不丢)

# PVC操作
kubectl get pvc -A                # 查看所有namespace的PVC
kubectl get pvc -n           # 查看指定namespace的PVC
kubectl describe pvc  -n   # PVC详细信息和事件
kubectl patch pvc  -p'{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'# 扩容

# StorageClass操作
kubectl get sc                  # 查看StorageClass列表
kubectl describe sc           # SC详细信息
kubectl patch sc  -p'{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'# 设为默认SC

# 排查命令
kubectl get volumeattachment           # 查看卷挂载关系
kubectl get events -A --field-selector reason=FailedMount # 挂载失败事件

B. 配置参数详解

StorageClass关键参数

参数 说明 可选值
provisioner 存储供给器 rbd.csi.ceph.com 、nfs.csi.k8s.io等
reclaimPolicy 回收策略 Retain (保留)、Delete(删除)
volumeBindingMode 绑定模式 Immediate (立即)、WaitForFirstConsumer(延迟)
allowVolumeExpansion 允许扩容 true /false
mountOptions 挂载选项 依存储类型而定

PV访问模式

模式 缩写 说明 支持的存储
ReadWriteOnce RWO 单节点读写 所有存储类型
ReadOnlyMany ROX 多节点只读 NFS、CephFS、云盘
ReadWriteMany RWX 多节点读写 NFS、CephFS、GlusterFS
ReadWriteOncePod RWOP 单Pod读写(1.27 GA) CSI驱动支持

C. 术语表

术语 英文 解释
PV PersistentVolume 集群级别的存储资源,由管理员创建或动态供给
PVC PersistentVolumeClaim 用户对存储的申请,绑定到PV后供Pod使用
SC StorageClass 存储类,定义动态供给的参数和策略
CSI Container Storage Interface 容器存储接口标准,存储厂商实现CSI驱动接入K8s
RBD RADOS Block Device Ceph的块存储设备,提供高性能块级IO
Provisioner 供给器 负责根据PVC自动创建PV的控制器
VolumeSnapshot 卷快照 基于CSI的存储快照机制,用于数据备份
fsGroup 文件系统组 Pod SecurityContext中设置,控制挂载卷的文件组权限

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储管理
    +关注

    关注

    0

    文章

    33

    浏览量

    9624
  • kubernetes
    +关注

    关注

    0

    文章

    273

    浏览量

    9530

原文标题:从 Pod 重建不丢数据开始:Kubernetes PV/PVC/StorageClass 落地实践

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    KubePi:开源Kubernetes可视化管理面板,让集群管理如此简单

    的开源Kubernetes可视化管理面板,正以其简洁的设计与强大的功能,让集群管理变得前所未有的简单。 一、KubePi是什么?为什么你需要它? 1.1 诞生背景:简化
    发表于 02-11 12:53

    Kubernetes Ingress 高可靠部署最佳实践

    摘要: 在Kubernetes集群中,Ingress作为集群流量接入层,Ingress的高可靠性显得尤为重要,今天我们主要探讨如何部署一套高性能高可靠的Ingress接入层。简介
    发表于 04-17 14:35

    不吹不黑,今天我们来聊一聊 Kubernetes 落地的三种方式

    Kubernetes 作为自己的基础设施重心,"一万个人眼中就有一万个哈姆雷特",虽说 Kubernetes 是容器管理领域的事实标准,但实际上在不同背景的企业中,Kubernetes
    发表于 10-12 16:07

    Kubernetes Dashboard实践学习

    关于Kubernetes Dashboard的实践学习
    发表于 04-10 14:09

    Kubernetes上运行Kubernetes

    拍案叫绝的容器管理平台却迟迟未出现。 这样的局面一直维持到2014年,谷歌将 Kubernetes 项目发布到开放源代码社区之前。 Kubernetes 一开源,企业或者开发人员就可以在 Ku
    发表于 09-30 13:33 0次下载
    在<b class='flag-5'>Kubernetes</b>上运行<b class='flag-5'>Kubernetes</b>

    Kubernetes API详解

    摘要:Kubernetes是Google开源的容器集群管理系统。它构建Ddocker技术之上,为容器化的应用提供资源调度、部署运行、服务发现、扩容缩容等整一套功能,本文节选自龚正
    发表于 10-12 16:19 0次下载
    <b class='flag-5'>Kubernetes</b> API详解

    阿里巴巴 Kubernetes 应用管理实践中的经验与教训

    自带的复杂性足以让一批开发者望而却步。本文中,阿里巴巴技术专家孙健波在接受采访时基于阿里巴巴 Kubernetes 应用管理实践过程提供了一些经验与建议,以期对开发者有所帮助。在互联网时代,开发者更多
    发表于 11-14 23:06 490次阅读

    Kubernetes是什么,一文了解Kubernetes

    香了。 这时候就需要我们的主角 Kubernetes 上场了,先来了解一下 Kubernetes 的基本概念,后面再介绍实践,由浅入深步步为营。 关于 Kubernetes 的基本概念
    发表于 12-21 13:40 2111次阅读
    <b class='flag-5'>Kubernetes</b>是什么,一文了解<b class='flag-5'>Kubernetes</b>

    Kubernetes上Java应用的最佳实践

    在本文中,您将了解在 Kubernetes 上运行 Java 应用程序的最佳实践。大多数这些建议也适用于其他语言。但是,我正在考虑 Java 特性范围内的所有规则,并且还展示了可用于基于 JVM
    的头像 发表于 03-14 17:47 1367次阅读

    Awesome 工具如何更好地管理Kubernetes

    应用程序,那么 Kubernetes 是必备工具之一。有数百种工具可与 Kubernetes 配合使用以添加更多功能。我说的是用于更好地管理,安全性,仪表板和
    的头像 发表于 06-25 16:12 1500次阅读
    Awesome 工具如何更好地<b class='flag-5'>管理</b><b class='flag-5'>Kubernetes</b>

    戴尔科技再次荣获Kubernetes数据存储领导者

    近日,国际权威研究机构GigaOm公布了《2023企业级Kubernetes存储雷达报告》戴尔科技集团连续两年被评为Kubernetes数据存储领导者。
    的头像 发表于 10-26 10:16 1444次阅读
    戴尔科技再次荣获<b class='flag-5'>Kubernetes</b>数据<b class='flag-5'>存储</b>领导者

    龙智出席2024零跑智能汽车技术论坛,分享功能安全、需求管理、版本管理、代码扫描等DevSecOps落地实践

    快讯!日前,龙智出席零跑汽车技术论坛,分享龙智DevSecOps解决方案在功能安全、精细化需求管理、流程自动化、版本控制和代码质量分析等方面的落地实践
    的头像 发表于 12-27 16:06 1955次阅读
    龙智出席2024零跑智能汽车技术论坛,分享<b class='flag-5'>功能</b>安全、需求<b class='flag-5'>管理</b>、版本<b class='flag-5'>管理</b>、代码扫描等DevSecOps<b class='flag-5'>落地</b><b class='flag-5'>实践</b>

    Kubernetes Pod常用管理命令详解

    Kubernetes Pod常用管理命令详解
    的头像 发表于 02-17 14:06 1628次阅读
    <b class='flag-5'>Kubernetes</b> Pod常用<b class='flag-5'>管理</b>命令详解

    Kubernetes管理工具Helm的安装和使用

    Helm 可以帮助我们管理 Kubernetes 应用程序 - Helm Charts 可以定义、安装和升级复杂的 Kubernetes 应用程序,Charts 包很容易创建、版本管理
    的头像 发表于 03-13 16:06 2347次阅读

    生产环境中Kubernetes容器安全的最佳实践

    随着容器化技术的快速发展,Kubernetes已成为企业级容器编排的首选平台。然而,在享受Kubernetes带来的便利性和可扩展性的同时,安全问题也日益凸显。本文将从运维工程师的角度,深入探讨生产环境中Kubernetes容器
    的头像 发表于 07-14 11:09 924次阅读