K8S持久化存储
- Kubernetes
- 2024-08-27
- 195热度
- 0评论
K8S持久化存储
Volumes
Container(容器)中的磁盘文件是短暂的,当容器崩溃时,kubelet 会重新启动容器,但最初的文件将丢失,Container 会以最干净的状态启动。另外,当一个 Pod 运行多个 Container 时,各个容器可能需要共享一些文件。Kubernetes Volume 可以解决这两个问题。一些需要持久化数据的程序才会用到 Volumes,或者一些需要共享数据的容器需要 volumes。如Ceph、日志收集等...
Docker 也有卷的概念,但是在 Docker 中卷只是磁盘上或另一个 Container 中的目录,其生命周期不受管理。虽然目前 Docker 已经提供了卷驱动程序,但是功能非常有限,例如从 Docker1.7 版本开始,每个 Container 只允许一个卷驱动程序,并且无法将参数传递给卷。另一方面,Kubernetes 卷具有明确的生命周期,与使用它的 Pod 相同。因此,在 Kubernetes中的卷可以比 Pod 中运行的任何 Container 都长,并且可以在 Container 重启或者销毁之后保留数据。Kubernetes 支持多种类型的卷,Pod 可以同时使用任意数量的卷。从本质上讲,卷只是一个目录,可能包含一些数据,Pod 中的容器可以访问它。要使用卷 Pod需要通过.spec.volumes 字段指定为 Pod 提供的卷,以及使用.spec.containers.volumeMounts 字段指定卷挂载的目录。从容器中的进程可以看到由 Docker 镜像和卷组成的文件系统视图,卷无法挂载其他卷或具有到其他卷的硬链接,Pod 中的每个 Container 必须独立指定每个卷的挂载位置。
emptyDir
emptyDir是 Kubernetes 中的一种卷类型,它在 Pod 被分配到的节点上创建一个空目录,并为该 Pod 中的容器共享使用。emptyDir 卷的生命周期与 Pod 相同,当 Pod 被删除时,emptyDir卷中的数据也会被删除。
使用场景
- 临时数据存储:emptyDir适用于存放在 Pod 生命周期内需要的临时数据,如缓存、临时文件等。
- 跨容器共享:如果同一个 Pod 中的多个容器需要共享一些数据,可以使用 emptyDir卷。
emptyDir 卷的特性
- 本地存储:emptyDir卷存储在节点的本地文件系统上,因此它的性能与节点的存储性能相关。
- 生命周期管理:当 Pod 被删除、重新调度到另一个节点或者由于节点故障被移除时,emptyDir卷中的数据会被删除。
创建emptyDir卷的示例如下:
[root@master-01 ~]# vim /k8spod/persistent_storage/emptyDir.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
volumeMounts:
- mountPath: /cache
name: empty-share
- name: nginx2
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
volumeMounts:
- mountPath: /cache2
name: empty-share
command:
- sh
- -c
- sleep 3600
volumes:
- name: empty-share
emptyDir: {}
创建资源,并查看资源状态
[root@master-01 ~]# kubectl create -f /k8spod/persistent_storage/emptyDir.yaml
deployment.apps/nginx-deployment created
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 217 (50m ago) 63d
nginx-deployment-99db6776f-hg526 2/2 Running 0 6s
进入nginx容器,查看卷挂载情况,并在enptyDir卷挂载的目录下写入数据,切换到同样挂载的另一个container内,可以观察到数据已经被写入,且数据共享
[root@master-01 ~]# kubectl exec -it nginx-deployment-99db6776f-hg526 -c nginx -- bash
root@nginx-deployment-99db6776f-hg526:/# df -Th | grep /cache
/dev/mapper/centos-root xfs 39G 6.5G 32G 17% /cache
root@nginx-deployment-99db6776f-hg526:/# touch /cache/1.txt
root@nginx-deployment-99db6776f-hg526:/# echo "This is Test Data For Nginx2" > /cache/1.txt
root@nginx-deployment-99db6776f-hg526:/# exit
exit
[root@master-01 ~]# kubectl exec -it nginx-deployment-99db6776f-hg526 -c nginx2 -- bash
root@nginx-deployment-99db6776f-hg526:/# df -Th /cache2/
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/centos-root xfs 39G 6.5G 32G 17% /cache2
root@nginx-deployment-99db6776f-hg526:/# cat /cache2/1.txt
This is Test Data For Nginx2
root@nginx-deployment-99db6776f-hg526:/# exit
删除容器,等待再次重建,观察到之前创建的数据已经丢失
[root@master-01 ~]# kubectl delete pod nginx-deployment-99db6776f-hg526
pod "nginx-deployment-99db6776f-hg526" deleted
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 217 (55m ago) 63d
nginx-deployment-99db6776f-s26r8 2/2 Running 0 42s
[root@master-01 ~]# kubectl exec nginx-deployment-99db6776f-s26r8 -c nginx -- ls /cache
[root@master-01 ~]#
HostPath
hostPath是 Kubernetes 中的一种卷类型,它允许将宿主机的文件系统中的某个目录或文件挂载到 Pod 中的容器里。与emptyDir不同,hostPath 卷在节点的本地文件系统上直接绑定到一个指定路径,因此即使 Pod 被删除,数据也不会被删除。
使用场景
- 访问节点上的特定文件或目录:当你需要访问节点上的一些特定文件或目录时,例如日志文件、设备文件等。
- 节点间数据共享:在某些情况下,可能希望多个 Pod 在同一个节点上共享数据。
- 与宿主环境交互:例如,使用 hostPath 卷来访问节点上的 Docker 套接字文件,以便在 Pod 内部运行 Docker 命令。
由于 hostPath 直接访问节点的文件系统,使用不当可能导致安全问题。需要小心选择挂载的路径,并确保授予最小权限。
hostPath可使用类型如下表所示
类型 | 含义 |
---|---|
空字符串 | 默认选项,意味着挂载 hostPath 卷之前不会执行任何检查 |
DirectoryOrCreate | 如果给定的 path 不存在任何东西,那么将根据需要创建一个权限为0755 的空目录,和 Kubelet 具有相同的组和权限。 |
Directory | 目录必须存在于给定的路径下 |
FileOrCreate | 如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为 0644,和 Kubelet 具有相同的组和所有权 |
File | 文件,必须存在于给定路径中 |
Socket | UNIX 套接字,必须存在于给定路径中 |
CharDevice | 字符设备,必须存在于给定路径中 |
BlockDevice | 块设备,必须存在于给定路径中 |
创建hostPath的示例如下:
[root@master-01 ~]# vim /k8spod/persistent_storage/hostPath.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
volumeMounts:
- mountPath: /etc/hosts
name: host-share
volumes:
- name: host-share
hostPath:
path: /changehosts.txt
type: FileOrCreate
创建资源并查看资源状态(注:查看资源请加上-owide参数,有些读者反映查看文件或目录是否可以持久化时,发现宿主机内并没有创建对应的文件或目录,这是因为读者习惯了在master01节点内查看,但是容器所在的宿主机其实并不在master01,如本例所示,容器其实在node01节点,所以持久化的数据会保存在node01节点)
[root@master-01 ~]# kubectl create -f /k8spod/persistent_storage/hostPath.yaml
deployment.apps/nginx-deployment created
[root@master-01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cluster-test-665f554bcc-bcw5v 1/1 Running 219 (28m ago) 63d 172.16.184.124 master-01 <none> <none>
nginx-deployment-6dd9c5bccf-vq5hk 1/1 Running 0 8s 172.16.190.48 node-01 <none> <none>
登录到容器内,在挂载文件内写入内容
[root@master-01 ~]# kubectl exec -it nginx-deployment-6dd9c5bccf-vq5hk -- bash
root@nginx-deployment-6dd9c5bccf-vq5hk:/# echo "1.1.1.1 change-hosts" > /etc/hosts
root@nginx-deployment-6dd9c5bccf-vq5hk:/# exit
在master01节点删除容器
[root@master-01 ~]# kubectl delete -f /k8spod/persistent_storage/hostPath.yaml
deployment.apps "nginx-deployment" deleted
切换到node01节点,查看数据是否已经持久化
[root@node-01 ~]# cat /changehosts.txt
1.1.1.1 change-hosts
NFS
所有节点安装NFS服务
[root@master-01 ~]# yum -y install nfs-utils
在master01节点配置NFS服务,对192.168.132.0/24网段授权
[root@master-01 nfs]# vim /etc/exports
/data/nfs/ 192.168.132.0/24(rw,sync,no_subtree_check,no_root_squash)
[root@master-01 ~]# exportfs -r
在node01节点尝试挂载,验证服务可用性,可以观察到成功挂载并成功写入
[root@node-01 ~]# mount -t nfs 192.168.132.169:/data/nfs /mnt
[root@node-01 ~]# cd /mnt/
[root@node-01 mnt]# ls
[root@node-01 mnt]# touch 1
配置容器挂载NFS服务
[root@master-01 ~]# vim /k8spod/persistent_storage/nfs.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
volumeMounts:
- mountPath: /data/write
name: nfs
volumes:
- name: nfs
nfs:
server: 192.168.132.169
path: /data/nfs/test
创建资源,并查看资源创建状态
[root@master-01 ~]# kubectl create -f /k8spod/persistent_storage/nfs.yaml
deployment.apps/nginx-deployment created
[root@master-01 nfs]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 220 (5m30s ago) 63d
nginx-deployment-699b74b545-6f4zn 1/1 Running 0 42s
登录容器,查看挂载情况,在挂载的目录内写入内容,随后删除容器
[root@master-01 ~]# kubectl exec -it nginx-deployment-699b74b545-6f4zn -- bash
droot@nginx-deployment-699b74b545-6f4zn:/# df -Th | grep data
192.168.132.169:/data/nfs/test nfs4 39G 6.5G 32G 17% /data/write
root@nginx-deployment-699b74b545-6f4zn:/# echo test > /data/write/write.txt
root@nginx-deployment-699b74b545-6f4zn:/# exit
[root@master-01 ~]# kubectl delete -f /k8spod/persistent_storage/nfs.yaml
deployment.apps "nginx-deployment" deleted
返回master01节点,查看数据是否完成持久化
[root@master-01 ~]# cat /data/nfs/test/write.txt
test
PV
PV概念
PersistentVolume(PV)是Kubernetes中的一个资源对象,用于提供持久化存储。它是集群存储的抽象,可以被多个Pod共享,并且可以跨节点迁移。PV的生命周期独立于Pod,即使Pod被删除,其使用的PV也可以被其他Pod使用。
PV的定义包括存储的大小、访问模式(如ReadWriteOnce、ReadWriteMany等)、存储类(StorageClass)等信息。存储类是一种机制,用于定义PV的创建和管理方式,例如动态供应、存储自动扩展等。
在Kubernetes中,用户不直接创建PV,而是请求一个PersistentVolumeClaim(PVC)。PVC定义了用户对存储的需求,包括所需的存储大小和访问模式。Kubernetes的调度器会根据PVC的要求和集群中可用的PV来调度Pod。如果集群中没有匹配的PV,PVC可以请求动态供应一个新的PV。
PV的使用提高了存储资源的灵活性和可重用性,允许存储解决方案与Kubernetes集群的其他部分解耦,从而简化了存储管理和维护。
PersistentVolume(PV)和Volume是Kubernetes中用于管理存储的两个不同概念。
-
PersistentVolume(PV 是集群中的一块存储,它的生命周期独立于任何单独的Pod。这意味着即使使用它的Pod不再存在,PV也会继续存在,直到被显式删除。PV可以绑定到多个Pod上,实现共享的持久化存储。PV通常由集群管理员预先定义和配置,而不是直接在Pod定义中创建。PV支持多种访问模式,如ReadWriteOnce、ReadOnlyMany和ReadWriteMany,并且可以通过动态供应器来自动创建和管理。
-
Volume 是Pod中的一个存储卷,它可以被Pod中的容器共享。Volume的生命周期与包含它的Pod相同,当Pod被删除时,Volume也会被自动删除。Volume可以支持多种存储后端,例如本地存储、网络文件系统、云存储等。在Pod的定义中直接声明并使用Volume,但它们不具备PV的持久性和独立于Pod的特性。
PV回收策略
PersistentVolume(PV)的回收策略定义了当PV被释放(与PersistentVolumeClaim(PVC)断开)后如何处理其中的数据。Kubernetes中PV的回收策略主要有以下三种:
- Retain:当设置为Retain策略时,PV在PVC被删除后不会被自动删除,而是进入Released状态。在这种状态下,PV中的数据仍然保留,直到管理员手动介入处理。这对于需要保留数据以备后续审计或分析的场景非常有用。管理员可以通过删除PV对象来释放存储资源,但这不会删除存储资产上的数据。如果需要重用存储资产,可以创建一个新的PV来绑定它。
- Delete:对于支持Delete回收策略的卷插件,当PVC被删除后,PV对象和关联的存储资产(如AWS EBS、GCE PD、Azure Disk或Cinder卷)会被自动删除。这种策略适用于那些不再需要的PV,或者PV中的数据可以重新生成的情况。
- Recycle(已废弃):在较旧的Kubernetes版本中,Recycle策略会在PV被释放后尝试清空卷中的数据,类似于执行
rm -rf /thevolume/*
命令,然后将PV状态变为Available,以便重新使用。然而,这种策略已经在Kubernetes v1.14版本中被废弃,不再推荐使用。 现在推荐使用的策略是动态制备。此策略会在用户创建PersistentVolumeClaim对象时自动制备存储。
在选择PV的回收策略时,需要根据应用的需求和数据的重要性来进行权衡。例如,如果PV中包含重要数据,可能需要选择Retain策略来避免数据丢失;如果PV中的数据可以重新生成,可以选择Delete策略来节省存储空间
PV访问策略
ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为RWO
ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为ROX
ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为RWX
ReadWriteOncePod :只允许被单个Pod访问,需要K8s 1.22+以上版本,并且是CSI创建的PV才可使用
在配置PV时,需要根据存储后端的能力和应用的需求来选择合适的访问模式。不是所有的存储系统都支持所有的访问模式,因此在设计存储解决方案时,这些限制需要被考虑进去
存储分类
文件存储:一些数据可能需要被多个节点使用,比如用户的头像、用户上传的文件等,实现方式:NFS、NAS、FTP、CephFS等
块存储:一些数据只能被一个节点使用,或者是需要将一块裸盘整个挂载使用,比如数据库、Redis等,实现方式:Ceph、GlusterFS、公有云
对象存储:由程序代码直接实现的一种存储方式,云原生应用无状态化常用的实现方式,实现方式:一般是符合S3协议的云存储,比如AWS的S3存储、Minio、七牛云等
参考NFS配置章节,配置NFS服务端,配置共享目录和权限
[root@master-01 ~]# vim /etc/exports
/data/nfs/ 192.168.132.0/24(rw,sync,no_subtree_check,no_root_squash)
尝试挂载,确保NFS共享可用,查看共享目录下的文件
[root@master-02 ~]# mount -t nfs 192.168.132.169:/data/nfs /mnt
[root@master-02 ~]# df -Th | grep 192
192.168.132.169:/data/nfs nfs4 39G 6.5G 32G 17% /mnt
[root@master-02 ~]# ls /data/nfs/
pvmount.test
创建PV、PVC、Pod声明PVC的配置如下:(以NFS配置为例,但生产环境不推荐使用NFS)
[root@master-01 ~]# vim /k8spod/persistent_storage/pv_nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: nfs-slow
nfs:
path: /data/nfs
server: 192.168.132.169
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: task-pvc-claim
spec:
storageClassName: nfs-slow
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: task-pvc-claim
containers:
- name: nginx
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage
创建资源,并查看资源状态
[root@master-01 ~]# kubectl create -f /k8spod/persistent_storage/pv_nfs.yaml
persistentvolume/pv-nfs created
persistentvolumeclaim/task-pvc-claim created
deployment.apps/nginx-deployment created
[root@master-01 ~]# kubectl get -f /k8spod/persistent_storage/pv_nfs.yaml
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pv-nfs 5Gi RWO Delete Bound default/task-pvc-claim nfs-slow <unset> 8s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/task-pvc-claim Bound pv-nfs 5Gi RWO nfs-slow <unset> 8s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 1/1 1 1 8s
读者在不熟悉的情况下,可以单独查看某个资源的状态
[root@master-01 ~]# kubectl get persistentvolume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pv-nfs 5Gi RWO Delete Bound default/task-pvc-claim nfs-slow <unset> 53s
[root@master-01 ~]# kubectl get persistentvolumeclaims
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
task-pvc-claim Bound pv-nfs 5Gi RWO nfs-slow <unset> 63s
PV状态 | 含义 |
---|---|
Available | 可用,没有被PVC绑定的空闲资源 |
Bound | 已绑定,已经被PVC绑定 |
Released | 已释放,PVC被删除,但是资源还未被重新使用 |
Failed | 失败,自动回收失败 |
查看容器挂载情况,可以观察到pvmount.test文件已经存在,可以对此文件进行读写,但是查看挂载情况,发现PV和PVC的容量限制对NFS无效
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 231 (14m ago) 75d
nginx-deployment-c67fdffb4-gsx62 1/1 Running 0 7m40s
[root@master-01 ~]# kubectl exec -it nginx-deployment-c67fdffb4-gsx62 -- /bin/bash
root@nginx-deployment-c67fdffb4-gsx62:/# ls /usr/share/nginx/html/
pvmount.test
root@nginx-deployment-c67fdffb4-gsx62:/# echo "nginx" > /usr/share/nginx/html/pvmount.test
root@nginx-deployment-c67fdffb4-gsx62:/# cat /usr/share/nginx/html/pvmount.test
nginx
root@nginx-deployment-c67fdffb4-gsx62:/# df -Th | grep 192
192.168.132.169:/data/nfs nfs4 39G 6.5G 32G 17% /usr/share/nginx/html
给出HostPath的PV、PVC配置文件,供读者尝试
[root@master-01 ~]# vim /k8spod/persistent_storage/pv_hp.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
name: hp-pv-volume
labels:
type: local
spec:
storageClassName: hostpath
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/hp"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: task-pvc-claim
spec:
storageClassName: hostpath
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: task-pvc-claim
containers:
- name: nginx
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage
资源创建情况和挂载结果,读者在实验时,可能会出现ls挂载目录但没有输出的情况,读者可以思考造成这个问题的原因。
[root@master-01 ~]# kubectl get -f /k8spod/persistent_storage/pv_hp.yaml
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/hp-pv-volume 10Gi RWO Retain Bound default/task-pvc-claim hostpath <unset> 4m56s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/task-pvc-claim Bound hp-pv-volume 10Gi RWO hostpath <unset> 4m56s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 1/1 1 1 4m56s
[root@master-01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cluster-test-665f554bcc-bcw5v 1/1 Running 232 (28m ago) 75d 172.16.184.126 master-01 <none> <none>
nginx-deployment-c67fdffb4-wt9fj 1/1 Running 0 2m33s 172.16.133.162 master-03 <none> <none>
[root@master-01 ~]# kubectl exec nginx-deployment-c67fdffb4-wt9fj -- ls /usr/share/nginx/html/
hostpath-in-master-03