pengpengboshi

官方文档地址

https://kubernetes.io/docs/concepts/storage/volumes/

一、Kubernetes存储分类

1、本地存储

1.1、本地存储分类

本地存储有3种

  • hostPath
  • local
  • emptyDir

1.2、为什么需要本地存储

  1. 特殊使用场景需求,如需要个临时存储空间,运行cAdvisor需要能访问到node节点/sys/fs/cgroup的数据,做本机单节点的k8s环境功能测试等等。

  2. 容器集群只是做小规模部署,满足开发测试、集成测试需求。

  3. 作为分布式存储服务的一种补充手段,比如我在一台node主机上插了块SSD,准备给某个容器吃小灶。

  4. 目前主流的两个容器集群存储解决方案是ceph和glusterfs,二者都是典型的网络分布式存储,所有的数据读、写都是对磁盘IO和网络IO的考验,所以部署存储集群时至少要使用万兆的光纤网卡和光纤交换机。如果你都没有这些硬货的话,强上分布式存储方案的结果就是收获一个以”慢动作”见长的容器集群啦。

  5. 分布式存储集群服务的规划、部署和长期的监控、扩容与运行维护是专业性很强的工作,需要有专职的技术人员做长期的技术建设投入。

2、集群存储

集群存储有3种,就是前面我们介绍过的投射数据卷,不再细说

  • secret
  • configMap
  • downwardAPI

3、远程存储

远程存储相当多,下面只列出常见的几种,更多种类详见官方文档

  • persistentClaim
  • nfs
  • gitRepo
  • flexVolume
  • rbd
  • cephfs

二、本地存储详解

1、hostPath

官方定义如下

A hostPath volume mounts a file or directory from the host node’s filesystem into your Pod. This is not something that most Pods will need, but it offers a powerful escape hatch for some applications.

这种方式依赖与 node,如果 pod 被重建创建到了其他的 node,这时如果没有在新 node 上准备好 hostpath,就会出问题

这种会把宿主机上的指定卷加载到容器之中,当然,如果 Pod 发生跨主机的重建,其内容就难保证了。

这种卷一般和DaemonSet搭配使用,用来操作主机文件,例如进行日志采集的 FLK 中的 FluentD 就采用这种方式,加载主机的容器日志目录,达到收集本主机所有日志的目的。

hatch 英[hætʃ] 美[hætʃ]

v. 孵出; 出壳; 孵化; 破壳; 使(小鸟、小鱼、小虫等)孵出;

capacity 英[kəˈpæsəti] 美[kəˈpæsəti]

n. 容量; 容积; 容纳能力; 领悟(或理解、办事)能力; 职位; 职责;

实例

[root@master hostPath]# cat hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
 name: test-pd
spec:
 containers:
  	- image: daocloud.io/library/nginx:1.7.9
 	  name: test-container
	  volumeMounts:
  	  - mountPath: /test-pd
   	    name: test-volume

 volumes:
 - name: test-volume
   hostPath:
	 # directory location on host
     path: /data01
     # this field is optional
     type: Directory

2、local

一个很新的存储类型,建议在k8s v1.10+以上的版本中使用。该local volume类型目前还只是beta版,这里不做测试,同样也不建议在生产环境中使用

hostPath和local对比

  1. 二者都基于node节点本地存储资源实现了容器内数据的持久化功能,都为某些特殊场景下提供了更为适用的存储解决方案;

  2. 前者时间很久了,所以功能稳定,而后者因为年轻,所以功能的可靠性与稳定性还需要经历时间和案例的历练,尤其是对Block设备的支持还只是alpha版本;

  3. 二者都为k8s存储管理提供了PV、PVC和StorageClass的方法实现;

  4. local volume实现的StorageClass不具备完整功能,目前只支持卷的延迟绑定;

  5. hostPath是单节点的本地存储卷方案,不提供任何基于node节点亲和性的pod调度管理支持;

  6. local volume适用于小规模的、多节点的k8s开发或测试环境,尤其是在不具备一套安全、可靠且性能有保证的存储集群服务时;

3、emptyDir

EmptyDir是一个空目录,他的生命周期和所属的 Pod 是完全一致的

EmptyDir的用处是,可以在同一 Pod 内的不同容器之间共享工作过程中产生的文件。

默认,EmptyDir 是使用主机磁盘进行存储的,也可以设置emptyDir.medium 字段的值为Memory,来提高运行速度,但是这种设置,对该卷的占用会消耗容器的内存份额。

