k8s基础篇-持久化存储入门
该示例是使用 StorageClass 链接 GlusterFS 的一个示例,PV 和 Volume 可以直接配置 GlusterFS,其配置参数和 StorageClass 的 parameters 类似,只不过 PV 是单独供 PVC 使用的,Volume 是针对某个 Pod 使用的,而 StorageClass 是一个全局的配置,没有命名空间隔离性,任何命名空间下有存储需求的应用都可以配置对
volume的概念
对于大多数项目来说,数据文件的持久存储都是非常常见的需求,比如存储用户上传的头像、文件以及数据库数据等。然而在Kubernetes中,由于应用具有高度的动态扩缩容和编排能力,不再固定部署在某些节点上,因此如果直接将数据保存到容器的本地磁盘,既不安全,也难以满足数据持久化的需求。
对于了解云原生应用的读者来说,会知道“无状态应用”的概念——即将应用的数据和状态剥离出来,存储在外部的“云端”,比如常用的NFS(但不建议在生产中使用,因存在单点故障,可考虑分布式存储)、Ceph、GlusterFS、MinIO等。
在传统架构中,要使用这些存储就需要提前在宿主机上进行挂载,这在运维中常导致新节点部署时忘记挂载存储的问题。鉴于此,Kubernetes在设计之初就考虑了数据存储的需求,并通过Volume资源提供了抽象与解耦的能力。
容器里的数据具有短暂性,容器崩溃后重启时往往导致数据丢失,并回到最干净的状态。而当Pod中运行多个容器时,它们之间也经常需要共享文件。这些需求都可以通过Volume来解决。
Docker也有Volume的概念,但其生命周期不受管理,仅是宿主机目录的映射。而Kubernetes的Volume具有与Pod同等的生命周期,可以在容器重启时保留数据。同时,Kubernetes还支持多种类型的Volume,以满足不同的数据持久化需求。
从本质上看,Volume的作用与磁盘挂载类似,是容器内的一个目录。Pod可以通过.spec.volumes
声明,容器通过.spec.containers.volumeMounts
来挂载使用。其操作方式与磁盘挂载几乎无异。
您提供了Kubernetes Volume支持的各种类型,我已经整理转换为Markdown格式:
Volume类型
在传统架构中,企业内可能有自己的存储平台,比如NFS、Ceph、GlusterFS、Minio等。如果读者的环境在公有云,可以使用公有云提供的NAS、对象存储等。在Kubernetes中,Volume也支持配置以上存储,用于挂载到Pod中实现数据的持久化。
Kubernetes Volume支持的卷类型有很多,以下为常用的几种:
- CephFS
- GlusterFS
- iSCSI
- Cinder
- NFS
- RBD
- HostPath
当然,也支持一些Kubernetes独有的类型:
- ConfigMap:用于存储配置文件
- Secret:用于存储敏感数据
- EmptyDir:用于一个Pod内多个容器的数据共享
- PersistentVolumeClaim:对PersistentVolume的申请
以上列举的是一些比较常用的类型,其他支持的类型可以查看Volume的官方文档:
https://kubernetes.io/docs/concepts/storage/volumes/
volume实践
通过 emptyDir 共享数据
EmptyDir 是一个特殊的 Volume 类型,与上述 Volume 不同的是,如果删除 Pod,emptyDir 卷中的数据也将被删除,所以一般 emptyDir 用于 Pod 中的不同 Container 共享数据,比如一个 Pod 存在两个容器 A 和 B,容器 A 需要使用容器 B 产生的数据,此时可以采用 emptyDir 共享数据,类似的使用如 Filebeat 收集容器内程序产生的日志。
默认情况下,emptyDir 卷支持节点上的任何介质,可能是 SSD、磁盘或网络存储,具体取 决于自身的环境。可以将 emptyDir.medium 字段设置为 Memory,让 Kubernetes 使用 tmpfs(内 存支持的文件系统),虽然 tmpfs 非常快,但是 tmpfs 在节点重启时,数据同样会被清除,并且 设置的大小会被计入到 Container 的内存限制当中。
使用 emptyDir 卷的示例如下,直接指定 emptyDir 为 {}
即可:
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx1
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /opt
name: share-volume
- image: nginx
name: nginx2
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 1h"]
volumeMounts:
- mountPath: /opt
name: share-volume
volumes:
- name: share-volume
emptyDir: {}
#medium: Memory
这样容器 ngixn1 和容器 nginx2就可以通过挂载到 /opt
目录来共享数据。
# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-68499574bb-67grm 2/2 Running 0 68s
# kubectl exec -it nginx-68499574bb-67grm -c nginx1 -- bash
root@nginx-68499574bb-67grm:/# echo "nginx1" > 、/opt/nginx1.txt
# -c 指定pod中的容器名字
#在容器nginx2中查看nginx1创建的文件
kubectl exec -it nginx-68499574bb-67grm -c nginx2 -- bash
root@nginx-68499574bb-67grm:/# cat /opt/nginx1.txt
nginx1
HostPath挂载
HostPath 卷可用于实现 Pod 和宿主机之间的数据共享,常用的示例有:
- 将宿主机的时区文件挂载到 Pod
- 将 Pod 的日志文件挂载到宿主机
以下为使用 hostPath 卷的示例,实现将主机的 /etc/localtime
文件挂载到 Pod 的 /etc/localtime
(挂载路径可以不一致):
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /etc/localtime
name: host-time-volume
readOnly: true
volumes:
- name: host-time-volume
hostPath:
path: /etc/localtime
type: File
在配置 HostPath 时,有一个 type 参数,用于表达不同的挂载类型,HostPath 卷常用的 type(类型)如下:
""
(空字符串):默认选项,意味着挂载 hostPath 卷之前不会执行任何检查。DirectoryOrCreate
:如果给定的 path 不存在任何东西,那么将根据需要创建一个权限为 0755 的空目录,和 Kubelet 具有相同的组和权限。Directory
:目录必须存在于给定的路径下。FileOrCreate
:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为 0644,和 Kubelet 具有相同的组和所有权。File
:文件,必须存在于给定路径中。Socket
:UNIX 套接字,必须存在于给定路径中。CharDevice
:字符设备,必须存在于给定路径中。BlockDevice
:块设备,必须存在于给定路径中。
# kubectl get po
NAME READY STATUS RESTARTS AGE
test-pd 1/1 Running 0 6m29s
# 同步时间
# kubectl exec -it test-pd bash
root@test-pd:/# date
Tue Aug 1 08:56:54 CST 2023
挂载 NFS 至容器
在生产环境中,考虑到数据的高可用性,仍然不建议使用 NFS 作为后端存储。因为 NFS 存在单点故障,很多企业并非对其实现高可用,并且 NFS 的性能存在很大的瓶颈。所以生产中,建议使用分布式存储,在公有云中建议使用公有云提供的 NAS 存储来替代 NFS,并且 NAS 性能更好,可用性更高。NFS 作为一个比较流行的远端存储工具,在非生产环境中是一个比较好用的选择,可以快速地搭建使用。
接下来演示如何使用 Volume 挂载 NFS 为容器提供持久化存储。
在开始之前,需要提前配置 NFS 的权限和数据目录,具体可以参考 NFS 的文档,此处只提供 NFS 的 exporter 配置,假设 NFS 提供的数据目录为 /data/nfs
,授权可挂载该目录的 IP 段为 192.168.0.0/24
:
/data/nfs/ *(rw,sync,no_subtree_check,no_root_squash)
和 emptyDir、HostPath 的配置方法类似,NFS 的 Volume 配置也是在 Volumes 字段中配置的,和 emptyDir 不同的是,NFS 属于持久化存储的一种,在 Pod 删除或者重启后,数据依旧会存储在 NFS 节点上。配置 NFS 也相对简单,代码如下(挂载方式和 hostPath、emptyDir 类似,在此不再演示):
- Server:NFS 服务器的 IP。
- Path:NFS 服务器的共享目录。
注意 Kubernetes 所有的节点都需要安装上 nfs-utils 才可以正常挂载 NFS。
本节演示了类型为 emptyDir、hostPath、NFS 的 Volume 的使用,在配置管理 ConfigMap、Secret 时也讲解了类型为 Secret 和 ConfigMap 的使用,后面的章节将为读者讲解 persistentVolumeClaim 以及更高级的存储配置的使用。
对于其他类型的 Volume 配置,原理和配置方法类似,具体可以参考:https://kubernetes.io/docs/concepts/storage/volumes/。
安装nfs
在node安装nfs
yum install -y nfs-utils
# vi /etc/exports
/data/nfs/ *(rw,sync,no_subtree_check,no_root_squash)
# mkdir -p /data/nfs
# exportfs -r
# systemctl start nfs
在master查看
# ip是node节点的ip地址
# showmount -e 192.168.5.62
Export list for 192.168.5.62:
/data/nfs *
挂载nfs到容器
apiVersion: v1
kind: Pod
metadata:
name: test-pod-nfs
spec:
containers:
- image: nginx
name: test-container-nfs
volumeMounts:
- mountPath: /opt
name: nfs
volumes:
- name: nfs
nfs:
server: 192.168.5.62
path: /data/nfs
在nfs中创建文件查看在容器是否存在
# 在node节点操作
# echo "nfs" > /data/nfs/nfs
#进入容器查看
# kubectl exec -it test-pod-nfs bash
root@test-pod-nfs:/# cat /opt/nfs
nfs
root@test-pod-nfs:/#
PersistentVolume
使用场景
虽然 Volume 已经可以接入大部分存储后端,但是实际使用时还有诸多问题,比如:
- 当某个数据卷不再被挂载使用时,里面的数据如何处理?
- 如果想要实现只读挂载如何处理?
- 如果想要只能有一个 Pod 挂载如何处理?
如上所述,对于很多复杂的需求 Volume 可能难以实现,并且无法对存储卷的生命周期进行管理。另一个很大的问题是,在企业内使用 Kubernetes 的不仅仅是 Kubernetes 管理员,可能还有开发人员、测试人员以及初学 Kubernetes 的技术人员,对于 Kubernetes 的 Volume 或者相关存储平台的配置参数并不了解,所以无法自行完成存储的配置。
为此,Kubernetes 引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。PersistentVolume(简称 PV)是由 Kubernetes 管理员设置的存储,PersistentVolumeClaim(简称 PVC)是对 PV 的请求,表示需要什么类型的 PV。它们同样是集群中的一类资源,但其生命周期比较独立,管理员可以单独对 PV 进行增删改查,不受 Pod 的影响,生命周期可能比挂载它的其他资源还要长。如果一个 Kubernetes 集群的使用者并非只有 Kubernetes 管理员,那么可以通过提前创建 PV,用以解决对存储概念不是很了解的技术人员对存储的需求。和单独配置 Volume 类似,PV 也可以使用 NFS、GFS、CEPH 等常用的存储后端,并且可以提供更加高级的配置,比如访问模式、空间大小以及回收策略等。目前 PV 的提供方式有两种:静态或动态。静态 PV 由管理员提前创建,动态 PV 无须提前创建(动态 PV 在后面章节进行讲解)。
PV 回收策略
当用户使用完卷时,可以从 API 中删除 PVC 对象,从而允许回收资源。回收策略会告诉 PV 如何处理该卷。目前回收策略可以设置为 Retain、Recycle 和 Delete:
- Retain:保留,该策略允许手动回收资源,当删除 PVC 时,PV 仍然存在,Volume 被视为已释放,管理员可以手动回收卷。
- Recycle:回收,如果 Volume 插件支持,Recycle 策略会对卷执行 rm -rf 清理该 PV,并使其可用于下一个新的 PVC,但是本策略将来会被弃用,目前只有 NFS 和 HostPath 支持该策略。
- Delete:删除,如果 Volume 插件支持,删除 PVC 时会同时删除 PV,动态卷默认为 Delete,目前支持 Delete 的存储后端包括 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等。
PV 访问策略
在实际使用 PV 时,可能针对不同的应用会有不同的访问策略,比如某类 Pod 可以读写,某类 Pod 只能读,或者需要配置是否可以被多个不同的 Pod 同时读写等,此时可以使用 PV 的访问策略进行简单控制,目前支持的访问策略如下:
- ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为 RWO。
- ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为 ROX。
- ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为 RWX。
- ReadWriteOncePod:只能被一个 Pod 以读写的模式挂载,命令中可以被缩写为 RWOP(1.22以上版本)。
虽然 PV 在创建时可以指定不同的访问策略,但是也要后端的存储支持才行。PV创建成功不表示可以使用,是否能正常使用一般创建完成Pod使用时来判断,比如一般情况下大部分块存储是不支持 ReadWriteMany 的,具体后端存储支持的访问模式可以参考 Kubernetes 官方文档。
在企业内,可能存储很多不同类型的存储,比如 NFS、Ceph、GlusterFS 等,针对不同类型的后端存储具有不同的配置方式,这也是对集群管理员的一种挑战,因为集群管理员需要对每种存储都有所了解。
已经将您提供的内容转换成Markdown格式:
基于 NFS 的 PV 创建
创建一个基于 NFS 的 PV(PV 目前没有 Namespace 隔离,不需要指定命名空间,在任意命名空间下创建的 PV 均可以在其他 Namespace 使用):
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /opt
server: 192.168.5.62
配置参数说明:
- capacity:容量配置。
- volumeMode:卷的模式,目前支持 Filesystem(文件系统)和 Block(块),其中 Block 类型需要后端存储支持,默认为文件系统。
- accessModes:该 PV 的访问模式。
- storageClassName:PV 的类,一个特定类型的 PV 只能绑定到特定类别的 PVC。
- persistentVolumeReclaimPolicy:回收策略。
- nfs:NFS 服务配置,包括以下两个选项。
- path:NFS 上的共享目录。
- server:NFS 的 IP 地址。
注意一般情况下,企业内的 NFS 很有可能是没有高可用性的,所以在企业内部要谨慎使用 NFS 作为后台存储,可以使用公有云的 NAS 服务,和 NFS 的协议一致,或者使用支持文件系统类型的分布式存储,比如 Ceph 等。
# kubectl create -f pv.yaml
persistentvolume/pv-nfs created
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWX Recycle Available nfs 3s
基于 HostPath 的 PV 创建
可以创建一个基于 hostPath 的 PV,和配置 NFS 的 PV 类似,只需要配置 hostPath 字段即可,其他配置基本一致:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-hostpath
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: hostpath
hostPath:
path: /data
配置参数说明:
- hostPath:宿主机路径配置
注意 使用 hostPath 类型需要固定 Pod 所在的节点,防止 Pod 漂移造成数据丢失。
PV 的状态
在创建 PV 后,可以通过 kubectl get pv
查看已经创建的 PV 的状态:
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWX Recycle Bound default/pvc-nfs nfs 2m
pv-host 1Gi RWO Retain Available hostpath 1m
其中有一个字段 STATUS 表示当前 PV 的状态,会有以下几种状态:
- Available:可用,没有被 PVC 绑定的空闲资源。
- Bound:已绑定,已经被 PVC 绑定。
- Released:已释放,PVC 被删除,但是资源还未被重新使用。
- Failed:失败,自动回收失败。
PersistentVolumeClaim
由 Kubernetes 管理员提前创建了 PV,我们应该怎样使用它呢?这里介绍 Kubernetes 的另一个概念 PersistentVolumeClaim(简称 PVC)。PVC 是其他技术人员在 Kubernetes 上对存储的申请,它可以标明一个程序需要用到什么样的后端存储、多大的空间以及以什么访问模式进行挂载。这一点和 Pod 的 QoS 配置类似,Pod 消耗节点资源,PVC 消耗 PV 资源,Pod 可以请求特定级别的资源(CPU 和内存),PVC 可以请求特定的大小和访问模式的 PV。例如申请一个大小为 5Gi 且只能被一个 Pod 只读访问的存储。
在实际使用时,虽然用户通过 PVC 获取存储支持,但是用户可能需要具有不同性质的 PV 来解决不同的问题,比如使用 SSD 硬盘来提高性能。所以集群管理员需要根据不同的存储后端来提供各种 PV,而不仅仅是大小和访问模式的区别,并且无须让用户了解这些卷的具体实现方式和存储类型,达到了存储的解藕,降低了存储使用的复杂度。
PVC 的创建
我们列举了几个常用的存储类型的配置方式,接下来看一下如何让 PVC 和这些 PV 绑定。
注意 PVC 和 PV 进行绑定的前提条件是一些参数必须匹配,比如 accessModes、storageClassName、volumeMode 都需要相同,并且 PVC 的 storage 需要小于等于 PV 的 storage 配置。
创建一个 PVC 即可与 PV 绑定(注意 PV 和 PVC 加粗的部分,PV 和 PVC 进行绑定并非是名字相同,而是 StorageClassName 相同且其他参数一致才可以进行绑定),代码如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: hostpath
resources:
requests:
storage: 1Gi
注意 PVC 具有命名空间隔离性,上述代码没有添加 namespace 参数,会默认创建在 default 命名空间。如果需要给其他命名空间的 Pod 使用,就应将其创建在指定的命名空间。
PVC 的使用
上述创建了 PV,并使用 PVC 与其绑定,现在还差一步就能让程序使用这块存储,那就是将 PVC 挂载到 Pod。和之前的挂载方式类似,PVC 的挂载也是通过 volumes 字段进行配置的,只不过之前需要根据不同的存储后端填写很多复杂的参数,而使用 PVC 进行挂载时,只填写 PVC 的名字即可,不需要再关心任何的存储细节,这样即使不是 Kubernetes 管理员,不懂存储的其他技术人员想要使用存储,也可以非常简单地进行配置和使用。
pvc-nfs
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /data/nfs
server: 192.168.5.62
① capacity 指定 PV 的容量为 1G。
② accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有:
-
ReadWriteOnce:PV 能以 read-write 模式 mount 到单个节点。
-
ReadOnlyMany:PV 能以 read-only 模式 mount 到多个节点。
-
ReadWriteMany :PV 能以 read-write 模式 mount 到多个节点。
③ persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有:
-
Retain: 需要管理员手工回收。
-
Recycle:启动一个pod容器来,清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。
-
Delete: 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、- OpenStack Cinder Volume 等。
④ storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。
⑤ 指定 PV 在 NFS 服务器上对应的目录。
创建pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs
# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pv-nfs 1Gi RWX Recycle Bound default/pvc-nfs nfs 4m48s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc-nfs Bound pv-nfs 1Gi RWX nfs 23s
创建deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nfs
mountPath: /data
volumes:
- name: nfs
persistentVolumeClaim:
claimName: pvc-nfs
验证
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-976b6f459-cpzjk 0/1 ContainerCreating 0 2s
test-pod-nfs 1/1 Running 0 5d
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-976b6f459-cpzjk 1/1 Running 0 44s
test-pod-nfs 1/1 Running 0 5d
[root@k8s-master ~]# kubectl exec -it nginx-deployment-976b6f459-cpzjk bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-deployment-976b6f459-cpzjk:/# cd /data/
root@nginx-deployment-976b6f459-cpzjk:/data# mkdir pod
root@nginx-deployment-976b6f459-cpzjk:/data#
在nfs服务查看
[root@k8s-node01 nfs]# ls /data/nfs/
nfs pod
删除pvc
[root@k8s-master ~]# kubectl delete -f deploy.yaml
[root@k8s-master ~]# kubectl delete -f pvc.yaml
在retain策略中,会将pv中的数据保留,但其 PV 状态会一直处于
Released
,不能被其他 PVC 申请。为了重新使用存储资源,可以删除并重新创建 pv。删除操作只是删除了 PV 对象,存储空间中的数据并不会被删除。
动态存储 StorageClass
虽然使用 PV 和 PVC 能屏蔽一些存储使用上的细节,降低了存储使用的复杂度,但是也会有另一个问题无法解决。当公司 Kubernetes 集群很多,并且使用它们的技术人员过多时,对于 PV 的创建是一个很耗时、耗力的工作,并且达到一定规模后,过多的 PV 将难以维护。所以就需要某种机制用于自动管理 PV 的生命周期,比如创建、删除、自动扩容等,于是 Kubernetes 就设计了一个名为 StorageClass(缩写为 SC,没有命名空间隔离性)的东西,通过它可以动态管理集群中的 PV,这样 Kubernetes 管理员就无须浪费大量的时间在 PV 的管理中。
在 Kubernetes 中,管理员可以只创建 StorageClass“链接”到后端不同的存储,比如 Ceph、GlusterFS、OpenStack 的 Cinder、其他公有云提供的存储等,之后有存储需求的技术人员,创建一个 PVC 指向对应的 StorageClass 即可,StorageClass 会自动创建 PV 供 Pod 使用,也可以使用 StatefulSet 的 volumeClaimTemplate 自动分别为每个 Pod 申请一个 PVC。
定义 StorageClass
定义一个 StorageClass 的示例如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gluster-heketi
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://10.10.10.100:8080"
restuser: "admin"
secretNamespace: "default"
secretName: "heketi-secret"
该示例是使用 StorageClass 链接 GlusterFS 的一个示例,PV 和 Volume 可以直接配置 GlusterFS,其配置参数和 StorageClass 的 parameters 类似,只不过 PV 是单独供 PVC 使用的,Volume 是针对某个 Pod 使用的,而 StorageClass 是一个全局的配置,没有命名空间隔离性,任何命名空间下有存储需求的应用都可以配置对应的 StorageClass 来获取存储。
StorageClass 还提供了一些更高级的配置,比如 provisioner、parameters、reclaimPolicy、allowVolumeExpansion 等:
- Provisioner:指定通过哪个插件来创建 PV,必须指定此字段。
- Parameters:针对后端配置的不同参数。
- ReclaimPolicy:回收策略,可以是 Delete、Retain,默认为 Delete。
- AllowVolumeExpansion:是否允许对 PV 进行扩容。
该小节通过一个简单的示例来说明如何配置一个 StorageClass,对于其他不同类型的存储配置方式只是 parameters 不一样,使用方式都是一样的。
PV/PVC动态供给项目实战
部署步骤
1、部署nfs
3个节点都下载:
# yum -y install nfs-utils
主节点配置nfs服务端
[root@k8s-node01 ~]# mkdir /data/nfs
[root@k8s-node01 ~]# chmod 777 -R /data/nfs
[root@k8s-node01 ~]# cat /etc/exports
/data/nfs *(rw,no_root_squash,no_all_squash,sync)
[root@k8s-node01 ~]# systemctl restart nfs
2、定义一个storageclass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: test-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:
# archiveOnDelete: "false"
archiveOnDelete: "true"
reclaimPolicy: Retain
3、部署授权
因为storage自动创建pv需要经过kube-apiserver,所以要进行授权
-
创建1个sa
-
创建1个clusterrole,并赋予应该具有的权限,比如对于一些基本api资源的增删改查;
-
创建1个clusterrolebinding,将sa和clusterrole绑定到一起;这样sa就有权限了;
-
然后pod中再使用这个sa,那么pod再创建的时候,会用到sa,sa具有创建pv的权限,便可以自动创建pv;
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default #根据实际环境设定namespace,下面类同
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
4、部署一个自动创建pv的服务
这里自动创建pv的服务由nfs-client-provisioner 完成
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
# 注意镜像版本 有的版本不兼容
image: q1617299788/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: test-nfs-storage
- name: NFS_SERVER
value: 192.168.5.62 #NFS Server IP地址
- name: NFS_PATH
value: /data/nfs #NFS挂载卷
volumes:
- name: nfs-client-root
nfs:
server: 192.168.5.62 #NFS Server IP地址
path: "/data/nfs" #NFS 挂载卷
创建:
[root@k8s-master ~]# kubectl apply -f sc.yaml
[root@k8s-master ~]# kubectl apply -f rbac.yaml
[root@k8s-master ~]# kubectl apply -f nfs.yaml
查看创建好的storageclass:
[root@k8s-master ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage nfs Delete Immediate false 3m23s
nfs-client-provisioner 会以pod运行在k8s中,
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-6866f976d9-s6vhx 1/1 Running 0 27s
我们部署一个nginx服务,让其html下面自动挂载数据卷,
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
spec:
clusterIP: None #None值,就是表示无头服务
selector:
app: nginx
ports:
- name: web
port: 80
protocol: TCP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
podManagementPolicy: OrderedReady #pod名-> 0-N,删除N->0
replicas: 3 #三个副本
revisionHistoryLimit: 10
serviceName: nginx-headless
selector:
matchLabels:
app: nginx
template:
metadata: #name没写,会默认生成的
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: web #填vcp名字
mountPath: /var/www/html
imagePullSecrets:
- name: registry-op.test.cn
volumeClaimTemplates:
- metadata:
name: web
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: managed-nfs-storage #存储类名,指向我们已经建好的
volumeMode: Filesystem
resources:
requests:
storage: 512M
查看pod
# kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m16s
web-1 1/1 Running 0 4m9s
web-2 1/1 Running 0 4m3s
进入其中一个容器,创建一个文件:
[root@k8s-master pvc-test]# kubectl exec -it web-0 bash
root@web-0:/# cd /var/www/html/
root@web-0:/var/www/html# ls
root@web-0:/var/www/html# mkdir 123
root@web-0:/var/www/html#
查看nfs目录
[root@k8s-node01 nfs]# ll
total 0
drwxrwxrwx. 3 root root 17 Aug 6 18:47 default-web-web-0-pvc-567713f0-b838-4967-8f3c-86300b7894b9
drwxrwxrwx. 2 root root 6 Aug 6 18:40 default-web-web-1-pvc-82d32cd9-87f4-42fb-a5d2-4c59914183e8
drwxrwxrwx. 2 root root 6 Aug 6 18:40 default-web-web-2-pvc-50b97e1b-5db5-470d-8b97-08646a5241d6
关于StorageClass回收策略对数据的影响
1、第一种配置
archiveOnDelete: "false"
reclaimPolicy: Delete #默认没有配置,默认值为Delete
测试结果
- pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- 删除PVC后,PV被删除且NFS Server对应数据被删除
2、第二种配置
archiveOnDelete: "false"
reclaimPolicy: Retain
测试结果
- pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- 删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
- 重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中
3、第三种配置
archiveOnDelete: "ture"
reclaimPolicy: Retain
测试结果
- pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- 删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
- 重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中
4、第四种配置
archiveOnDelete: "ture"
reclaimPolicy: Delete
测试结果
- pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
- 删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
- 重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中
总结:除以第一种配置外,其他三种配置在PV/PVC被删除后数据依然保留
常见问题
1、如何设置默认的StorageClass
有两种方法,一种是用kubectl patch,一种是在yaml文件直接注明
kubectl patch
#设置default时是为"true"
# kubectl patch storageclass managed-nfs-storage -p '{ "metadata" : { "annotations" :{"storageclass.kubernetes.io/is-default-class": "true"}}}'
# kubectl get sc #名字后面有个"default"字段
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage (default) test-nfs-storage Retain Immediate false 28h
#取消default,值为"false"
# kubectl patch storageclass managed-nfs-storage -p '{ "metadata" : { "annotations" :{"storageclass.kubernetes.io/is-default-class": "false"}}}'
# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage test-nfs-storage Retain Immediate false 28h
yaml文件
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
annotations:
"storageclass.kubernetes.io/is-default-class": "true" #添加此注释,这个变为default storageclass
provisioner: test-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:
# archiveOnDelete: "false"
archiveOnDelete: "true"
reclaimPolicy: Retain
2、如何使用默认的StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-www
# annotations:
# volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" ##这里就可以不用指定
spec:
# storageClassName: "managed-nfs-storage" ##这里就可以不用指定
accessModes:
- ReadWriteMany
#- ReadWriteOnce
resources:
requests:
storage: 10Gi
更多推荐
所有评论(0)