存储类 官方文档
上一节演示了 PVC的自动化实现方式:利用volumeClaimTemplates
这一节将讲解PV的自动化: 利用StorageClass实现,可以根据PVC需求,自动构建相对应的PV持久化存储卷,进一步简化运维管理成本。
在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制。
volumeClaimTemplates实现了pvc的自动化,StorageClass实现了pv的自动化
Kubernetes提供了一套可以自动创建PV的机制,即:Dynamic Provisioning。而这个机制的核心在于StorageClass这个API对象。
StorageClass对象会定义下面两部分内容:
有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass,之后Kubernetes就会调用该StorageClass声明的存储插件,进而创建出需要的PV。
但是其实使用起来是一件很简单的事情,你只需要根据自己的需求,编写YAML文件即可,然后使用kubectl create命令执行即可。
StorageClass 为管理员提供了描述存储 “类” 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 “配置文件”。
在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。而且通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求。
而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner
(制备器),这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
${namespace}-${pvcName}-${pvName}
这样的命名格式创建在 NFS 服务器上的共享数据目录中archieved-${namespace}-${pvcName}-${pvName}
这样的命名格式存在 NFS 服务器上。每个 StorageClass 都包含 provisioner
、parameters
和 reclaimPolicy
字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。
StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。
管理员可以为没有申请绑定到特定 StorageClass 的 PVC 指定一个默认的存储类。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
- debug
volumeBindingMode: Immediate
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。
卷插件 | 内置制备器 | 配置例子 |
---|---|---|
AWSElasticBlockStore | ✓ | AWS EBS |
AzureFile | ✓ | Azure File |
AzureDisk | ✓ | Azure Disk |
CephFS | - | - |
Cinder | ✓ | OpenStack Cinder |
FC | - | - |
FlexVolume | - | - |
Flocker | ✓ | - |
GCEPersistentDisk | ✓ | GCE PD |
Glusterfs | ✓ | Glusterfs |
iSCSI | - | - |
Quobyte | ✓ | Quobyte |
NFS | - | - |
RBD | ✓ | Ceph RBD |
VsphereVolume | ✓ | vSphere |
PortworxVolume | ✓ | Portworx Volume |
ScaleIO | ✓ | ScaleIO |
StorageOS | ✓ | StorageOS |
Local | - | Local |
你不限于指定此处列出的 “内置” 制备器(其名称前缀为 “kubernetes.io” 并打包在 Kubernetes 中)。 你还可以运行和指定外部制备器,这些独立的程序遵循由 Kubernetes 定义的 规范。 外部供应商的作者完全可以自由决定他们的代码保存于何处、打包方式、运行方式、使用的插件(包括 Flex)等。 代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 包含一个用于为外部制备器编写功能实现的类库。你可以访问代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 了解外部驱动列表。
例如,NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。
后面会演示NFS
由 StorageClass 动态创建的 PersistentVolume 会在类的 reclaimPolicy
字段中指定回收策略,可以是 Delete 或者 Retain。如果 StorageClass 对象被创建时没有指定 reclaimPolicy
,它将默认为 Delete
。
通过 StorageClass 手动创建并管理的 PersistentVolume 会使用它们被创建时指定的回收政策。
PersistentVolume 可以配置为可扩展。将此功能设置为 true
时,允许用户通过编辑相应的 PVC 对象来调整卷大小。
当下层 StorageClass 的 allowVolumeExpansion
字段设置为 true 时,以下类型的卷支持卷扩展。
卷类型 | Kubernetes 版本要求 |
---|---|
gcePersistentDisk | 1.11 |
awsElasticBlockStore | 1.11 |
Cinder | 1.11 |
glusterfs | 1.11 |
rbd | 1.11 |
Azure File | 1.11 |
Azure Disk | 1.11 |
Portworx | 1.11 |
FlexVolume | 1.13 |
CSI | 1.14 (alpha), 1.16 (beta) |
说明: 此功能仅可用于扩容卷,不能用于缩小卷。
由 StorageClass 动态创建的 PersistentVolume 将使用类中 mountOptions
字段指定的挂载选项。
如果卷插件不支持挂载选项,却指定了该选项,则制备操作会失败。 挂载选项在 StorageClass 和 PV 上都不会做验证,所以如果挂载选项无效,那么这个 PV 就会失败。
volumeBindingMode
字段控制了卷绑定和动态制备 应该发生在什么时候。
默认情况下,Immediate
模式表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态制备。 对于由于拓扑限制而非集群所有节点可达的存储后端,PersistentVolume 会在不知道 Pod 调度要求的情况下绑定或者制备。
集群管理员可以通过指定 WaitForFirstConsumer
模式来解决此问题。 该模式将延迟 PersistentVolume 的绑定和制备,直到使用该 PersistentVolumeClaim 的 Pod 被创建。 PersistentVolume 会根据 Pod 调度约束指定的拓扑来选择或制备。这些包括但不限于 资源需求、 节点筛选器、 pod 亲和性和互斥性、 以及污点和容忍度。
以下插件支持动态供应的 WaitForFirstConsumer
模式:
以下插件支持预创建绑定 PersistentVolume 的 WaitForFirstConsumer 模式:
动态配置和预先创建的 PV 也支持 CSI卷, 但是你需要查看特定 CSI 驱动程序的文档以查看其支持的拓扑键名和例子。
当集群操作人员使用了 WaitForFirstConsumer
的卷绑定模式, 在大部分情况下就没有必要将制备限制为特定的拓扑结构。 然而,如果还有需要的话,可以使用 allowedTopologies
。
这个例子描述了如何将供应卷的拓扑限制在特定的区域,在使用时应该根据插件 支持情况替换 zone
和 zones
参数。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
- key: failure-domain.beta.kubernetes.io/zone
values:
- us-central1-a
- us-central1-b
搭建StorageClass + NFS,大致有以下几个步骤:
1. 创建一个可用的NFS Server
2. 创建Service Account,这是用来管控NFS Provisioner 在k8s集群中运行的权限
NFS provisioner是一个provisioner相关的插件,需要从网络上下载,我们已经下载下来放到镜像库中了
下载以后需要以pod方式运行通过deployment部署导入到本地环境中创建StorageClass的时候需要指定provisioner
3. 创建StorageClass,负责建立PVC并调用NFS provisioner进行预定的工作,并让PV与PVC建立关联
4. 创建NFS provisioner,有两个功能,一个是在NFS共享目录下创建挂载点(volume),另一个则是建了PV并将PV与NFS的挂载点建立关联
NFS Server在前面的章节中已经安装过了。
鉴权概述 官方文档
使用 RBAC 鉴权 官方文档
# rbac.yaml:#唯一需要修改的地方只有namespace,根据实际情况定义
apiVersion: v1
kind: ServiceAccount # 创建一个账户,主要用来管理NFS provisioner在k8s集群中运行的权限
metadata:
name: nfs-client-provisioner
namespace: default
---
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
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 # 角色名
namespace: default # Role需要指定名称空间,ClusterRole 不需要
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
namespace: default
roleRef: # 绑定哪个角色
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
# 创建NFS资源的StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass # 创建StorageClass
metadata:
name: managed-nfs-storage
provisioner: qgg-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:
archiveOnDelete: "false"
# 创建NFS provisioner
apiVersion: apps/v1
kind: Deployment # 部署nfs-client-provisioner
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
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 # 指定serviceAccount!
containers:
- name: nfs-client-provisioner
image: hub.kaikeba.com/java12/nfs-client-provisioner:v1 #镜像地址
volumeMounts: # 挂载数据卷到容器指定目录
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME # 配置provisioner的Name
value: qgg-nfs-storage # 确保该名称与 StorageClass 资源中的provisioner名称保持一致
- name: NFS_SERVER #绑定的nfs服务器
value: 192.168.66.13
- name: NFS_PATH #绑定的nfs服务器目录
value: /opt/k8s
volumes: # 申明nfs数据卷
- name: nfs-client-root
nfs:
server: 192.168.66.13
path: /opt/k8s
创建pod,申明PVC进行测试
# 申明一个PVC,指定StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
annotations:
# 通过annotations注解,和storage-class进行关联,为什么不使用storageClassName,因为版本比较低
# 这里指定的名字就是上面创建的StorageClass的名字,让它帮我们创建PV
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
---
# 创建测试pod,查看是否可以正常挂载
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox:1.24
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && exit 0 || exit 1" #创建一个SUCCESS文件后退出
volumeMounts:
- name: nfs-pvc # 挂载数据卷
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim: # 数据卷挂载的是pvc
claimName: test-claim #与PVC名称保持一致
StatefulSet+volumeClaimTemplates自动创建PV:
# StateFulDet+volumeClaimTemplates自动创建PV
---
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: # 通过模板化方式绑定
- metadata:
name: www # 指定pvc的名字
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" #只指定了storageClass
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi