介绍
容器中的文件在磁盘上是临时存放的,当容器崩溃或停止时容器上面的数据未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题,跨所有容器设置和访问共享文件系统具有一定的挑战性。K8S 卷(Volume) 这一抽象概念能够解决这两个问题。
存储卷的分类
hostPath 存储
警告:
HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用 HostPath 卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。HostPath 仅适用于单个节点上的存储,不支持跨节点访问(如果Pod偏移到其他宿主机节点上面可能会出问题)。
支持的 type 值如下:
type值 | 说明 |
---|---|
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查 | |
DirectoryOrCreate | 如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet相同的组和属主信息 |
Directory | 在给定路径上必须存在的目录 |
FileOrCreate | 如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权 |
File | 在给定路径上必须存在的文件 |
Socket | 在给定路径上必须存在的 UNIX 套接字 |
CharDevice | 在给定路径上必须存在的字符设备 |
BlockDevice | 在给定路径上必须存在的块设备 |
hostPath 配置示例
如有多个节点,在使用 hostPath 存储需要指定pod部署在那个节点上面,例如在Pod上面添加nodeSelector字段指定到某一个节点(需要提前给节点打标签)
yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
# nodeSelector: # 指定部署到特定节点上面
# disktype: ssd # 标签
- image: nginx
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data # 宿主机上目录位置,注意权限
type: Directory # 此字段为可选,详细解释在上面
emptyDir
定义了 emptyDir 卷的 Pod,在 Pod 被指派到某节点时此卷会被创建。emptyDir 卷最初是空的。Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。主要作用是为了共享数据用。
emptyDir 卷存储可以使用任何磁盘、SSD 或网络存储,这取决于你的环境。 你可以将 emptyDir.medium 字段设置为 "Memory", 以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。虽然 tmpfs 速度非常快,但是要注意它与磁盘不同, 并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束
说明:
容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。
emptyDir 配置示例
yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir:
sizeLimit: 500Mi # 写入磁盘的大小限制,如果没限制直接删除这行 在emptyDir: []即可
nfs
nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
配置示例
说明:
在使用 NFS 卷之前,你必须部署自己的 NFS 服务器才可以使用,这里不再叙述。
yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /my-nfs-data
name: test-volume
volumes:
- name: test-volume
nfs:
server: my-nfs-server.example.com # nfs服务的地址
path: /my-nfs-volume # nfs 服务共享的路径
readOnly: true
PV和PVC的使用
在 Kubernetes 中,PV(PersistentVolume)和 PVC(PersistentVolumeClaim)是用于持久化存储的核心概念,其本身并没有存储的相关功能所以需要准备后端存储的环境。
-
PersistentVolume(PV):PV 是集群中的一块持久化存储,它是集群管理员预先配置好的存储资源。PV 可以是网络存储(如 NFS、GlusterFS、Ceph)、云存储(如 AWS EBS、Azure Disk)、本地存储(HostPath)等。PV 与存储后端进行绑定,表示集群中的可用存储资源,支持的后端存储类型详情请查看K8S官方文档
-
PersistentVolumeClaim(PVC):PVC 是 Pod 对 PV 的申请。PVC 定义了对存储资源的需求,包括存储容量、访问模式和其他属性。Pod 中的容器可以通过 PVC 来申请并使用 PV 提供的持久化存储。Kubernetes 会根据 PVC 的需求匹配可用的 PV,并将其动态绑定到 Pod 中。
PV(PersistentVolume)
下面是 hostPath PersistentVolume 的配置文件:
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
labels:
type: local
name: task-pv-volume
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
hostPath:
path: /mnt
type: ""
persistentVolumeReclaimPolicy: Recycle
storageClassName: manual
volumeMode: Filesystem
关键词解释
- spec: capacity: storage: 10Gi:定义了 PV 的存储容量为 10Gi。
- volumeMode: Filesystem:指定了 PV 的卷模式为文件系统。
- accessModes: - ReadWriteOnce:指定了 PV 的访问模式为 ReadWriteOnce,表示此 PV 只能被单个节点挂载为读写模式。
- persistentVolumeReclaimPolicy: Recycle:指定了 PV 回收策略为 Recycle,表示当 PV 被释放时,其存储资源将被重新使用。
- storageClassName: manual:指定了 PV 的存储类名称为 slow。
- hostPath.path:指定了 PV 的路径。在这里,path 指定了 PV 使用的主机路径,即 "/mnt/data"。
- hostPath.type:指定 PV 所使用的主机路径的类型,不指定默认是:DirectoryOrCreate 还有 Directory 和 File 类型可选
pv存储卷的回收策略
- 回收(Reclaiming) :当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。
- 保留(Retain):回收策略 Retain 使得用户可以手动回收资源。
- 删除(Delete):对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。
- 回收(Recycle):如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。(注意:回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。)
卷模式
Kubernetes 支持两种卷模式(volumeModes):
- Filesystem(文件系统):文件系统模式,表示 PV 中存储的数据是文件系统数据,可以像普通文件系统一样被挂载到 Pod 中,并且可以在容器中被读取和写入。默认的卷模式是 Filesystem。
- Block():块模式,表示 PV 中存储的数据是块设备数据,可以将 PV 挂载为块设备卷(例如 /dev/sdX),并且可以被容器用作块设备。
访问模式
- ReadWriteOnce:卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式仍然可以在同一节点上运行的多个 Pod 访问该卷。
- ReadOnlyMany:卷可以被多个节点以只读方式挂载。
- ReadWriteMany:卷可以被多个节点以读写方式挂载。
- ReadWriteOncePod:(目前K8S版本在1.22及以上的才支持到1.29才GA)卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。
创建PV
bash
kubectl create -f pv0003.yaml
创建好以后查看
bash
kubectl get pv
yaml
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pv0003 5Gi RWO Recycle Available manual 4s
PV状态说明:
- NAME:PVC名字
- CAPACITY:PV大小
- ACCESSMODES:访问模式
- RECLAIMPOLICY:回收策略
- STATUS:PV使用状态
- STORAGECLASS: 动态存储名字
- AGE:创建时间
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
- RWOP - ReadWriteOncePod
PV每个阶段的状态
每个持久卷会处于以下阶段(Phase)之一:
- Available:卷是一个空闲资源,尚未绑定到任何申领
- Bound:该卷已经绑定到某申领
- Released:所绑定的申领已被删除,但是关联存储资源尚未被集群回收
- Failed:卷的自动回收操作失败
使用 kubectl describe persistentvolume 查看已绑定到 PV 的 PVC 的名称。
PVC(PersistentVolumeClaims)
创建一个PVC:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
这里面的字段意思基本和PV里面的一样,访问模式和卷模式也是一样的,就不再解释了。
PV和PVC的绑定模式
匹配步骤:
- 检查 PV 的状态:Kubernetes 控制器会查找状态为 Available(可用)的 PV,这些 PV 尚未被其他 PVC 绑定。
- PV 属性的匹配:控制器会检查 PVC 的需求与 PV 的属性是否匹配。这包括容量、访问模式、持久性和标签等。
- 针对 PVC 的需求选择 PV:控制器会选择满足 PVC 所有需求的 PV,并尝试将其与 PVC 绑定。
自动绑定
创建PV和PVC
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task1
labels:
type: local1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 9Gi
查看PV和PVC状态
yaml
[root@master01 pv]# kubectl get pvc,pv
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/task-pv-claim Bound task1 10Gi RWO 26s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/task1 10Gi RWO Retain Bound default/task-pv-claim 10m
都是已经绑状态
根据标签绑定
创建PV和PVC
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
labels:
desk: "test1" # 设置标签
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
selector:
matchLabels:
desk: "test1" # 匹配PV的标签
查看PV和PVC状态
可以看到 my-pvc 已经绑定到 my-pv 上面了,如果没有标签自动绑定的话 则会绑定到 task2 的PV上面
yaml
[root@master01 pv]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-pv 10Gi RWO Retain Bound default/my-pvc 3s
task1 10Gi RWO Retain Available 46s
task2 5Gi RWO Retain Available 46s
[root@master01 pv]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pvc Bound my-pv 10Gi RWO 12s
指定PV名字绑定
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
volumeName: my-pv # 指定要绑定的 PV 的名称
resources:
requests:
storage: 5Gi
查看PV和PVC绑定情况
yaml
[root@master01 pv]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/my-pv 20Gi RWO Retain Bound default/my-pvc 5s
persistentvolume/task1 10Gi RWO Retain Available 158m
persistentvolume/task2 5Gi RWO Retain Available 158m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/my-pvc Pending my-pv 0 5s
注意事项
PV 和 PVC 之间的绑定是一对一的关系。一个 PVC 只能绑定到一个 PV 上,而一个 PV 可以同时被多个 PVC 绑定。如果没有足够的 PV 来满足 PVC 的需求,或者没有满足 PVC 的要求的可用 PV,则 PVC 将处于 Pending(挂起)状态,直到满足条件为止。
Pod使用PVC存储
创建PV和PVC:
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
创建一个Pod使用上面这个PVC
yaml
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
volumes:
- name: task-pv-storage
persistentVolumeClaim: # 指定使用PVC存储
claimName: task-pv-claim # PVC存储的名字
在Pod里面多次挂在PVC
yaml
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: test
image: nginx
volumeMounts:
# 网站数据挂载
- name: config
mountPath: /usr/share/nginx/html
subPath: html
# Nginx 配置挂载
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: config
persistentVolumeClaim:
claimName: test-nfs-claim
PVC绑定常见问题
创建PVC后,一直绑定不上PV(Pending):
- PVC的空间申请大小大于PV的大小
- PVC的StorageClassName没有和PV的一致
- PVC的accessModes和PV的不一致
Pod挂在PVC后,一直处于Pending状态:
- PVC没有被创建成功,或者被创建
- PVC和Pod不在同一个Namespace(PV是没有命名空间的,PVC是有有命名空间概念的)
删除PVC后k8s会创建一个用于回收的Pod,根据PV的回收策略进行pv的回收,回收完以后PV的状态就会变成可被绑定的状态也就是空闲状态,其他的Pending状态的PVC如果匹配到了这个PV,他就能和这个PV进行绑定。
动态存储(StorageClass)
主要功能:
- 动态配置持久存储:StorageClass 允许管理员定义多种类型的存储,包括云存储、本地存储、网络存储等,并且可以根据用户的请求动态创建 PersistentVolume(PV)。
- 自动绑定:一旦 PVC 请求了特定的存储类,Kubernetes 将根据 StorageClass 的定义自动创建 PV,并将其绑定到 PVC 上,简化了存储资源的管理过程。
- 多种属性支持:StorageClass 允许管理员定义存储的各种属性,如容量、访问模式、持久性、副本数等,以满足不同应用程序的需求。
- 灵活性和可扩展性:管理员可以根据实际需求定义多个不同的 StorageClass,并根据应用程序的需求选择合适的存储类。
环境准备
在开始之前请安装后端存储,我这里使用的是CEPH存储并且安装到K8S集群里,下面是安装方法。(所有环境中如果有用到的安装包或者容器镜像什么的都可以私信我)
安装方法:点击跳转
查看已经部署好的StorageClass
注意: 本次演示默认已经安装好ceph存储。
bash
## sc 是 StorageClass 简写
[root@master01 ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
rook-ceph-rbd rook-ceph.rbd.csi.ceph.com Retain Immediate true 228d
rook-cephfs rook-ceph.cephfs.csi.ceph.com Retain Immediate true 228d
- rook-ceph-rbd :这个是块存储。区别:只能由一个节点进行读写操作
- rook-cephfs:这个是共享文件存储。区别:可以在多个节点上同时挂载和访问。
适用场景:
- RBD:适用于需要高性能、低延迟和可扩展性的场景,如数据库、块存储卷等。
- CephFS:适用于需要共享数据、访问文件系统的场景,如共享存储、容器卷等。
查看 rook-ceph-rbd
查看命令
bash
kubectl get sc rook-ceph-rbd -oyaml
yaml
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-rbd
parameters:
clusterID: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/fstype: ext4
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
imageFeatures: layering
imageFormat: "2"
pool: replicapool
provisioner: rook-ceph.rbd.csi.ceph.com
reclaimPolicy: Retain
volumeBindingMode: Immediate
关键字段解释
- kind:表示这是 Kubernetes 存储 API 的版本,而 kind 设置为 StorageClass,表示这是一个 StorageClass 对象。
- allowVolumeExpansion: true :这个字段用于指示是否允许对使用这个 StorageClass 创建的持久卷进行扩展。
- metadata:这个部分包含有关 StorageClass 的元数据,其中最重要的是 name 字段,它指定了 StorageClass 的名称,这里是 "rook-ceph-rbd"。
- parameters:这个字段定义了传递给 StorageClass 的参数。在这个例子中,这些参数包括:
-
- clusterID:Rook Ceph 集群的 ID。
-
- csi.storage.k8s.io/controller-expand-secret-name : 的名称和命名空间。
- imageFeatures:这个参数指定了卷的特性,即它包含了支持镜像层叠(layering)的功能。镜像层叠允许在卷上创建多个镜像,每个镜像可以包含自己的修改,而不会影响其他镜像。
- imageFormat:这个参数指定了卷的格式,即它使用的是格式版本 2。格式版本 2 是 RBD 卷的一种格式,它支持更高级的功能和性能,例如支持更大的卷和更好的快照管理。
- pool:指定了用于存储的池名称。
- provisioner:这个字段指定了用于创建持久卷的存储提供程序。在这里,使用的提供程序是 rook-ceph.rbd.csi.ceph.com,表明这个 StorageClass 使用了 Rook Ceph 的 RBD(Rados Block Device)CSI(Container Storage Interface)提供程序。
- reclaimPolicy:这个字段指定了持久卷的回收策略。在这里,设置为 Retain,表示当与此 StorageClass 关联的持久卷不再需要时,它们的资源应该保留而不被自动删除。
- volumeBindingMode:这个字段指定了持久卷的绑定模式。在这里,设置为 Immediate,表示持久卷应该立即绑定到声明,即创建声明时应立即创建卷。
StorageClass 使用方法
创建 StatefulSet 使用
部署成功以后 StorageClass 会根据PVC申请的去自动创建PV并绑定好
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-statefulset
spec:
serviceName: "my-statefulset"
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-container
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: my-persistent-storage
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: my-persistent-storage
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "rook-ceph-rbd" # 指定刚刚查出来的 StorageClass 名字
resources:
requests:
storage: 5Gi
使用PVC申请动态存储
创建PVC成功以后会自动创建PV并绑定好,后面Pod使用方式和上面手动创建PV和PVC的方式一样。
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: rook-ceph-rbd # 指定要使用的 StorageClass 的名称
resources:
requests:
storage: 1Gi # 请求 1GB 的存储容量
PVC扩容
直接修改 "storage: 1Gi " 修改为2G即可,但是生效还需要等几分钟。
bash
kubectl edit pvc my-pvc