ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

4.3 K8s存储之 Storage Class

存储类StorageClass

说明:要使用nfs、glusterfs等网络存储,pod运行的宿主机上必须安装相关的程序包:

nfs:yum install -y nfs-utils

glusterfs:yum install -y centos-release-gluster ; yum install -y glusterfs-fuse

1、存储类介绍

简称:sc

​ Kubernetes集群管理员通过提供不同的存储类,可以满足用户不同的服务质量级别、备份策略和任意策略要求的存储需求(实现对不同存储进行分类)。动态存储卷供应使用StorageClass进行实现,其实现pv的动态供给、按需被创建。如果没有动态存储供应,Kubernetes集群的管理员将不得不通过手工的方式类创建新的存储卷。通过动态存储卷,Kubernetes将能够按照用户的需要,自动创建其需要的存储。

定义存储类后,当创建pvc后就不再请求pv,而是来请求StorageClass,而StorageClass将会为pvc自动创建一个可用pv。(存储类实例化成存储对象即pv)

基于StorageClass的动态存储供应整体过程如下图所示:ss

ss

1)集群管理员预先创建存储类(StorageClass);

2)用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim);

3)存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume);

4)系统读取存储类的信息;

5)系统基于存储类的信息,在后台自动创建PVC需要的PV;

6)用户创建一个使用PVC的Pod;

7)Pod中的应用通过PVC进行数据的持久化;

8)而PVC使用PV进行数据的最终持久化处理。

2、定义存储类

​ 每一个存储类都包含provisioner、parameters和reclaimPolicy这三个参数域,当一个属于某个类的PersistentVolume需要被动态提供时,将会使用上述的参数域来进行创建。

​ 存储类对象的名称非常重要,用户通过类名称请求特定的存储类。管理员创建存储类对象时,会设置类的名称和其它的参数,存储类的对象一旦被创建,将不能被更新。管理员能够为那些不请求某个特定存储类的PVC指定一个默认的存储类

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: standard    #StorageClass是集群级别的对象,不属于任何名称空间
# 指定存储类的供应者
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
# 指定回收策略
reclaimPolicy: Retain
mountOptions:
  - debug
2.1 供应者:provisioner

存储类有一个供应者的参数,此参数域决定PV使用什么存储卷插件。该参数是必须参数,参数必需进行设置:

存储卷内置供应者配置例子
AWSElasticBlockStoreAWS
AzureFileAzure File
AzureDiskAzure Disk
CephFS
CinderOpenStack Cinder
FC
FlexVolume
Flocker
GCEPersistentDiskGCE
GlusterfsGlusterfs
iSCSI
PhotonPersistentDisk
QuobyteQuobyte
NFS
RBDCeph RBD
VsphereVolumevSphere
PortworxVolumePortworx Volume
ScaleIOScaleIO
StorageOSStorageOS
LocalLocal

​ Kubernetes的存储类并不局限于表中的“interneal”供应者,“interneal”供应者的名称带有“kubernetes.io”前缀;也可以允许和指定外部的供应者,外部供应者通过独立的程序进行实现。外部供应者的作者可以完全自行决定代码 在何处生存、如何供应、如何运行、使用什么卷插件(包括Flex)等,kubernetes-incubator/external-storage仓库中存在编写外部提供者的类库。例如,NFS不是内部的供应者,但也是可以使用。在kubernetes-incubator/external-storage仓库中以列表的形式展示了一些外部的供应者,一些第三方供应商也提供了他们自己的外部供应者。

2.2 提供者的参数:parameters

存储类存在很多描述存储卷的参数,依赖不同的提供者可能有不同的参数。例如,对于type参数,它的值可能为io1。当一个参数被省略,则使用默认的值。

2.3 回收策略:reclaimPolicy

通过存储类创建的pv通过reclaimPolicy参数来指定回收策略,它的值可以是Delete或者Retain,默认为Delete。对于通过手工创建的,但是使用存储类进行管理的持久化存储卷,将使用在创建时指定的存储卷回收策略。

  • Delete:如果删除pvc,pv和数据都会被删除。默认值
  • Retain:如果删除pvc,pv处于“Released”状态。即便删除pvc,数据也不会被删除。
