storageclass和本地持久化存储

StorageClass

之前我们部署了PV 和 PVC 的使用方法,但是前面的 PV 都是静态的,什么意思?就是我要使用的一个 PVC 的话就必须手动去创建一个 PV,我们也说过这种方式在很大程度上并不能满足我们的需求,比如我们有一个应用需要对存储的并发度要求比较高,而另外一个应用对读写速度又要求比较高,特别是对于 StatefulSet 类型的应用简单的来使用静态的 PV 就很不合适了,这种情况下我们就需要用到动态 PV,也就是我们今天要讲解的 StorageClass。

创建

要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。

  • 自动创建的 PV 以${namespace}-${pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中
  • 而当这个 PV 被回收后会以archieved-${namespace}-${pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。

当然在部署nfs-client之前,我们需要先成功安装上 nfs 服务器,前面的课程中我们已经过了,服务地址是10.151.30.57,共享数据目录是/data/k8s/,然后接下来我们部署 nfs-client 即可,我们也可以直接参考 nfs-client 的文档:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client,进行安装即可。

第一步:配置 Deployment,将里面的对应的参数替换成我们自己的 nfs 配置(nfs-client.yaml)

kind: Deployment

apiVersion: extensions/v1beta1

metadata:

  name: nfs-client-provisioner

spec:

  replicas: 1

  strategy:

    type: Recreate

  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: 10.151.30.57

            - name: NFS_PATH

              value: /data/k8s

      volumes:

        - name: nfs-client-root

          nfs:

            server: 10.151.30.57

            path: /data/k8s

第二步:将环境变量 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: ["list", "watch", "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-client-provisioner-runner 的ClusterRole,而该ClusterRole声明了一些权限,其中就包括对persistentvolumes的增、删、改、查等权限,所以我们可以利用该ServiceAccount来自动创建 PV。

第三步:nfs-client 的 Deployment 声明完成后,我们就可以来创建一个StorageClass对象了:(nfs-client-class.yaml)

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: course-nfs-storage

provisioner: fuseim.pri/ifs # or choose another name, must match deployment‘s env PROVISIONER_NAME‘

我们声明了一个名为 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

创建完成后查看下资源状态:

$ kubectl get pods

NAME                                             READY     STATUS             RESTARTS   AGE

...

nfs-client-provisioner-7648b664bc-7f9pk          1/1       Running            0          7h

...$ kubectl get storageclass

NAME                 PROVISIONER      AGE

course-nfs-storage   fuseim.pri/ifs   11s

新建

上面把StorageClass资源对象创建成功了,接下来我们来通过一个示例测试下动态 PV,首先创建一个 PVC 对象:(test-pvc.yaml)

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

name: test-pvc

spec:

accessModes:  - ReadWriteMany

resources:

requests:

storage: 1Mi

我们这里声明了一个 PVC 对象,采用 ReadWriteMany 的访问模式,请求 1Mi 的空间,但是我们可以看到上面的 PVC 文件我们没有标识出任何和 StorageClass 相关联的信息,那么如果我们现在直接创建这个 PVC 对象能够自动绑定上合适的 PV 对象吗?显然是不能的(前提是没有合适的 PV),我们这里有两种方法可以来利用上面我们创建的 StorageClass 对象来自动帮我们创建一个合适的 PV

  • 在这个 PVC 对象中添加一个声明 StorageClass 对象的标识,这里我们可以利用一个 annotations 属性来标识,如下:

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: test-pvc

annotations:

volume.beta.kubernetes.io/storage-class: "course-nfs-storage"

spec:

accessModes:  - ReadWriteMany

resources:

requests:

storage: 1Mi

$ kubectl create -f test-pvc.yaml

persistentvolumeclaim "test-pvc" created$ kubectl get pvc

NAME         STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE...

test-pvc     Bound     pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7   1Mi        RWX            course-nfs-storage    2m...

我们可以看到一个名为 test-pvc 的 PVC 对象创建成功了,状态已经是 Bound 了,是不是也产生了一个对应的 VOLUME 对象,最重要的一栏是 STORAGECLASS,现在是不是也有值了,就是我们刚刚创建的 StorageClass 对象 course-nfs-storage。

然后查看下 PV 对象呢:

$ kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                STORAGECLASS          REASON    AGE...

pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7   1Mi        RWX            Delete           Bound       default/test-pvc     course-nfs-storage              8m...

可以看到是不是自动生成了一个关联的 PV 对象,访问模式是 RWX,回收策略是 Delete,这个 PV 对象并不是我们手动创建的吧,这是通过我们上面的 StorageClass 对象自动创建的。这就是 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-pvc

上面这个 Pod 非常简单,就是用一个 busybox 容器,在 /mnt 目录下面新建一个 SUCCESS 的文件,然后把 /mnt 目录挂载到上面我们新建的 test-pvc 这个资源对象上面了,要验证很简单,只需要去查看下我们 nfs 服务器上面的共享数据目录下面是否有 SUCCESS 这个文件即可:

$ kubectl create -f test-pod.yaml

pod "test-pod" created

然后我们可以在 nfs 服务器的共享数据目录下面查看下数据:

$ ls /data/k8s/

default-test-pvc-pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7

我们可以看到下面有名字很长的文件夹,这个文件夹的命名方式是不是和我们上面的规则:${namespace}-${pvcName}-${pvName}是一样的,再看下这个文件夹下面是否有其他文件:

$ ls /data/k8s/default-test-pvc-pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7

SUCCESS

我们看到下面有一个 SUCCESS 的文件,是不是就证明我们上面的验证是成功的啊。

另外我们可以看到我们这里是手动创建的一个 PVC 对象,在实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务我们也可以通过一个 volumeClaimTemplates 属性来直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: nfs-web
spec:
  serviceName: "nginx"
  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: 10Mi
---
apiVersion: v1
kind: Service
metadata:
  name: nfs-web
spec:
  type: NodePort
  selector:
    app: nfs-web
  ports:
  - name: nfs-port
    protocol: TCP
    port: 80
    targetPort: web
    nodePort: 8613

实际上 volumeClaimTemplates 下面就是一个 PVC 对象的模板,就类似于我们这里 StatefulSet 下面的 template,实际上就是一个 Pod 的模板,我们不单独创建成 PVC 对象,而用这种模板就可以动态的去创建了对象了,这种方式在 StatefulSet 类型的服务下面使用得非常多。

直接创建上面的对象:

$ kubectl create -f test-statefulset-nfs.yaml

statefulset.apps "nfs-web" created$ kubectl get pods

NAME                                             READY     STATUS              RESTARTS   AGE...

nfs-web-0                                        1/1       Running             0          1m

nfs-web-1                                        1/1       Running             0          1m

nfs-web-2                                        1/1       Running             0          33s...

创建完成后可以看到上面的3个 Pod 已经运行成功,然后查看下 PVC 对象:

$ kubectl get pvc

NAME            STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE...

www-nfs-web-0   Bound     pvc-cc36b3ce-8b50-11e8-b585-525400db4df7   1Gi        RWO            course-nfs-storage    2m

www-nfs-web-1   Bound     pvc-d38285f9-8b50-11e8-b585-525400db4df7   1Gi        RWO            course-nfs-storage    2m

www-nfs-web-2   Bound     pvc-e348250b-8b50-11e8-b585-525400db4df7   1Gi        RWO            course-nfs-storage    1m...

我们可以看到是不是也生成了3个 PVC 对象,名称由模板名称 name 加上 Pod 的名称组合而成,这3个 PVC 对象也都是 绑定状态了,很显然我们查看 PV 也可以看到对应的3个 PV 对象:

$ kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS          REASON    AGE...                                                        1d

pvc-cc36b3ce-8b50-11e8-b585-525400db4df7   1Gi        RWO            Delete           Bound       default/www-nfs-web-0   course-nfs-storage              4m

pvc-d38285f9-8b50-11e8-b585-525400db4df7   1Gi        RWO            Delete           Bound       default/www-nfs-web-1   course-nfs-storage              4m

pvc-e348250b-8b50-11e8-b585-525400db4df7   1Gi        RWO            Delete           Bound       default/www-nfs-web-2   course-nfs-storage              4m...

查看 nfs 服务器上面的共享数据目录:

$ ls /data/k8s/...

default-www-nfs-web-0-pvc-cc36b3ce-8b50-11e8-b585-525400db4df7

default-www-nfs-web-1-pvc-d38285f9-8b50-11e8-b585-525400db4df7

default-www-nfs-web-2-pvc-e348250b-8b50-11e8-b585-525400db4df7...

是不是也有对应的3个数据目录,这就是我们的 StorageClass 的使用方法,对于 StorageClass 多用于 StatefulSet 类型的服务。

本地持久化存储

本地持久化存储(Local Persistent Volume)就是把数据存储在POD运行的宿主机上,我们知道宿主机有hostPath和emptyDir,由于这两种的特定不适用于本地持久化存储。那么本地持久化存储必须能保证POD被调度到具有本地持久化存储的节点上。

但这里有个问题,通常我们先创建PV,然后创建PVC,这时候如果两者匹配那么系统会自动进行绑定,哪怕是动态PV创建,也是先调度POD到任意一个节点,然后根据PVC来进行创建PV然后进行绑定最后挂载到POD中,可是本地持久化存储有一个问题就是这种PV必须要先准备好,而且不一定集群所有节点都有这种PV,如果POD随意调度肯定不行,如何保证POD一定会被调度到有PV的节点上呢?这时候就需要在PV中声明节点亲和,且POD被调度的时候还要考虑卷的分布情况。

定义PV

apiVersion: v1

kind: PersistentVolume

metadata:

name: example-pv

spec:

capacity:

storage: 5Gi

volumeMode: Filesystem

accessModes:

- ReadWriteOnce

persistentVolumeReclaimPolicy: Delete

storageClassName: local-storage

local: # local类型

path: /data/vol1  # 节点上的具体路径

nodeAffinity: # 这里就设置了节点亲和

required:

nodeSelectorTerms:

- matchExpressions:

- key: kubernetes.io/hostname

operator: In

values:

- node01 # 这里我们使用node01节点,该节点有/data/vol1路径

如果你在node02上也有/data/vol1这个目录,上面这个PV也一定不会在node02上,因为下面的nodeAffinity设置了主机名就等于node01。

另外这种本地PV通常推荐使用的是宿主机上单独的硬盘设备,而不是和操作系统共有一块硬盘,虽然可以这样用。

定义存储类

kind: StorageClass

apiVersion: storage.k8s.io/v1

metadata:

name: local-storage

provisioner: kubernetes.io/no-provisioner

volumeBindingMode: WaitForFirstConsumer

这里的volumeBindingMode: WaitForFirstConsumer很关键,意思就是延迟绑定,当有符合PVC要求的PV不立即绑定。因为POD使用PVC,而绑定之后,POD被调度到其他节点,显然其他节点很有可能没有那个PV所以POD就挂起了,另外就算该节点有合适的PV,而POD被设置成不能运行在该节点,这时候就没法了,延迟绑定的好处是,POD的调度要参考卷的分布。当开始调度POD的时候看看它要求的LPV在哪里,然后就调度到该节点,然后进行PVC的绑定,最后在挂载到POD中,这样就保证了POD所在的节点就一定是LPV所在的节点。所以让PVC延迟绑定,就是等到使用这个PVC的POD出现在调度器上之后(真正被调度之前),然后根据综合评估再来绑定这个PVC。

定义PVC

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

name: local-claim

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 5Gi

storageClassName: local-storage

可以看到这个PVC是pending状态,这也就是延迟绑定,因为此时还没有POD。

定义POD

apiVersion: apps/v1

kind: Deployment

metadata:

name: tomcat-deploy

spec:

replicas: 1

selector:

matchLabels:

appname: myapp

template:

metadata:

name: myapp

labels:

appname: myapp

spec:

containers:

- name: myapp

image: tomcat:8.5.38-jre8

ports:

- name: http

containerPort: 8080

protocol: TCP

volumeMounts:

- name: tomcatedata

mountPath : "/data"

volumes:

- name: tomcatedata

persistentVolumeClaim:

claimName: local-claim

这个POD被调度到node01上,因为我们的PV就在node01上,这时候你删除这个POD,然后在重建该POD,那么依然会被调度到node01上。

总结:本地卷也就是LPV不支持动态供给的方式,延迟绑定,就是为了综合考虑所有因素再进行POD调度。其根本原因是动态供给是先调度POD到节点,然后动态创建PV以及绑定PVC最后运行POD;而LPV是先创建与某一节点关联的PV,然后在调度的时候综合考虑各种因素而且要包括PV在哪个节点,然后再进行调度,到达该节点后在进行PVC的绑定。也就说动态供给不考虑节点,LPV必须考虑节点。所以这两种机制有冲突导致无法在动态供给策略下使用LPV。换句话说动态供给是PV跟着POD走,而LPV是POD跟着PV走。

Storageclass参考:https://www.cnblogs.com/fuyuteng/p/11017630.html

本地持久化存储(LPV)参考:https://www.cnblogs.com/rexcheny/p/10925464.html

原文地址:https://www.cnblogs.com/leiwenbin627/p/12641550.html

时间: 2024-11-10 21:07:42

storageclass和本地持久化存储的相关文章

本地持久化存储

//用持久化对象去存储一个字符串作为标识,如果找到标识用户不是第一次登陆,就加在主界面,如果找不到,说明用户是第一次登陆,加载引导界面. if (![[NSUserDefaults standardUserDefaults] boolForKey:@"ccc"]) { UserGuideViewController *guideVC = [[UserGuideViewController alloc] init]; self.window.rootViewController = gu

iOS 关于本地持久化存储的探讨

目前,用以本地化存储的方式有很多,常用的有以下: 1.临时缓存 先说说临时缓存,临时缓存一般相当于用来管理应用程序中全局需要常用的一些内容.比如当前用户的ID或者当前的定位信息等. 常用的方式就是写一个静态变量,然后使用类方法调用.(或者单例类也行) static NSMutableDictionary *_cacheDic; #pragma mark - 临时缓存(退出应用后消失) +(id)cacheValueForKey:(SCCacheKey)key{ return [[self cac

iOS数据持久化存储

本文中的代码托管在github上:https://github.com/WindyShade/DataSaveMethods 相对复杂的App仅靠内存的数据肯定无法满足,数据写磁盘作持久化存储是几乎每个客户端软件都需要做的.简单如"是否第一次打开"的BOOL值,大到游戏的进度和状态等数据,都需要进行本地持久化存储.这些数据的存储本质上就是写磁盘存文件,原始一点可以用iOS本身支持有NSFileManager这样的API,或者干脆C语言fwrite/fread,Cocoa Touch本身

【Unity3D游戏开发】—— PlayerPrefs类实现本地持久化数据存储

在很多游戏中都会有"存储进度","读取进度"等菜单,或者当我们进入下一个场景时有些数据需要带入下一个场景.这就需要数据储存.Unity中提供了一个用于本地持久化保存于读取数据的类--PlayerPrefs. 它是以键值对的形式将数据保存在文件中. 下面来看看PlayerPrefs的简单用法.. //得到存储的数据 Grade = PlayerPrefs.GetFloat("mGrade", 0.0f).ToString(); //获取浮点型 Ag

kubernetes持久化存储,静态存储【pv】,动态存储【StorageClass】(5)

在开始介绍k8s持久化存储前,我们有必要了解一下k8s的emptydir和hostpath.configmap以及secret的机制和用途 1.EmptydirEmptyDir是一个空目录,他的生命周期和所属的 Pod 是完全一致的,pod删掉目录消失 2.HostpathHostpath会把宿主机上的指定卷加载到容器之中,如果 Pod 发生跨主机的重建,其内容就难保证了 3.ConfigmapConfigMap跟Secrets类似,但是ConfigMap可以更方便的处理不包含敏感信息的字符串.

iOS开发——数据持久化&使用NSUserDefaults来进行本地数据存储

使用NSUserDefaults来进行本地数据存储 NSUserDefaults适合存储轻量级的本地客户端数据,比如记住密码功能,要保存一个系统的用户名.密码.使用NSUserDefaults是首选.下次再登陆的时候就可以直接从NSUserDefaults里面读取上次登陆的信息. 一般来说本地存储数据我们还可以是用SQlite数据库,或者使用自己建立的plist文件什么的,但这还得自己显示创建文件,读取文件,很麻烦,而是用NSUserDefaults则不用管这些东西,就像读字符串一样,直接读取就

Longhorn:实现Kubernetes集群的持久化存储

Longhorn项目是Rancher Labs推出的开源的基于云和容器部署的分布式块存储新方式.Longhorn遵循微服务的原则,利用容器将小型独立组件构建为分布式块存储,并使用容器编排来协调这些组件,形成弹性分布式系统. 自2017年4月Longhorn项目发布以来,人们对在Kubernetes集群上运行Longhorn存储就产生了极大的兴趣.近日,Longhorn v0.2版本发布了,它可支持任何Kubernetes集群的持久化存储实现! Why Longhorn 如今,基于云和容器的部署规

k8s之volumes持久化存储

k8s之数据持久化 kubernetes存储卷:我们知道默认情况下容器的数据都是非持久化的,在容器销毁以后数据也跟着丢失,所以docker提供了volume机制以便将数据持久化存储.类似的,k8s提供了更强大的volume机制和丰富的插件,解决了容器数据持久化和容器间共享数据的问题. volume:我们经常会说:容器和 Pod 是短暂的.其含义是它们的生命周期可能很短,会被频繁地销毁和创建.容器销毁时,保存在容器内部文件系统中的数据都会被清除.为了持久化保存容器的数据,可以使用k8s volum

一文读懂 K8s 持久化存储流程

作者 | 孙志恒(惠志)? 阿里巴巴开发工程师<br /> 导读:众所周知,K8s 的持久化存储(Persistent Storage)保证了应用数据独立于应用生命周期而存在,但其内部实现却少有人提及.K8s?内部的存储流程到底是怎样的?PV.PVC.StorageClass.Kubelet.CSI 插件等之间的调用关系又如何,这些谜底将在本文中一一揭晓. K8s 持久化存储基础 在进行 K8s 存储流程讲解之前,先回顾一下 K8s 中持久化存储的基础概念. 1. 名词解释 in-tree:代