实例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

三、认识 pv 和 pvc

1、pv和pvc概述

PersistentVolume(持久卷)和PersistentVolumeClaim(持久卷申请)是k8s提供的两种API资源,用于抽象存储细节。也提供了基础设施和应用之间的分界,管理员关注于如何通过pv提供存储功能而无需关注用户如何使用,同样的用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。换种说法就是管理员创建一系列的 PV 提供存储,然后为应用提供 PVC,应用程序仅需要加载一个 PVC,就可以进行访问

1.5版本之后又提供了 PV 的动态供应。可以不经 PV 步骤直接创建 PVC

pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式,这就可以通过Provision -> Claim 的方式,来对存储资源进行控制。

2、基本概念

1、pv

管理存储和管理计算有着明显的不同。PersistentVolume给用户和管理员提供了一套API,抽象出存储是如何提供和消耗的细节。

PersistentVolume(持久卷,简称PV)是集群内,由管理员提供的网络存储的一部分。就像集群中的节点一样,PV也是集群中的一种资源。它的生命周期却是和使用它的Pod相互独立的。PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节。

2、pvc

PersistentVolumeClaim(持久卷声明,简称PVC)是用户的一种存储请求。它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源。Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式(可以被映射为一次读写或者多次只读)。

PVC允许用户消耗抽象的存储资源,用户也经常需要各种属性(如性能)的PV。集群管理员需要提供各种各样、不同大小、不同访问模式的PV,而不用向用户暴露这些volume如何实现的细节。因为这种需求,就催生出一种StorageClass资源。

3、StorageClass

StorageClass提供了一种方式,使得管理员能够描述他提供的存储的等级。集群管理员可以将不同的等级映射到不同的服务等级、不同的后端策略。 后面会详细解释

4、volume和claim的生命周期

PV是集群中的资源,PVC是对这些资源的请求,同时也是这些资源的“提取证”。PV和PVC的交互遵循以下生命周期:

  1. 供给

​ 有两种PV提供的方式:静态和动态。

静态

​ 集群管理员创建多个PV,它们携带着真实存储的详细信息,这些存储对于集群用户是可用的。它们存在于Kubernetes API中,并可用于存储使用。

**动态**

​ 当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass:PVC必须请求这样一个等级,而管理员必须已经创建和配置过这样一个等级,以备发生这种动态供给的情况。请求等级配置为“”的PVC,有效地禁用了它自身的动态供给功能。

  1. 绑定

用户创建一个PVC(或者之前就已经就为动态供给创建了),指定要求存储的大小和访问模式。master中有一个控制回路用于监控新的PVC,查找匹配的PV(如果有),并把PVC和PV绑定在一起。如果一个PV曾经动态供给到了一个新的PVC,那么这个回路会一直绑定这个PV和PVC。另外,用户总是至少能得到它们所要求的存储,但是volume可能超过它们的请求。一旦绑定了,PVC绑定就是专属的,无论它们的绑定模式是什么。

如果没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态,一旦PV可用了,PVC就会又变成绑定状态。比如,如果一个供给了很多50G的PV集群,不会匹配要求100G的PVC。直到100G的PV添加到该集群时,PVC才会被绑定。

  1. 使用

Pod使用PVC就像使用volume一样。集群检查PVC,查找绑定的PV,并映射PV给Pod。对于支持多种访问模式的PV,用户可以指定想用的模式。一旦用户拥有了一个PVC,并且PVC被绑定,那么只要用户还需要,PV就一直属于这个用户。用户调度Pod,通过在Pod的volume块中包含PVC来访问PV。

  1. 释放

当用户使用PV完毕后,他们可以通过API来删除PVC对象。当PVC被删除后,对应的PV就被认为是已经是“released”了,但还不能再给另外一个PVC使用。前一个PVC的属于还存在于该PV中,必须根据策略来处理掉。

  1. 回收

PV的回收策略告诉集群,在PV被释放之后集群应该如何处理该PV。当前,PV可以被Retained(保留)、 Recycled(再利用)或者Deleted(删除)。保留允许手动地再次声明资源。对于支持删除操作的PV卷,删除操作会从Kubernetes中移除PV对象,还有对应的外部存储(如AWS EBS,GCE PD,Azure Disk,或者Cinder volume)。动态供给的卷总是会被删除。

  1. Recycled(再利用)