2.4挂载选项:mountOptions

通过存储类动态创建的pv,通过mountOptions参数指定的挂载选项。如果存储卷插件不支持mountOptions指定的挂载选项,这提供存储供应(provisioner)就会失败。挂载选项不会在存储类或者PV中进行验证,因此存储类或PV中任何一个无效都会导致PV的挂载失败。

3、使用存储类

动态存储卷供应基于StorageClass的API对象的来实现,集群管理员能够按需定义StorageClass对象,每一个StorageClass对象能够指定一个存储卷插件(即供应者)。集群管理员能够在一个集群中定义各种存储卷供应,用户不需要了解存储的细节和复杂性,就能够选择符合自己要求的存储。

3.1 创建StorageClass

为了启用动态供应,集群管理员需要预先为用户创建一个或者多个存储类对象。存储类对象定义了使用哪个供应者,以及供应者相关的参数。下面是存储类的一个示例,它创建一个名称为slow的存储类,使用gce供应者:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard

下面创建了一个名为“fast”的存储类,其提供类似固态磁盘的存储卷磁盘:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
3.2 pvc使用StorageClass

用户通过在PersistentVolumeClaim中包含一个存储类,来请求动态供应存储。在Kubernetes v1.6之前的版本,通过volume.beta.kubernetes.io/storage-class注释类请求动态供应存储;在v1.6版本之后,用户应该使用PersistentVolumeClaim对象的storageClassName参数来请求动态存储。

下面是请求fast存储类的持久化存储卷声明的YAML配置文件示例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
 storageClassName: fast  # 指定所使用的存储类,此存储类将会自动创建符合要求的PV
 resources:
    requests:
      storage: 30Gi

此声明将使用类似于固态存储磁盘,当持久化存储卷声明被删除后,存储卷也将会被销毁。

3.3 默认存储类

如果Kubernetes的集群中没有指定存储类,集群管理员可以通过执行下面的设置,启用默认的存储类:

  • 标记一个默认的StorageClass对象;
  • 确定API server中DefaultStorage接入控制器已被启用

管理员能够通过添加storageclass.kubernetes.io/is-default-class注解,标记一个特定的StorageClass作为默认的存储类。在集群中,如果存在一个默认的StorageClass,系统将能够在不指定storageClassName 的情况下创建一个PersistentVolume,DefaultStorageClass接入控制器会自动将storageClassName指向默认的存储类。

注意:在一个集群中,最多只能有一个默认的存储类,如果没有默认的存储类,那么如果在PersistentVolumeClaim中没有显示指定storageClassName,则将无法创建PersistentVolume。

4、NFS存储类(外部存储类)示例

文档:https://github.com/kubernetes-incubator/external-storage/blob/master/nfs/docs/deployment.md

4.1 【前提准备】部署nfs-provisioner

deployment.yaml主要修改以下几点:

wget https://raw.githubusercontent.com/kubernetes-incubator/external-storage/master/nfs/deploy/kubernetes/deployment.yaml
sed -i 's/image: quay.io/image: quay-mirror.qiniu.com/g' deployment.yaml 
sed -i 's/example.com/ljzsdut.com/g' deployment.yaml 

1、为nfs-provisioner实例选择存储卷,用于存储状态和nfs数据,并将存储卷挂接到容器的/export 命令。

...
 volumeMounts:
    - name: export-volume
      mountPath: /export
volumes:
  - name: export-volume
    hostPath:
      path: /tmp/nfs-provisioner
...

2、为StorageClass选择一个供应者名称。

args:
  - "-provisioner=example.com/nfs"
...

**nfs-provisioner完整的deployment.yaml文件内容如下: **

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
---
kind: Service
apiVersion: v1
metadata:
  name: nfs-provisioner
  labels:
    app: nfs-provisioner
