前面的课程中我们学习了 PV 和 PVC 的使用方法,但是前面的 PV 都是静态的。比如我要使用⼀个 PVC 的话就必须手动去创建⼀个 PV,这种方式在很大程度上并不能满足我们的需求。再比如我们有⼀个应用对存储的并发度要求比较高,而另外⼀个应用对读写速度又要求比较高,特别是对于 StatefulSet 类型应用简单的来使用静态 PV 就很不合适了,这种情况下就需要用到动态 PV,也就是今天要讲解的 StorageClass。
1、创建StorageClass
要使用 StorageClass,我们就得安装对应的自动配置程序,比如这里我们存储后端使用的是 nfs,那么我们就需要使用到⼀个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
自动创建的 PV 以 {namespace}-namespace−{pvcName}-{pvName} 这样的命名格式创建在 NFS 服务器的共享数据目录中,而当这个 PV 被回收后会以 archieved-pvName这样的命名格式创建在NFS服务器的共享数据目录中,而当这个PV被回收后会以archieved−{namespace}-{pvcName}-pvcName−{pvName} 这样的命名格式存在NFS 服务器上。
当然在部署 nfs-client 之前,我们需要先成功安装上 nfs 服务器,前面我们已经安装过了,服务地址是172.16.200.1,共享数据目录是/data/k8s/,接下来我们直接部署 nfs-client 即可:
NFS_Client Github地址:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client
第1步:配置 Deployment
将里面对应的参数替换成自己的 nfs 配置(nfs-client.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
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: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 172.16.200.1
- name: NFS_PATH
value: /data/k8s
volumes:
- name: nfs-client-root
nfs:
server: 172.16.200.1
path: /data/k8s
第2步:配置环境变量
将环境变量 NFS_SERVER 和 NFS_PATH 替换,当然也包括下⾯的 nfs 配置,我们可以看到这里使用了⼀个名为 nfs-client-provisioner 的 serviceAccount ,所以我们也需要创建⼀个 sa,然后绑定上对应的权限:(nfs-client-sa.yaml)
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
---
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"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
---
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
我们新建的⼀个名为 nfs-client-provisioner 的 ServiceAccount ,然后绑定了⼀个名为 nfs-clientprovisioner-runner 的 ClusterRole ,而该 ClusterRole 声明了⼀些权限,其中就包括对 persistentvolumes 的增、删、改、查等权限,所以我们可以利用该 ServiceAccount 来自动创建PV。
第3步:创建StorageClass
nfs-client 的 Deployment 声明完成后,我们就可以来创建⼀个 StorageClass 对象了:(nfsclient-class.yaml)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: course-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, 必须和 deployment's env PROVISIONER_NAME' 一致
parameters:
archiveOnDelete: "false"
我们声明⼀个名为 course-nfs-storage 的 StorageClass 对象,注意下面 provisioner 对应的值⼀定要和上面 Deployment 下面的 PROVISIONER_NAME 这个环境变量的值⼀样。
现在我们来创建这些资源对象吧:
kubectl create -f nfs-client.yaml
kubectl create -f nfs-client-sa.yaml
kubectl create -f nfs-client-class.yaml
创建完成后查看下资源状态,我们可以看到storage-class的绑定模式:立即绑定;回收模式:删除;允允许扩容:否:
2、新建PVC
上面我们把 StorageClass 资源对象创建成功了,接下来我们通过⼀个示例测试下动态 PV,首先创建⼀个 PVC 对象:(test-pvc.yaml)
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
annotations:
volume.beta.kubernetes.io/storage-class: "course-nfs-storage" # 对应class名称
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
我们声明⼀个 PVC 对象,采用 ReadWriteMany 的访问模式,请求 1Mi 的空间,但是我们可以看到上面的 PVC 文件我们没有标识出任何和 StorageClass 相关联的信息,如果我们现在直接创建这个 PVC 对象,他能够自动绑定上合适的 PV 对象吗?显然是不能的(前提是没有合适的 PV),这里有两种方法可以来利用上面创建的 StorageClass 对象来帮我们自动创建⼀个合适的 PV:
第1种方法:添加 StorageClass 对象标识
在这个 PVC 对象中添加⼀个声明 StorageClass 对象的标识,我们可以利用⼀个 annotations 属性来标识,如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
annotations:
volume.beta.kubernetes.io/storage-class: "course-nfs-storage"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
第2种方法:设置默认存储后端
我们可以设置 course-nfs-storage 的 StorageClass 为 Kubernetes 的默认存储后端,然后使用 kubectl patch 命令来更新:
kubectl patch storageclass course-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
上⾯两种方法都是可以的,当然为了不影响系统的默认行为,我们还是采用第⼀种方法,直接创建即可。我们可以看到⼀个名为 test-pvc 的 PVC 对象创建成功了,状态已经是 Bound 了,当然也产生了⼀个对应的 VOLUME 对象,最重要的⼀栏是 STORAGECLASS,现在也有值了,就是我们刚刚创建的 StorageClass 对象 course-nfs-storage。然后查看 PV 对象:
可以看到自动生成了⼀个关联的 PV 对象,访问模式是 RWX,回收策略是 Delete,这个 PV 对象并不是我们手动创建的,这是通过上面的 StorageClass 对象自动创建的。这就是StorageClass 的创建方法。
3、测试StorageClass
接下来我们还是用⼀个简单的示例来测试下上⾯用 StorageClass 方式声明的 PVC 对象:(test-pod.yaml)
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox
imagePullPolicy: IfNotPresent
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && exit 0 || exit 1"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim
注:上面这个 Pod 非常简单,就是用⼀个 busybox 容器,在 /mnt 目录下⾯新建⼀个 SUCCESS 的文件,然后把 /mnt 目录挂载到上⾯新建的 test-pvc 资源对象上,我们只需要去查看下 nfs 服务器上面的共享数据目录,是否有 SUCCESS 这个文件即可:
我们可以看到下⾯有名字很长的文件夹,这个文件夹的命名方式是不是和上面的规则:{namespace}-namespace−{pvcName}-${pvName} 是⼀样的。我们看到下面有⼀个 SUCCESS 的文件,就可以证明上⾯的验证已经成功了。
另外我们可以看到这里是手动创建的⼀个 PVC 对象,在实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务我们也可以通过⼀个 volumeClaimTemplates 属性来直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nfs-web
spec:
selector:
matchLabels:
app: nfs-web
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nfs-web
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
annotations:
volume.beta.kubernetes.io/storage-class: course-nfs-storage
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
实际上 volumeClaimTemplates 下面就是⼀个 PVC 对象的模板,就类似于这里 StatefulSet 下面的 template,实际上就是⼀个 Pod 的模板,我们不单独创建成 PVC 对象,而是用这种模板就可以动态的去创建对象了,这种方式在 StatefulSet 类型的服务下⾯使用得非常多。直接创建上面的对象,创建完成后可以看到上面的3个 Pod 已经运行成功,然后查看下 PVC 对象:
我们可以看到也生成了3个 PVC 对象,名称由模板名称 name 加上 Pod 的名称组合而成,这3个 PVC 对象也都是绑定状态了,很显然我们查看 PV 也可以看到对应的3个 PV 对象。查看 nfs 服务器上面的共享数据目录:
也有对应的3个数据目录,这就是 StorageClass 的使用方法,对于 StorageClass 多用于StatefulSet 类型的服务;后面我们也会不断的接触到这些知识。