3-9-3-PV、PVC、SC资源
1. 基础概念
volume的基础使用 , 需要我们用户手动来向不同类型存储接口传递不同的参数 , 从而实现把外部存储映射到k8s上的一个volume对象 , 使得pod才能正常的挂载对应的存储卷 , 对应pod里的容器才能正常使用 ; 这种使用方式的前提是用户必须了解对应的存储系统 , 了解对应类型的存储接口 , 以及相关参数 ; 这使得用户在k8s上使用存储卷变得有些复杂 ; 为了简化这一过程 , 在k8s上使用pv和pvc资源来把对应底层存储接口给隐藏了 , 用户使用存储卷不再关心底层存储系统接口 ; 不管底层是那种类型的存储 , 用户只需面对一个pvc接口即可 ;


用户在创建pod时使用存储卷只需要关心对应名称空间的pvc对象 ; 而对应pv是需要集群管理管理员定义 ; 后端存储是专门的存储管理员负责管理 ; pv是k8s上的一种标准资源 , 全称叫做PersistentVolume翻译成中文就是持久存储卷 ; 它主要作用是把后端存储中的某个逻辑单元 , 映射为k8s上的pv资源 ; pv是集群级别的资源 ; 任意名称空间都可以直接关联某一个pv ; 关联pv的过程我们叫做绑定pv ; 而对应名称空间关联某一pv需要使用pvc资源来定义 ; pvc全称PersistentVolumeClaim的缩写 , 意思就是持久存储卷申请 ; 在一个名称空间下创建一个pvc就是把对应名称空间同集群上的某一pv做绑定 ; 一旦一个名称空间绑定了一个pv后 , 对应的pv就会从available状态转变成bond状态 , 其他名称空间将不能再使用 , 只有对应pv是available状态才能正常的被其他名称空间关联绑定 ; 简单讲pvc和pv的关系是一一对应的 , 一个pv只能对应一个pvc ; 至于同一名称空间下的多个pod是否能够同时使用一个PVC取决pv是否允许多路读写 , 对应pv是否支持多路读写取决后端存储系统 ; 不同类型的存储系统 , 对应访问模式也有所不同。访问模式有三种 , 单路读写(ReadWriteOnce简称RWO) , 多路读写(ReadWriteMany简称RWX) , 多路只读(ReadOnlyMany简称ROX) ;
2. PV、PVC使用流程
2.1 PV资源创建
示例 : pv资源创建
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-v1
labels:
storsystem: nfs-v1
rel: stable
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce","ReadWriteMany","ReadOnlyMany"]
persistentVolumeReclaimPolicy: Retain
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /data/v1
server: 192.168.71.34pv是k8s的标准资源 , 其群组版本为v1,类型为PersistentVolume ;
spec.capacity.storage字段用来描述pv的存储容量 ;volumeMode用来描述对应存储系统提供的存储卷类型接口 , 一般存储卷类型接口有两种 , 分别是Filesystem文件系统接口和Block块设备接口 ;accessModes用来描述pv的访问模式 ;presistentVolumeReclaimPolicy字段用来描述存储卷回收策略 , 持久卷回收策略有三种 :一种是
Delete, 表示当pvc删除以后 , 对应pv也随之删除 ;第二种是
Recycle,表示当pvc删除以后 , 对应pv的数据也随之被删除 ;第三种是
Retain, 表示当pvc被删除以后 ,pv原封动 , 即pv也在 , 对应数据也在 ;
mountOptions字段用来指定挂载选项 ;nfs表示后端存储为nfs, 对于不同类型的存储 , 对应的要传递的参数各不相同 , 对于nfs这种类型的存储 , 我们只需要指定其nfs服务器地址以及对应共享出来的文件路径 ;
以上配置就表示把nfs上的/data/v1目录映射到k8s上的pv , 对应pv的名称为nfs-pv-v1 ; 这里需要注意一点 , 在创建pv时 , 对应后端存储应该提前准备好 ;
应用配置清单
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl apply -f pv-v1-demo.yaml
persistentvolume/nfs-pv-v1 created
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfs-pv-v1 1Gi RWO,ROX,RWX Retain Available <unset> 8s
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl describe pv nfs-pv-v1
Name: nfs-pv-v1
Labels: rel=stable
storsystem=nfs-v1
Annotations: <none>
Finalizers: [kubernetes.io/pv-protection]
StorageClass:
Status: Available
Claim:
Reclaim Policy: Retain
Access Modes: RWO,ROX,RWX
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.71.34
Path: /data/v1
ReadOnly: false
Events: <none>
在pv的详细信息中能够看到 , 当前pv的状态为available , pv对应后端的存储是nfs , 对应存储的ip地址为192.168.71.34 , 当前pv对应后端存储的逻辑单元就是/data/v1 ;
2.2 PVC资源创建
示例 : 创建pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs-pv-v1
namespace: default
labels:
storsystem: nfs-v1
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 500Mi
selector:
matchLabels:
storsystem: nfs-v1
rel: stablepvc也是k8s上的标准资源 , 对应的群组版本为v1,类型为PersistentVolumeClaim ;
spec.accessModes字段是用来指定其pvc的访问模式 , 一般这个模式是被pv的accessModes包含 , 也就说pvc的访问模式必须是pv的子集 , 即等于小于pv的访问模式 ;resources用来描述对应pvc的存储空间限制 ,requests用来描述对应pvc最小容量限制 ,limits用来描述最大容量限制 ;
selector用来定义标签选择器 , 主要作用过滤符合对应标签的pv;如果不定义标签选择器 , 它会在所有
available状态的pv中 , 通过其容量大小限制以及访问模式去匹配一个最佳的pv进行关联 ;
应用配置清单
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# cat pvc-v1-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs-pv-v1
namespace: default
labels:
storsystem: nfs-v1
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 500Mi
selector:
matchLabels:
storsystem: nfs-v1
rel: stable
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl apply -f pvc-v1-demo.yaml
persistentvolumeclaim/pvc-nfs-pv-v1 created
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-nfs-pv-v1 Bound nfs-pv-v1 1Gi RWO,ROX,RWX <unset> 24s
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl describe pvc pvc-nfs-pv-v1
Name: pvc-nfs-pv-v1
Namespace: default
StorageClass:
Status: Bound
Volume: nfs-pv-v1
Labels: storsystem=nfs-v1
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWO,ROX,RWX
VolumeMode: Filesystem
Used By: <none>
Events: <none>
这里显示pvc的大小是pvc最大容量显示 , 默认不限制最大容量就是其pv的最大容量 ; 从上面的显示可以看到对应pv被pvc绑定以后 , 其状态就变成了bound ;
2.3 pod关联pvc
示例: 创建pod关联pvc , 并在其pod容器里挂载pvc
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# cat redis-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-demo
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
volumeMounts:
- mountPath: /data
name: redis-data
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: pvc-nfs-pv-v1
在pod里关联pvc , 只需要指定后端存储类型为persistentVolumeClaim , 然后指定对应的pvc名称 ;
应用资源清单
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl apply -f redis-demo.yaml
pod/redis-demo created
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-demo 1/1 Running 0 98s
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl describe pod redis-demo
Name: redis-demo
Namespace: default
Priority: 0
Service Account: default
Node: k8s-node01.cbear.top/192.168.71.35
Start Time: Sun, 02 Jun 2024 11:36:52 +0000
Labels: app=redis
Annotations: <none>
Status: Running
IP: 172.20.3.24
IPs:
IP: 172.20.3.24
Containers:
redis:
Container ID: containerd://09296d1b325d3e44069f7e969e86a9cbc2a72854ba34a3a9b3e1dc64811d3ea0
Image: redis:alpine
Image ID: docker.io/library/redis@sha256:4bed291aa5efb9f0d77b76ff7d4ab71eee410962965d052552db1fb80576431d
Port: <none>
Host Port: <none>
State: Running
Started: Sun, 02 Jun 2024 11:37:23 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/data from redis-data (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qjvdg (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
redis-data:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: pvc-nfs-pv-v1
ReadOnly: false
kube-api-access-qjvdg:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m8s default-scheduler Successfully assigned default/redis-demo to k8s-node01.cbear.top
Normal Pulling 2m7s kubelet Pulling image "redis:alpine"
Normal Pulled 97s kubelet Successfully pulled image "redis:alpine" in 29.902s (29.902s including waiting)
Normal Created 97s kubelet Created container redis
Normal Started 97s kubelet Started container redis
可以看到对应pod已经正常运行起来 ; 从详细信息中可以看到对应pod使用的volumes类型为PersistentVolumeClaim , 对应名称为pvc-nfs-pv-v1 ; 对应容器以读写方式挂载了对应存储卷 ;
测试 : 在redis-demo上产生数据 , 看看是否能够正常保存到nfs服务器上
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-demo 1/1 Running 0 4m31s 172.20.3.24 k8s-node01.cbear.top <none> <none>
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-demo 1/1 Running 0 8m44s
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl exec -it redis-demo -- /bin/sh
/data # redis-cli
127.0.0.1:6379> set mykey "this is test key"
OK
127.0.0.1:6379> get mykey
"this is test key"
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> exit
/data # ls
dump.rdb
/data # exit
在nfs服务器上查看对应目录下是否有dump.rdb文件产生
root@harbor:~# ls /data/v1/
dump.rdb可以看到 , redis上产生的快照文件在nfs服务器上有对应的文件存在 ;
2.4 测试效果
测试 : 删除pod , 看看对应文件是否还在
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl delete -f redis-demo.yaml
pod "redis-demo" deleted
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pods
No resources found in default namespace.
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# ssh root@192.168.71.34
root@harbor:~# ll /data/v1/
total 12
drwxr-xr-x 2 lxd root 4096 Jun 2 12:00 ./
drwxr-xr-x 11 root root 4096 Jun 2 09:59 ../
-rw-r--r-- 1 lxd xiong 121 Jun 2 12:00 dump.rdb
root@harbor:~# exit
logout
Connection to 192.168.71.34 closed.可以看到删除了pod对应快照文件在nfs服务器还是存在 ;
绑定节点 , 重新新建pod , 看看对应是否能够自动应用快照中的数据
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# cat redis-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-demo
labels:
app: redis
spec:
nodeName: k8s-node02.cbear.top
containers:
- name: redis
image: redis:alpine
volumeMounts:
- mountPath: /data
name: redis-data
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: pvc-nfs-pv-v1
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl apply -f redis-demo.yaml
pod/redis-demo created
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-demo 1/1 Running 0 112s 172.20.4.15 k8s-node02.cbear.top <none> <none>
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl exec -it redis-demo -- /bin/sh
/data # redis-cli
127.0.0.1:6379> get mykey
"this is test key"
127.0.0.1:6379> exit
/data # ls
dump.rdb
/data # exit可以看到新建的pod能够正常读取到nfs上的快照文件并应用到内存中 ;
删除pvc , 看看对应pv是否被删除
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-nfs-pv-v1 Bound nfs-pv-v1 1Gi RWO,ROX,RWX <unset> 45m
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl delete pvc pvc-nfs-pv-v1
persistentvolumeclaim "pvc-nfs-pv-v1" deleted
^Croot@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-nfs-pv-v1 Terminating nfs-pv-v1 1Gi RWO,ROX,RWX <unset> 46m
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfs-pv-v1 1Gi RWO,ROX,RWX Retain Bound default/pvc-nfs-pv-v1 可以看到现在pvc的状态变成了terminating , 但对应pvc还在并没有被删除 ; 对应pv还处于绑定状态 ;
删除pod , 看看对应pvc是否会被删除
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-demo 1/1 Running 0 10m
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl delete pod redis-demo
pod "redis-demo" deleted
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pvc
No resources found in default namespace.
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfs-pv-v1 1Gi RWO,ROX,RWX Retain Released default/pvc-nfs-pv-v1 <unset> 123m可以看到删除对应pod以后 , pvc就立即删除了 ; 对应pvc被删除以后 , 对应pv的状态就从bound状态转变为Released状态 , 表示等待回收 ; 我们在资源清单中使用的是Retain回收策略 , pv和pvc都是我们人工手动回收 ;
删除pv,看看对应数据是否会被删除
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfs-pv-v1 1Gi RWO,ROX,RWX Retain Released default/pvc-nfs-pv-v1 <unset> 123m
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
nfs-pv-v1 1Gi RWO,ROX,RWX Retain Released default/pvc-nfs-pv-v1 <unset> 127m
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl delete pv nfs-pv-v1
persistentvolume "nfs-pv-v1" deleted
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# kubectl get pv
No resources found
root@k8s-master01:/opt/k8s-data/yaml/pv-pvc# ssh root@192.168.71.34
Last login: Sun Jun 2 12:01:02 2024 from 192.168.71.29
root@harbor:~# ll /data/v1/
total 12
drwxr-xr-x 2 lxd root 4096 Jun 2 12:14 ./
drwxr-xr-x 11 root root 4096 Jun 2 09:59 ../
-rw-r--r-- 1 lxd xiong 121 Jun 2 12:14 dump.rdb可以看到删除了pv,对应快照文件并没有清除 ;
3. SC使用
SC是StorageClass的缩写,表示存储类 ; 这种资源主要用来对pv资源的自动供给提供接口 ; 所谓自动供给是指用户无需手动创建pv,而是在创建pvc时对应pv会由persistentVolume-controller自动创建并完成pv和pvc的绑定 ; 使用sc资源的前提是对应后端存储必须支持restfull类型接口的管理接口,并且pvc必须指定对应存储类名称来引用SC ; 简单讲SC资源就是用来为后端存储提供自动创建pv并关联对应pvc的接口 ; 如下图

使用sc动态创建pv,对应pvc必须也是属于对应的sc ; 上图主要描述了用户在创建pvc时,引用对应的sc以后,对应sc会调用底层存储系统的管理接口,创建对应的pv并关联至对应pvc ;
示例 : 创建sc资源
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://127.0.0.1:8081"
clusterid: "630372ccdc720a92c681fb928f27b53f"
restauthenabled: "true"
restuser: "admin"
secretNamespace: "default"
secretName: "heketi-secret"
gidMin: "40000"
gidMax: "50000"
volumetype: "replicate:3"上述是官方文档中的一个示例,在创建sc资源时,对应群组是storage.k8s.io/v1,类型为StorageClass;provisioner字段用于描述对应供给接口名称;parameters用来定义向对应存储管理接口要传递的参数;
在pvc资源中引用SC资源对象
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: foo-pvc
namespace: foo
spec:
storageClassName: "slow"
volumeName: foo-pv
...在创建pvc时用storageClassName字段来指定对应的SC名称即可
参考链接
容器编排系统K8s之PV、PVC、SC资源 - Linux-1874 - 博客园 (cnblogs.com)