如果PV卷支持再利用,再利用会在PV卷上执行一个基础的擦除操作(rm -rf /thevolume/*),使得它可以再次被其他PVC声明利用。

管理员可以通过Kubernetes controller manager的命令行工具,来配置自定义的再利用Pod模板。自定义的再利用Pod模板必须包含PV卷的详细内容,如下示例:

apiVersion: v1
kind: Pod
metadata:
  name: pv-recycler-
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /any/path/it/will/be/replaced
  containers:
  - name: pv-recycler
    image: "gcr.io/google_containers/busybox"
    command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/*  && test -z \"$(ls -A /scrub)\" || exit 1"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

如上,在volumes部分的指定路径,应该被替换为PV卷需要再利用的路径。

5、pv类型

pv类型使用插件的形式来实现。Kubernetes现在支持以下插件:

  • GCEPersistentDisk
  • AWSElasticBlockStore
  • AzureFile
  • AzureDisk
  • FC (Fibre Channel)
  • Flocker
  • NFS
  • iSCSI
  • RBD (Ceph Block Device)
  • CephFS
  • Cinder (OpenStack block storage)
  • Glusterfs
  • VsphereVolume
  • Quobyte Volumes
  • HostPath
  • VMware Photon
  • Portworx Volumes
  • ScaleIO Volumes

四、pv 配置详解

以如下Yaml文件为例

apiVersion: v1
kind: PersistentVolume
metadata:
    name: pv0003
spec:
    capacity:
      storage: 5Gi
    accessModes:
      - ReadWriteOnce
    persistentVolumeReclaimPolicy: Recycle
    storageClassName: slow
    nfs:
      path: /tmp
      server: 172.17.0.2

1、Capacity(容量)

一般来说,PV会指定存储的容量,使用PV的capacity属性来设置。当前,存储大小是唯一能被设置或请求的资源。未来可能包含IOPS,吞吐率等属性。

2、访问模式

PV可以使用存储资源提供者支持的任何方法来映射到host中。如下所示,提供者有着不同的功能,每个PV的访问模式被设置为卷支持的指定模式。比如,NFS可以支持多个读/写的客户端,但可以在服务器上指定一个只读的NFS PV。每个PV有它自己的访问模式。

访问模式包括:

  • ReadWriteOnce — 该volume只能被单个节点以读写的方式映射
  • ReadOnlyMany — 该volume可以被多个节点以只读方式映射
  • ReadWriteMany — 该volume只能被多个节点以读写的方式映射

在CLI中,访问模式可以简写为:

  • RWO – ReadWriteOnce
  • ROX – ReadOnlyMany
  • RWX – ReadWriteMany

注意:即使volume支持很多种访问模式,但它同时只能使用一种方式来映射。比如,GCEPersistentDisk可以被单个节点映射为ReadWriteOnce,或者多个节点映射为ReadOnlyMany,但不能同时使用这两种方式来映射。

3、Class

一个PV可以有一种class,通过设置storageClassName属性来选择指定的StorageClass。有指定class的PV只能绑定给请求该class的PVC。没有设置storageClassName属性的PV只能绑定给未请求class的PVC。

过去,使用volume.beta.kubernetes.io/storage-class注解,而不是storageClassName属性。该注解现在依然可以工作,但在Kubernetes的未来版本中已经被完全弃用了。

4、回收策略

警告:Recycle回收政策已弃用。官方推荐的方法是使用动态配置。

回收策略有:

  • Retain: 手动回收

  • Recycle: 需要擦出后才能再使用

  • Delete: 相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除

当前,只有NFS和HostPath支持回收利用,AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持删除操作。

5、阶段

一个volume卷处于以下几个阶段之一:

  • Available:空闲的资源,未绑定给PVC

  • Bound: 绑定给了某个PVC

  • Released: PVC已经删除了,但是PV还没有被集群回收

  • Failed: PV在自动回收中失败了

CLI可以显示PV绑定的PVC名称。

6、映射选项

当PV被映射到一个node上时,Kubernetes管理员可以指定额外的映射选项。可以通过使用标注volume.beta.kubernetes.io/mount-options来指定PV的映射选项。

比如:

apiVersion: "v1"
kind: "PersistentVolume"
metadata:
  name: gce-disk-1
  annotations:
    volume.beta.kubernetes.io/mount-options: "discard"
spec:
  capacity:
    storage: "10Gi"
  accessModes:
    - "ReadWriteOnce"
  gcePersistentDisk:
    fsType: "ext4"
    pdName: "gce-disk-1

映射选项是当映射PV到磁盘时,一个可以被递增地添加和使用的字符串。

注意,并非所有的PV类型都支持映射选项。在Kubernetes v1.6中,以下的PV类型支持映射选项。

  • GCEPersistentDisk

  • AWSElasticBlockStore

  • AzureFile

  • AzureDisk

  • NFS

  • iSCSI

  • RBD (Ceph Block Device)

  • CephFS

  • Cinder (OpenStack block storage)

  • Glusterfs

  • VsphereVolume

  • Quobyte Volumes

  • VMware Photon

  • PersistentVolumeClaims(PVC)

五、pvc 配置详解

以下面pvc的yaml文件为例

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

1、访问模式

当请求指定访问模式的存储时,PVC使用的规则和PV相同。

2、资源

PVC就像pod一样,可以请求指定数量的资源。请求资源时,PV和PVC都使用相同的资源样式。

3、选择器(Selector)

PVC可以指定标签选择器进行更深度的过滤PV,只有匹配了选择器标签的PV才能绑定给PVC。选择器包含两个字段:

  • matchLabels(匹配标签) – PV必须有一个包含该值的标签

  • matchExpressions(匹配表达式) – 一个请求列表,包含指定的键、值的列表、关联键和值的操作符。合法的操作符包含In,NotIn,Exists和DoesNotExist。

所有来自matchLabels和matchExpressions的请求,都是逻辑与关系的,它们必须全部满足才能匹配上。

4、Class

PVC可以使用属性storageClassName来指定StorageClass的名称,从而请求指定的等级。只有满足请求等级的PV,即那些包含了和PVC相同storageClassName的PV,才能与PVC绑定。

PVC并非必须要请求一个等级。设置storageClassName为“ ”的PVC被理解为请求一个无等级的PV,因此它只能被绑定到无等级的PV(未设置对应的标注,或者设置为“”)。未设置storageClassName的PVC不太相同,DefaultStorageClass的权限插件打开与否,集群也会区别处理PVC。

如果权限插件被打开,管理员可能会指定一个默认的StorageClass。所有没有指定StorageClassName的PVC只能被绑定到默认等级的PV。要指定默认的StorageClass,需要在StorageClass对象中将标注storageclass.kubernetes.io/is-default-class设置为“true”。如果管理员没有指定这个默认值,集群对PVC创建请求的回应就和权限插件被关闭时一样。如果指定了多个默认等级,那么权限插件禁止PVC创建请求。

如果权限插件被关闭,那么就没有默认StorageClass的概念。所有没有设置StorageClassName的PVC都只能绑定到没有等级的PV。因此,没有设置StorageClassName的PVC就如同设置StorageClassName为“”的PVC一样被对待。

根据安装方法的不同,默认的StorageClass可能会在安装过程中被插件管理默认的部署在Kubernetes集群中。

当PVC指定selector来请求StorageClass时,所有请求都是与操作的。只有满足了指定等级和标签的PV才可能绑定给PVC。当前,一个非空selector的PVC不能使用PV动态供给。

过去,使用volume.beta.kubernetes.io/storage-class注解,而不是storageClassName属性。该注解现在依然可以工作,但在Kubernetes的未来版本中已经被完全弃用了。

StorageClass定义示例

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2

StorageClass对象的命名

StorageClass对象的命名是非常重要的,它是用户请求指定等级的方式。当创建StorageClass对象时,管理员设置等级的名称和其他参数,但对象不会在创建后马上就被更新。

每个StorageClass都包含字段provisioner和parameters,在所属的PV需要动态供给时使用这些字段。

Provisioner

StorageClass都有存储供应商provisioner,用来决定哪种volume插件提供给PV使用。必须制定该字段。

除了可以指定此处列出的“内部”供应商(其名称前缀为“kubernetes.io”并与Kubernetes一起分发)。还可以运行和指定外部供应商,它们是遵循Kubernetes定义的规范的独立程序。外部提供者的作者对代码的生命周期,供应商的分发方式,运行状况以及使用的卷插件(包括Flex)等都有充分的自主权。库kubernetes-incubator/external-storage存放了一个库,用于编写外部存储供应商,而这些提供者实现了大量的规范,并且是各种社区维护的。

参数

StorageClass有一些参数用于描述归属于该StorageClass的volume。不同的存储提供商需要不同的参数。比如,参数type对应的值io1,还有参数iopsPerGB,都是EBS专用的参数。当参数省略时,就会使用它的默认值。

比如:Ceph RBD

  apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    name: fast
  provisioner: kubernetes.io/rbd
  parameters:
    monitors: 10.16.153.105:6789
    adminId: kube
    adminSecretName: ceph-secret
    adminSecretNamespace: kube-system
    pool: kube
    userId: kube
    userSecretName: ceph-secret-user

monitors:Ceph的monitor,逗号分隔。该参数是必须的。

adminId: Ceph的客户端ID,可在pool中创建镜像。默认的是“admin”。

adminSecretNamespace:adminSecret的命名空间,默认值是“default”。

adminSecretName:adminId的Secret Name。该参数是必须的,提供的秘钥必须有类型“kubernetes.io/rbd”。

pool:Ceph的RBD pool,默认值是“rbd”。

userId:Ceph的客户ID,用于映射RBD镜像的,默认值和adminId参数相同。

userSecretName:Ceph Secret的名称,userId用该参数来映射RBD镜像。它必须和PVC在相同的命名空间。该参数也是必须的。提供的秘钥必须有类型“kubernetes.io/rbd”。比如,按照下面的方式来创建:

$ kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" --from-literal=key=\'QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==\' --namespace=kube-system

配置注意事项

如果你在写配置模板和示例,用于在需要持久化存储的集群中使用,那么,建议使用以下的一些模式:

在你的捆绑配置(如Deployment、ConfigMap)中包含PVC对象。

在配置中不要包含PersistentVolume对象,因为实例化配置的用户可能没有创建PersistentVolumes的权限

当用户提供实例化模板时,给用户提供存储类名称的选项。

如果用户提供了一个StorageClass名称,并且Kubernetes版本是1.4及以上,那么将该值设置在PVC的volume.beta.kubernetes.io/storage-class标注上。这会使得PVC匹配到正确的StorageClass。

如果用户没有提供StorageClass名称,或者集群版本是1.3,那么就需要在PVC配置中设置volume.alpha.kubernetes.io/storage-class: default标注。

— 这会使得在一些默认配置健全的集群中,PV可以动态的提供给用户。

— 尽管在名称中包含了alpha单词,但是该标注对应的代码有着beta级别的支持。

— 不要使用volume.beta.kubernetes.io/storage-class,无论设置什么值,甚至是空字符串。因为它会阻止DefaultStorageClass许可控制器。

在你的工具中,要监视那些一段时间后还没有获得绑定的PVC,并且展示给用户。因为这可能表明集群没有支持动态存储(此时我们应该创建匹配的PV),或者集群没有存储系统(此时用户不能部署需要PVC的情况)。

5、使用PVC

Pod通过使用PVC(使用方式和volume一样)来访问存储。PVC必须和使用它的pod在同一个命名空间,集群发现pod命名空间的PVC,根据PVC得到其后端的PV,然后PV被映射到host中,再提供给pod。

kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: dockerfile/nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

6、命名空间注意事项

PV绑定是独有的,因为PVC是命名空间对象,映射PVC时只能在同一个命名空间中使用多种模式(ROX,RWX)。

六、存储配置及使用实例

NFS

不支持动态创建持久卷,只能手工创建

先手工创建PV,再通过PV手工创建PVC,PVC就是真正可用的持久卷

PVC和PV绑定规则

​ PVC会根据自己需求空间的大小自动选择合适的PV,比如需要一个5G的PVC,PV分别为2G,7G和10G,那么PVC会自动选择7G的,但是剩余的空间不是浪费了么?原因如下:

​ 一个被绑定的PV只能用于一个PVC,他们是一对一绑定的,如果PVC大小只需要5G,但是所选的PV有7G,那么剩余的2G是没办法使用的,如果不想这样浪费空间只能使用动态创建的方式

ceph、glusterfs、云硬盘

支持动态创建持久卷

动态创建持久卷的时候也是需要手动创建PV,但是PVC是不用手工创建的,PVC会自动从PV上拿取空间

1、过程摘要

  1. 集群管理员创建由物理存储支持的PersistentVolume。管理员不会将卷与任何Pod关联。

  2. 集群用户创建PersistentVolumeClaim,它会自动绑定到合适的PersistentVolume。

  3. 用户创建一个使用PersistentVolumeClaim作为存储的Pod。

2、具体步骤

  1. 在你的节点上创建index.html文件

  2. 创建PersistentVolume

  3. 创建PersistentVolumeClaim

  4. 创建一个Pod

3、实现过程

  1. 在你的节点上创建index.html文件
# mkdir /test/pv/data
# echo \'Hello from Kubernetes storage\' > /test/pv/data/index.html
  1. 创建PersistentVolume

本练习将创建hostPath PersistentVolume。Kubernetes支持hostPath在单节点集群上进行开发和测试。hostPath PersistentVolume使用节点上的文件或目录来模拟网络附加存储。

在生产群集中,不会使用hostPath。相反,集群管理员可以配置网络资源,如Google Compute Engine永久磁盘,NFS共享或Amazon Elastic Block Store卷。群集管理员还可以使用StorageClasses 来设置动态配置。

# vim pv-volume.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/test/pv/data"

PersistentVolume 的StorageClass名称 manual,该名称将用于将PersistentVolumeClaim请求绑定到此PersistentVolume。

# kubectl  apply -f pv-volume.yaml
  1. 创建PersistentVolumeClaim
# vim pv-claim.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Mi

创建PersistentVolumeClaim之后,Kubernetes控制平面将寻找满足索赔要求的PersistentVolume。如果控制平面找到具有相同StorageClass的合适持久卷,则将声明绑定到该卷。

再次查看PersistentVolume:

kubectl get pv task-pv-volume

输出显示状态为Bound

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                   STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Bound     default/task-pv-claim   manual                   2m

查看PersistentVolumeClaim:

kubectl get pvc task-pv-claim

输出显示PersistentVolumeClaim被绑定到PersistentVolume task-pv-volume

NAME            STATUS    VOLUME           CAPACITY   ACCESSMODES   STORAGECLASS   AGE
task-pv-claim   Bound     task-pv-volume   10Gi       RWO           manual         30s
  1. 创建pod

下一步是创建一个Pod,它使用PersistentVolumeClaim作为卷。

以下是Pod的配置文件:

#vim pv-pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
       claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

注意,Pod的配置文件指定了一个PersistentVolumeClaim,但是它没有指定一个PersistentVolume。从Pod的角度来看,这个声明是一个卷。

创建pod:

# kubectl apply -f pv-pod.yaml

确认pod中的容器是否正在运行:

# kubectl get pod task-pv-pod

验证nginx是否提供了hostPath卷中的index.html文件:

此pod运行在了node1上,所以可以直接在node1*问此pod的ip地址
[root@node1 /]# curl  10.244.1.77
Hello from Kubernetes storage
  1. 访问控制

使用组ID (GID)配置的存储只允许使用相同GID的pod进行写入。不匹配或丢失GID会导致权限拒绝错误。为了减少与用户协调的需要,管理员可以使用GID向PersistentVolume添加注解。然后,GID会自动添加到使用PersistentVolume的所有Pod中。

按如下使用pv.beta.kubernetes.io/gid 注解:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv1
  annotations:
    pv.beta.kubernetes.io/gid: "1234"

当Pod使用具有GID注解的持久卷时,带注解的GID将以与Pod的安全上下文中指定的GID相同的方式应用于Pod中的所有容器。每个GID,无论是源自PersistentVolume注解还是Pod的规约,都应用于每个容器中运行的第一个进程。

七、NFS持久卷应用实例

注意:实际测试,pv容量未受限制

1、NFS安装

我这里是用NODE1提供NFS服务,生产环境要独立

# yum -y install nfs-utils rpcbind

这里是做多个NFS目录用于挂载,因为一个PVC取消一个PV的绑定之后,原来的PV还是不能被其他PVC使用的

# mkdir /data/{nfs1,nfs2,nfs3,nfs4,nfs5,nfs6,nfs7,nfs8,nfs9,nfs10} -pv && chmod 777 /data/nfs*

# vim /etc/exports   //wing拿node2(192.168.1.208)做的nfs服务,这里就写了一条带IP的,其他偷懒用的*
/data/nfs1 192.168.1.208/24(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs2 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs3 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs4 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs5 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs6 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs7 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs8 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs9 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs10 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)

• rw:read-write,可读写

• async:文件暂存于内存,而不是直接写入硬盘;

• anonuid:匿名用户的UID值

• anongid:匿名用户的GID值。注:其中anonuid=1000,anongid=1000,为此目录用户web的ID号,达到连接

NFS用户权限一致。

• insecure: 需要设置,否则挂载无权限

• no_root_squash:NFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,也

拥有root权限。显然开启这项是不安全的。

# exportfs -rv
# systemctl enable rpcbind nfs-server
# systemctl start nfs-server rpcbind
# rpcinfo -p

2、持久卷的运用

可以使用下面的explain子命令去查看学习pv的使用方法,这里不是要大家操作的,这些命令直接回车会看到帮助

# kubectl explain PersistentVolume
# kubectl explain PersistentVolume.spec
# kubectl explain PersistentVolume.spec.accessModes

使用YAML文件创建PV和PVC(如果你用的wing的yaml文件,要注意修改里面的内容)

# cat volume/nfs-pv.yml   //内容在子目录
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/nfs1
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/nfs2
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/nfs3
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/nfs4
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/nfs5
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv006
  labels:
    name: pv006
spec:
  nfs:
    path: /data/nfs6
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv007
  labels:
    name: pv007
spec:
  nfs:
    path: /data/nfs7
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv008
  labels:
    name: pv008
spec:
  nfs:
    path: /data/nfs8
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv009
  labels:
    name: pv009
spec:
  nfs:
    path: /data/nfs9
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv010
  labels:
    name: pv010
spec:
  nfs:
    path: /data/nfs10
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
    
[root@master volume]# kubectl apply -f nfs-pv.yml --record
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created
persistentvolume/pv006 created
persistentvolume/pv007 created
persistentvolume/pv008 created
persistentvolume/pv009 created
persistentvolume/pv010 created

[root@master volume]# kubectl  get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv001   1Gi        RWO,RWX        Retain           Available                                   16s
pv002   1Gi        RWO,RWX        Retain           Available                                   16s
pv003   1Gi        RWO,RWX        Retain           Available                                   16s
pv004   1Gi        RWO,RWX        Retain           Available                                   16s
pv005   1Gi        RWO,RWX        Retain           Available                                   16s
pv006   1Gi        RWO,RWX        Retain           Available                                   16s
pv007   2Gi        RWO,RWX        Retain           Available                                   16s
pv008   1Gi        RWO,RWX        Retain           Available                                   16s
pv009   1Gi        RWO,RWX        Retain           Available                                   16s
pv010   1Gi        RWO,RWX        Retain           Available                                   16s


# cat volume/nfs-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi

[root@master volume]# kubectl apply -f nfs-pvc.yml --record
persistentvolumeclaim/mypvc created
[root@master volume]# kubectl  get pvc
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Bound    pv007    2Gi        RWO,RWX                       5s

3、用nginx应用测试PVC

# cat volume/nginx-pvc.yml // 运行pod的节点必须安装 nfs-utils
apiVersion: v1
kind: Pod
metadata:
  name: nginx-vol-pvc
  namespace: default
spec:
  containers:
  - name: mywww
    image: daocloud.io/library/nginx
    volumeMounts:
    - name: www
      mountPath: /usr/share/nginx/html
  volumes:
  - name: www
    persistentVolumeClaim:
      claimName: mypvc

# kubectl apply -f volume/nginx-pvc.yml
pod/nginx-vol-pvc created

# kubectl exec -it nginx-vol-pvc -- sh
# echo "hello,world! I am wing" > /usr/share/nginx/html/index.html 
# exit

访问测试:
# curl http://PodIP:80
[root@master volume]# kubectl  get pod -o wide
NAME                        READY   STATUS      RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
nginx-vol-pvc               1/1     Running     0          91s     10.244.1.217    node1   <none>           <none>

# curl 10.244.1.217
hello,world! I am wing

也可以直接去修改NFS:
[root@node2 data]# ls
nfs1  nfs10  nfs2  nfs3  nfs4  nfs5  nfs6  nfs7  nfs8  nfs9
[root@node2 data]# cd nfs7
[root@node2 nfs7]# ls
index.html
[root@node2 nfs7]# echo also can write con to here > index.html 
[root@node2 nfs7]# curl 10.244.1.217
also can write con to here

4、删除PVC和PV

虽然可以删掉pvc把状态变成released(释放状态),但是记录状态里面还是有的,其他的PVC还是不能调用删掉的PV,这是NFS的缺点,没办法解决的

# kubectl delete -f nginx-pvc.yml 
# kubectl delete -f nfs-pv.yml

分类:

技术点:

相关文章: