在k8s中为什么要做持久化存储?
在k8s中部署的应用都是以pod容器的形式运行的,假如我们部署MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。
1、k8s持久化存储:emptyDir
查看k8s支持哪些存储
bash
[[root@k8s-master01](mailto:root@k8s-master01) ~]# kubectl explain pods.spec.volumes
FIELDS:
awsElasticBlockStore <Object>
azureDisk <Object>
azureFile <Object>
cephfs <Object>
cinder <Object>
configMap<Object>
csi <Object>
downwardAPI <Object>
emptyDir <Object>
ephemeral <Object>
fc <Object>
flexVolume <Object>
flocker <Object>
gcePersistentDisk <Object>
gitRepo <Object>
glusterfs<Object>
hostPath <Object>
iscsi <Object>
name <string> -required-
nfs <Object>
persistentVolumeClaim<Object>
photonPersistentDisk <Object>
portworxVolume <Object>
projected <Object>
quobyte <Object>
rbd <Object>
scaleIO <Object>
secret <Object>
storageos <Object>
vsphereVolume <Object>
bash
emptyDirhostPathnfspersistentVolumeClaimglusterfscephfsconfigMapsecret
我们想要使用存储卷,需要经历如下步骤
1、定义pod的volume,这个volume指明它要关联到哪个存储上的
2、在容器中要使用volumemounts挂载对应的存储
经过以上两步才能正确的使用存储卷
emptyDir类型的Volume是在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。**
创建一个pod,挂载临时目录emptyDir
Emptydir的官方网址:
https://kubernetes.io/docs/concepts/storage/volumes#emptydir
bash
[[root@k8s-master01](mailto:root@k8s-master01) ~]# cat emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-empty
spec:
containers:
- name: container-empty
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /cache
name: cache-volume ##与volumes中的name保持一致
volumes:
- emptyDir: {}
name: cache-volume
更新资源清单文件
bash
[[root@k8s-master01](mailto:root@k8s-master01) ~]# kubectl apply -f emptydir.yaml
pod/pod-empty created
查看本机临时目录存在的位置,可用如下方法:
查看pod调度到哪个节点
root@k8s-master01 \~\]# kubectl get pod pod-empty -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-empty 1/1 Running 0 27s 172.16.69.230 k8s-worker02 \
\
查看pod的uid
root@k8s-master01 \~\]# kubectl get pod pod-empty -o yaml \| grep uid uid: 45a10614-495b-4745-be0f-c7492b90e2b7
登录到k8s-worker02上
root@k8s-worker02 \~\]# tree /var/lib/kubelet/pods/45a10614-495b-4745-be0f-c7492b90e2b7/ /var/lib/kubelet/pods/45a10614-495b-4745-be0f-c7492b90e2b7/ ├── containers │ └── container-empty │ └── 43f2b9b9 ├── etc-hosts ├── plugins │ └── kubernetes.io\~empty-dir │ ├── cache-volume │ │ └── ready │ └── wrapped_kube-api-access-njjrv │ └── ready └── volumes ├── kubernetes.io\~empty-dir │ └── cache-volume └── kubernetes.io\~projected └── kube-api-access-njjrv ├── ca.crt -\> ..data/ca.crt ├── namespace -\> ..data/namespace └── token -\> ..data/token 11 directories, 7 files
测试
###模拟产生数据测试
root@pod-empty:/cache# touch file{1..10}
root@pod-empty:/cache# ls
10}file{1. aaa file1 file10 file2 file3 file4 file5 file6 file7 file8 file9
root@pod-empty:/cache# exit
exit
###node查看
root@k8s-worker02 \~\]# tree /var/lib/kubelet/pods/45a10614-495b-4745-be0f-c7492b90e2b7/ /var/lib/kubelet/pods/45a10614-495b-4745-be0f-c7492b90e2b7/ ├── containers │ └── container-empty │ └── 43f2b9b9 ├── etc-hosts ├── plugins │ └── kubernetes.io\~empty-dir │ ├── cache-volume │ │ └── ready │ └── wrapped_kube-api-access-njjrv │ └── ready └── volumes ├── kubernetes.io\~empty-dir │ └── cache-volume │ ├── 10}file{1. │ ├── aaa │ ├── file1 │ ├── file10 │ ├── file2 │ ├── file3 │ ├── file4 │ ├── file5 │ ├── file6 │ ├── file7 │ ├── file8 │ └── file9 └── kubernetes.io\~projected └── kube-api-access-njjrv ├── ca.crt -\> ..data/ca.crt ├── namespace -\> ..data/namespace └── token -\> ..data/token 12 directories, 18 files ###模拟删除pod测试 \[root@k8s-master01 \~\]# kubectl delete pod pod-empty pod "pod-empty" deleted \[root@k8s-worker02 \~\]# tree /var/lib/kubelet/pods/45a10614-495b-4745-be0f-c7492b90e2b7/ /var/lib/kubelet/pods/45a10614-495b-4745-be0f-c7492b90e2b7/ \[error opening dir
0 directories, 0 files
2、k8s持久化存储:hostPath
hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。
查看hostPath存储卷的用法
\[root@k8s-master01\](mailto:root@k8s-master01) \~\]# kubectl explain pods.spec.volumes.hostPath KIND: Pod VERSION: v1 RESOURCE: hostPath \
创建一个pod,挂载hostPath存储卷
bash
[root@k8s-master01~]# cat hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
containers:
- image: nginx
name: test-nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data
type: DirectoryOrCreate
注意:
DirectoryOrCreate表示本地有/data目录,就用本地的,本地没有就会在pod调度到的节点自动创建一个
更新资源清单文件,并查看pod调度到了哪个物理节点
bash
[root@k8s-master01 ~]# kubectl apply -f test-hostpath.yaml
pod/test-hostpath created
[root@k8s-master01 ~]# kubectl get pod test-hostpath -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-hostpath 1/1 Running 0 80s 172.16.69.248 k8s-worker02 <none> <none>
测试
bash
###没有首页
[root@k8s-master01 ~]# curl 172.16.69.248
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
###生成首页
[root@k8s-worker02 ~]# cd /data/
[root@k8s-worker02 data]# ls
[root@k8s-worker02 data]# echo 1111 > index.html
[root@k8s-master01 ~]# curl 172.16.69.248
1111
hostpath存储卷缺点
单节点,pod删除之后重新创建必须调度到同一个node节点,数据才不会丢失
如何调度到同一个nodeName呢 ? 需要我们再yaml文件中进行指定就可以
啦
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
nodeName: k8s-worker02
containers:
- image: nginx
name: test-nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data
type: DirectoryOrCreate
测试
root@k8s-master01 \~\]# kubectl apply -f test-hostpath.yaml pod/test-hostpath configured \[root@k8s-master01 \~\]# kubectl delete pod test-hostpath pod "test-hostpath" deleted \[root@k8s-master01 \~\]# kubectl apply -f test-hostpath.yaml pod/test-hostpath created \[root@k8s-master01 \~\]# kubectl get pod test-hostpath -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-hostpath 1/1 Running 0 9s 172.16.69.219 k8s-worker02 \
\ \[root@k8s-master01 \~\]# curl 172.16.69.219 1111
bash
## 3、k8s持久化存储:nfs
上节说的hostPath存储,存在单点故障,pod挂载hostPath时,只有调度到同一个节点,数据才不会丢失。那可以使用nfs作为持久化存储。
**搭建nfs服务**
以k8s的控制节点作为NFS服务端
```shell
[root@k8s-master03 ~]# yum install -y nfs-utils
```
在宿主机创建NFS需要的共享目录
```shell
[root@k8s-master03 ~]# mkdir /data/volumes -pv
mkdir: 已创建目录 "/data"
mkdir: 已创建目录 "/data/volumes"
```
配置nfs共享服务器上的/data/volumes目录
```shell
[root@k8s-master01] ~]# systemctl enable --now nfs
[root@k8s-master01] ~]# vim /etc/exports
/data/volumes 192.168.115.0/24(rw,no_root_squash)
```
#使NFS配置生效
```shell
[root@k8s-master03 ~]# exportfs -avr
exporting 192.168.115.0/24:/data/volumes
```
**所有的worker节点安装nfs-utils**
yum install nfs-utils -y
systemctl enable --now nfs
#在k8s-worker01和k8s-worker02上手动挂载试试:
root@k8s-worker01 \~\]# mount 192.168.115.163:/data/volumes /mnt \[root@k8s-worker01 \~\]# df -Th \| grep nfs 192.168.115.163:/data/volumes nfs4 116G 6.7G 109G 6% /mnt #nfs可以被正常挂载 #手动卸载: \[root@k8s-worker01 \~\]# umount /mnt
创建Pod,挂载NFS共享出来的目录
Pod挂载nfs的官方地址:https://kubernetes.io/zh/docs/concepts/storage/volumes/
root@k8s-master01 \~\]# cat nfs.yaml apiVersion: v1 kind: Pod metadata: name: test-nfs spec: containers: - name: test-nfs image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 protocol: TCP volumeMounts: - name: nfs-volumes mountPath: /usr/share/nginx/html volumes: - name: nfs-volumes nfs: path: /data/volumes #共享目录 server: 192.168.115.163 ##nfs服务器地址 更新资源清单文件 \`\`\`shell \[root@k8s-master01 \~\]# kubectl apply -f test-nfs.yaml pod/test-nfs created \`\`\` 查看pod是否创建成功 \`\`\`shell \[root@k8s-master01 \~\]# kubectl get pods -o wide \| grep nfs test-nfs 1/1 Running 0 55s 172.16.79.68 k8s-worker01 \
\ \`\`\` \*\*测试\*\* \[root@k8s-master01 \~\]# curl 172.16.79.68 \ \\ 403 Forbidden\ \ \ \\ \403 Forbidden\
\
\nginx/1.25.3\ \ \
PVC 基本概念与工作原理
PersistentVolumeClaim (PVC) 是用户对存储资源的抽象请求,通过声明式配置定义所需的存储大小、访问模式等属性。PVC 与 PersistentVolume (PV) 绑定后,Pod 可通过 PVC 挂载存储资源,实现数据持久化。
PVC 核心特性
动态供给
通过 StorageClass 动态创建 PV,无需管理员手动预配置 PV。例如定义以下 StorageClass:
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://glusterfs-cluster.example.com"
访问模式
支持三种模式:
ReadWriteOnce(RWO):单节点读写ReadOnlyMany(ROX):多节点只读ReadWriteMany(RWX):多节点读写
生命周期阶段
Available:未绑定的空闲 PVBound:已与 PVC 绑定Released:PVC 删除但 PV 未回收Failed:自动回收失败
PVC 使用示例
创建 PVC
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: fast
Pod 挂载 PVC
yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: storage
volumes:
- name: storage
persistentVolumeClaim:
claimName: mypvc
PVC 与分布式存储集成
GlusterFS 示例
- 部署 GlusterFS 集群并创建 Volume
- 定义 StorageClass 使用 GlusterFS 插件
- PVC 申请时自动创建 GlusterFS 后端 PV
CephFS 示例
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cephfs
provisioner: ceph.com/cephfs
parameters:
monitors: "10.16.154.78:6789"
adminId: admin
adminSecretName: ceph-secret
数据高可用建议
- 分布式存储系统(如 Ceph/GlusterFS)提供副本机制
- 定期备份 PV 数据至对象存储(如 S3/MinIO)
- 监控 PV/PVC 状态并设置告警规则
通过 PVC 抽象存储细节,结合分布式存储后端,可实现跨节点数据共享与高可用性。
PV和PVC工作原理补充说明
PV(PersistentVolume)是集群中的存储资源,PVC(PersistentVolumeClaim)是用户对存储资源的请求。生命周期分为供应、绑定、使用和回收四个阶段。
静态供应 集群管理员手动创建PV,明确指定存储容量、访问模式等细节。这些PV会持久存在于Kubernetes API中,供PVC匹配使用。
动态供应 当没有匹配的静态PV时,集群会根据PVC中指定的StorageClass自动创建PV。需要预先配置好StorageClass和相关存储插件。
PVC绑定细节
PVC通过以下字段匹配PV:
- 存储容量需求(resources.requests.storage)
- 访问模式(ReadWriteOnce/ReadOnlyMany/ReadWriteMany)
- StorageClass名称(若指定)
- 标签选择器(selector)
绑定成功后,PV的状态变为Bound,且被独占锁定。若没有可用PV,PVC会保持Pending状态。
使用PVC的典型流程
创建NFS共享目录(假设NFS服务器已就绪):
shell
mkdir -p /nfs/data/pv1
chmod 777 /nfs/data/pv1
echo "/nfs/data/pv1 *(rw,no_root_squash)" >> /etc/exports
exportfs -a
定义PV示例(static-pv.yaml):
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /nfs/data/pv1
server: nfs-server-ip
定义PVC示例(pvc-claim.yaml):
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
回收策略对比
Retain策略
- PVC删除后PV状态变为Released
- 需要手动清理PV(删除或重新定义)才能再次使用
- 数据永久保留,适用于关键数据场景
Delete策略
- 自动删除PV及后端存储数据
- 适用于临时数据或可丢失数据
- 依赖StorageClass的配置支持
Recycle策略(已弃用)
- 基本数据擦除(如rm -rf /volume/*)
- 现推荐使用动态供应配合Delete策略
在Pod中使用PVC
示例Pod定义(pod-with-pvc.yaml):
yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nfs-vol
mountPath: /usr/share/nginx/html
volumes:
- name: nfs-vol
persistentVolumeClaim:
claimName: nfs-pvc
验证步骤:
- 应用PV/PVC定义:
kubectl apply -f static-pv.yaml,pvc-claim.yaml - 检查绑定状态:
kubectl get pv,pvc - 创建Pod:
kubectl apply -f pod-with-pvc.yaml - 验证数据持久性:删除Pod后重新创建,检查数据是否保留
动态供应配置要点
- 创建StorageClass(nfs-sc.yaml):
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-sc
provisioner: example.com/nfs
parameters:
archiveOnDelete: "false"
- PVC中指定StorageClass:
yaml
spec:
storageClassName: nfs-sc
accessModes: [...]
注意:动态供应需要对应的provisioner组件,不同存储系统(如AWS EBS、Ceph RBD等)需要安装各自的插件。
#在宿主机创建NFS需要的共享目录
root@k8s-master01\] \~\]# mkdir /data/volume_test/v{1,2,3,4,5,6,7,8,9,10} -p #配置nfs共享宿主机上的/data/volume_test/v1..v10目录 \[root@k8s-master01 \~\]# cat /etc/exports /data/volumes 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v1 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v2 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v3 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v4 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v5 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v6 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v7 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v8 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v9 192.168.115.0/24(rw,no_root_squash) /data/volume_test/v10 192.168.115.0/24(rw,no_root_squash) #重新加载配置,使配置成效 \[root@k8s-master01 \~\]# exportfs -arv
#查看定义pv需要的字段
root@k8s-master01 \~\]# kubectl explain pv KIND: PersistentVolume VERSION: v1 DESCRIPTION: PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: \[https://kubernetes.io/docs/concepts/storage/persistent-volumes\](https://kubernetes.io/docs/concepts/storage/persistent-volumes) FIELDS: apiVersion \
s kind\ metadata\
root@k8s-master01 \~\]# cat pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: v1 spec: capacity: storage: 1Gi #pv的存储空间容量 accessModes: \["ReadWriteOnce"
nfs:
path: /data/volume_test/v1 #把nfs的存储空间创建成pv
server: 192.168.115.163 #nfs服务器的地址
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
spec:
capacity:
storage: 2Gi
accessModes: ["ReadWriteMany"]
nfs:
path: /data/volume_test/v2
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v3
spec:
capacity:
storage: 3Gi
accessModes: ["ReadOnlyMany"]
nfs:
path: /data/volume_test/v3
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v4
spec:
capacity:
storage: 4Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v4
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v5
spec:
capacity:
storage: 5Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v5
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v6
spec:
capacity:
storage: 6Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v6
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v7
spec:
capacity:
storage: 7Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v7
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v8
spec:
capacity:
storage: 8Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v8
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v9
spec:
capacity:
storage: 9Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v9
server: 192.168.115.163
apiVersion: v1
kind: PersistentVolume
metadata:
name: v10
spec:
capacity:
storage: 10Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v10
server: 192.168.115.163
更新资源清单文件
root@k8s-master01 \~\]# kubectl apply -f pv.yaml persistentvolume/v1 created persistentvolume/v2 created persistentvolume/v3 created persistentvolume/v4 created persistentvolume/v5 created persistentvolume/v6 created persistentvolume/v7 created persistentvolume/v8 created persistentvolume/v9 created persistentvolume/v10 created 查看pv资源 \[root@k8s-master01 \~\]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE v1 1Gi RWO Retain Available 17m v10 10Gi RWO,RWX Retain Available 17m v2 2Gi RWX Retain Bound default/my-pvc 17m v3 3Gi ROX Retain Available 17m v4 4Gi RWO,RWX Retain Bound default/my-pvc1 17m v5 5Gi RWO,RWX Retain Available 17m v6 6Gi RWO,RWX Retain Available 17m v7 7Gi RWO,RWX Retain Available 17m v8 8Gi RWO,RWX Retain Available 17m v9 9Gi RWO,RWX Retain Available 17m \[root@k8s-master01 \~\]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE my-pvc Bound v2 2Gi RWX 12m my-pvc1 Bound v4 4Gi RWO,RWX 4m11s
root@hd1 volume\]# cat pod_pvc.yaml apiVersion: v1 kind: Pod metadata: name: pod-pvc spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent volumeMounts: - name: nginx-html mountPath: /usr/share/nginx/html volumes: - name: nginx-html persistentVolumeClaim: claimName: my-pvc
更新资源清单文件
更新资源清单文件
root@k8s-master01 \~\]# kubectl apply -f pod-pvc.yaml pod/pod-pvc created shell 查看pod状态 \[root@k8s-master01 \~\]# kubectl get pod -o wide \| grep pvc pod-pvc 1/1 Running 0 16s 172.16.79.127 k8s-worker01 \
\ \\ 403 Forbidden\ \ \ \\ \403 Forbidden\
\
\nginx/1.25.3\ \ \