spec:
  ports:
    - name: nfs
      port: 2049
    - name: mountd
      port: 20048
    - name: rpcbind
      port: 111
    - name: rpcbind-udp
      port: 111
      protocol: UDP
  selector:
    app: nfs-provisioner
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-provisioner
spec:
  selector:
    matchLabels:
      app: nfs-provisioner
  replicas: 1
  strategy:
    type: Recreate 
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
        - name: nfs-provisioner
          image: quay.io/kubernetes_incubator/nfs-provisioner:latest #修改为符合国情的镜像
          ports:
            - name: nfs
              containerPort: 2049
            - name: mountd
              containerPort: 20048
            - name: rpcbind
              containerPort: 111
            - name: rpcbind-udp
              containerPort: 111
              protocol: UDP
          securityContext:
            capabilities:
              add:
                - DAC_READ_SEARCH
                - SYS_RESOURCE
          args:
            # 定义提供者的名称,存储类通过此名称指定提供者。
            - "-provisioner=ljzsdut.com/nfs"   
          env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: SERVICE_NAME
              value: nfs-provisioner
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          imagePullPolicy: "IfNotPresent"
          volumeMounts:
            #在/export目录中存储nfs-provisioner实例的状态和nfs数据,该卷需要具有文件系统,不支持NFS
            - name: export-volume  
              mountPath: /export
      volumes:
        - name: export-volume
          hostPath:
            path: /srv  #该目录除了存储nfs-provisioner的一些元数据外还在此目录下创建由sc创建的各个pv的nfs共享目录(数据目录)

在设置好deploy/kubernetes/deployment.yaml文件后,通过kubectl create命令在Kubernetes集群中部署nfs-provisioner。

$ kubectl create -f {path}/deployment.yaml
service "nfs-provisioner" created
deployment "nfs-provisioner" created

rbac.yaml

wget https://raw.githubusercontent.com/kubernetes-incubator/external-storage/master/nfs/deploy/kubernetes/rbac.yaml

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-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: ["services", "endpoints"]
    verbs: ["get"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
     # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-provisioner
  apiGroup: rbac.authorization.k8s.io
4.2 创建StorageClass

下面是example-nfs的StorageClass配置文件,此配置文件定义了一个名称为nfs-storageclass的存储类,此存储类的提供者为nfs-provisioner。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storageclass
provisioner: ljzsdut.com/nfs  #上一步中定义的provisioner的名字,必须字段

通过kubectl create -f命令使用上面的配置文件创建:

$ kubectl create -f deploy/kubernetes/class.yaml
storageclass “example-nfs” created

**在存储类被正确创建后,就可以创建PersistenetVolumeClaim来请求StorageClass,而StorageClass将会为PersistenetVolumeClaim自动创建一个可用PersistentVolume。**所以说存储类为pvc实现动态供给pv。

4.3 创建pvc

PersistenetVolumeClaim是对PersistenetVolume的声明,即PersistenetVolume为存储的提供者,而PersistenetVolumeClaim为存储的消费者。下面是PersistentVolumeClaim的YAML配置文件,此配置文件通过spec.storageClassName字段指定所使用的存储储类。

在此配置文件中,使用nfs-storageclass存储类为PersistenetVolumeClaim创建PersistenetVolume,所要求的PersistenetVolume存储空间大小为1Mi,可以被多个容器进行读取和写入操作。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: nfs-storageclass
  resources:
    requests:
      storage: 1Mi

通过kubectl create命令创建上述的持久化存储卷声明:

$ kubectl create -f {path}/claim.yaml
4.4 创建使用pvc的deployment

在这里定义名为busybox-deployment的部署YAML配置文件,使用的镜像为busybox。基于busybox镜像的容器需要对**/mnt**目录下的数据进行持久化,在YAML文件指定使用名称为nfs的PersistenVolumeClaim对容器的数据进行持久化。

注意:所有的k8s的node节点都要安装yum install -y nfs-utils

# This mounts the nfs volume claim into /mnt and continuously
# overwrites /mnt/index.html with the time and hostname of the pod. 
apiVersion: v1
kind: Deployment
metadata:  
  name: busybox-deployment
spec:  
  replicas: 2  
  selector:    
    name: busybox-deployment
  template:    
    metadata:      
      labels:        
        name: busybox-deployment    
    spec:      
      containers:      
      - image: busybox        
        command:          
        - sh          
        - -c          
        - 'while true; do date > /mnt/index.html; hostname >> /mnt/index.html; sleep $(($RANDOM % 5 + 5)); done'        
        imagePullPolicy: IfNotPresent        
        name: busybox        
        volumeMounts:          
        # name must match the volume name below          
        - name: nfs            
          mountPath: "/mnt" 
     volumes:      
     - name: nfs        
       persistentVolumeClaim:          
         claimName: nfs-pvc

通过kubectl create创建busy-deployment部署:

$ kubectl create -f {path}/nfs-busybox-deployment.yaml

参考资料

  1. 《Storage Classes》地址:https://kubernetes.io/docs/concepts/storage/storage-classes/
  2. 《Dynamic Volume Provisioning》地址:https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/
  3. 《nfs-provisioner》地址:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs

##5、Glusterfs存储类(内部存储类)示例

由于glusterfs是内部存储类,已经在k8s集群中积成了provisioner,无需自己实现。

5.1、创建StorageClass

# provisioner:表示存储分配器,需要根据后端存储的不同而变更;
# reclaimPolicy: 默认即”Delete”,删除pvc后,相应的pv及后端的volume,brick(lvm)等一起删除;设置为”Retain”时则保留数据,需要手工处理
# resturl:heketi API服务提供的url;
# restauthenabled:可选参数,默认值为”false”,heketi服务开启认证时必须设置为”true”;
# restuser:可选参数,开启认证时设置相应用户名;
# secretNamespace:可选参数,开启认证时可以设置为使用持久化存储的namespace;
# secretName:可选参数,开启认证时,需要将heketi服务的认证密码保存在secret资源中;
# clusterid:可选参数,指定集群id,也可以是1个clusterid列表,格式为”id1,id2”;
# volumetype:可选参数,设置卷类型及其参数,如果未分配卷类型,则由分配器决定卷类型;如”volumetype: replicate:3”表示3副本的replicate卷,”volumetype: disperse:4:2”表示disperse卷,其中‘4’是数据,’2’是冗余校验,”volumetype: none”表示distribute分布式卷# 
[root@kubenode1 heketi]# vim gluster-heketi-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: glusterfs
provisioner: kubernetes.io/glusterfs
reclaimPolicy: Delete
parameters:
  resturl: "http://172.30.200.80:8080"
  clusterid: "dae1ab512dfad0001c3911850cecbd61"  #可选
  restauthenabled: "true"
  restuser: "admin"
  secretNamespace: "default"
  secretName: "heketi-secret"   
  #restuserkey: "adminkey"    #明文方式指定密码,不推荐
  volumetype: "replicate:2"
  
---
apiVersion: v1
kind: Secret
metadata:
  name: heketi-secret   #注意name与storageclass资源中的secretName定义一致;
  namespace: default    #注意namespace与storageclass资源中的secretNamespace定义一致
data:
  key: YWRtaW5AMTIz  # base64 encoded password. E.g.: echo -n "mypassword" | base64
type: kubernetes.io/glusterfs  #type必须为“kubernetes.io/glusterfs”

[root@kubenode1 heketi]# kubectl apply -f gluster-heketi-storageclass.yaml  

5.2、创建pvc

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mydata-pvc
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: glusterfs
  resources:
    requests:
      storage: 1Mi

通过kubectl create命令创建上述的持久化存储卷声明:

$ kubectl create -f claim.yaml

5.3、创建使用pvc的deployment

注意:所有的k8s的node节点都要安装glusterfs-fuse:yum install -y centos-release-gluster ; yum install -y glusterfs-fuse

apiVersion: v1
kind: Deployment
metadata:  
  name: busybox-deployment
spec:  
  replicas: 2  
  selector:    
    name: busybox-deployment
  template:    
    metadata:      
      labels:        
        name: busybox-deployment    
    spec:      
      containers:      
      - image: busybox        
        command:          
        - sh          
        - -c          
        - 'while true; do date > /mnt/index.html; hostname >> /mnt/index.html; sleep $(($RANDOM % 5 + 5)); done'        
        imagePullPolicy: IfNotPresent        
        name: busybox        
        volumeMounts:          
        - name: data           
          mountPath: "/mnt" 
     volumes:      
     - name: data        
       persistentVolumeClaim:          
         claimName: mydata-pvc

通过kubectl create创建busy-deployment部署:

$ kubectl create -f nfs-busybox-deployment.yaml