写点什么

Kubernetes 数据持久化管理

作者:乌龟哥哥
  • 2023-05-07
    上海
  • 本文字数:14420 字

    阅读完需:约 47 分钟

Kubernetes 为了能更好的支持有状态应用的数据存储问题,除了基本的 HostPath 和 EmptyDir 提供的数据持久化方案之外,还提供了 PV,PVC 和 StorageClass 资源对象来对存储进行管理。

PV 的全称是 Persistent Volume(持久化卷),是对底层数据存储的抽象,PV 由管理员创建、维护以及配置,它和底层的数据存储实现方法有关,比如 Ceph,NFS,ClusterFS 等,都是通过插件机制完成和共享存储对接。

PVC 的全称是 Persistent Volume Claim(持久化卷声明),我们可以将 PV 比喻为接口,里面封装了我们底层的数据存储,PVC 就是调用接口实现数据存储操作,PVC 消耗的是 PV 的资源。

StorageClass 是为了满足用于对存储设备的不同需求,比如快速存储,慢速存储等,通过对 StorageClass 的定义,管理员就可以将存储设备定义为某种资源类型,用户根据 StorageClass 的描述可以非常直观的知道各种存储资源的具体特性,这样就可以根据应用特性去申请合适的资源了。

安装存储系统

存储系统的选择有很多,常见的有 NFS、Ceph、GlusterFS、FastDFS 等,具体使用什么根据企业情况而定。在这里使用的是 NFS,下面简单介绍一下如何安装。

(1)安装服务

$ yum install nfs-utils rpcbind -y<br>
复制代码

(2)创建共享目录

$ mkdir /data/k8s -p<br>
复制代码

(3)配置 NFS 配置文件

$ vim /etc/exports<br>/data/k8s <em>(rw,sync,no_root_squash)<br></em>
复制代码

(4)启动服务

$ systemctl start rpcbind<br>$ systemctl start nfs<br>$ systemctl <span style="color: #a6e22e;line-height: 26px;">enable</span> rpcbind<br>$ systemctl <span style="color: #a6e22e;line-height: 26px;">enable</span> nfs<br>
复制代码

(5)测试

$ showmount -e 192.168.205.128<br>Export list <span style="color: #f92672;font-weight: bold;line-height: 26px;">for</span> 192.168.205.128:<br>/data/k8s <br>
复制代码

PS:所有节点都需要安装 NFS 客户端

PV

PV(Persistent Volume)作为 Kubernetes 存储设备,可以由管理员提前配置,也可以通过 StorageClass 来动态供应。

PV 是集群资源,可以通过 kubectl explain pv 来查看如何配置,主要包括存储能力,访问模式,存储类型,回收信息等关键信息。例如:

<span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">PersistentVolume</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">my-pv01</span><br>  <span style="line-height: 26px;">labels:</span><br>    <span style="line-height: 26px;">storage:</span> <span style="color: #a6e22e;line-height: 26px;">pv</span><br><span style="line-height: 26px;">spec:</span><br>  <span style="line-height: 26px;">accessModes:</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="color: #a6e22e;line-height: 26px;">ReadWriteOnce</span><br>  <span style="line-height: 26px;">capacity:</span><br>    <span style="line-height: 26px;">storage:</span> <span style="color: #a6e22e;line-height: 26px;">1Gi</span><br>  <span style="line-height: 26px;">persistentVolumeReclaimPolicy:</span> <span style="color: #a6e22e;line-height: 26px;">Recycle</span><br>  <span style="line-height: 26px;">nfs:</span><br>    <span style="line-height: 26px;">path:</span> <span style="color: #a6e22e;line-height: 26px;">/data/k8s</span><br>    <span style="line-height: 26px;">server:</span> <span style="line-height: 26px;">192.168</span><span style="line-height: 26px;">.205</span><span style="line-height: 26px;">.128</span><br>
复制代码

参数说明:(1)、accessMode:访问模式,有 ReadWriteOnce,ReadOnlyMany,ReadWriteMany。其中:

  • ReadWriteOnce:表示具有读写权限,但是只能被一个 node 挂载一次

  • ReadOnlyMany:表示具有只读权限,可以被多个 node 多次挂载

  • ReadWriteMany:表示具有读写权限,可以被多个 node 多次挂载

(2)、capacity:持久卷资源和容量的描述,存储大小是唯一可设置或请求的资源。(3)、persistentVolumeReclaimPolicy:回收策略,也就是释放持久化卷时的策略,其有以下几种:

  • Retain:保留数据,如果要清理需要手动清理数据,默认的策略;

  • Delete:删除,将从 Kubernetes 中删除 PV 对象,以及外部基础设施中相关的存储资产,比如 AWS EBS, GCE PD, Azure Disk, 或 Cinder volume;

  • Recycle:回收,清楚 PV 中的所有数据,相当于执行 rm -rf /pv-volume/*;

创建过后,PV 的状态如下:

$ kubectl get pv<br>NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE<br>my-pv01   1Gi        RWO            Recycle          Available                                   5s<br><br>$ kubectl describe pv my-pv01 <br>Name:            my-pv01<br>Labels:          storage=pv<br>Annotations:     <none><br>Finalizers:      [kubernetes.io/pv-protection]<br>StorageClass:    <br>Status:          Available<br>Claim:           <br>Reclaim Policy:  Recycle<br>Access Modes:    RWO<br>VolumeMode:      Filesystem<br>Capacity:        1Gi<br>Node Affinity:   <none><br>Message:         <br>Source:<br>    Type:      NFS (an NFS mount that lasts the lifetime of a pod)<br>    Server:    192.168.205.128<br>    Path:      /data/k8s<br>    ReadOnly:  <span style="color: #f92672;font-weight: bold;line-height: 26px;">false</span><br>Events:        <none><br>
复制代码

当前 PV 的状态是 Available,表示处于随时可用状态。PV 总共有以下四种状态:

  • Available(可用):表示可用状态,还未被任何 PVC 绑定

  • Bound(已绑定):表示 PVC 已经被 PVC 绑定

  • Released(已释放):PVC 被删除,但是资源还未被集群重新声明

  • Failed(失败):表示该 PV 的自动回收失败

单纯的创建 PV,我们并不能直接使用,需要使用 PVC(Persistent Volume Claim)来进行声明。

PVC

PVC(Persistent Volume Claim)用于表达用户对存储的需求,申请 PVC 会消耗掉 PV 的资源,可以通过kubectl explain pvc来查看帮助文档。

在上一节我们创建了 PV,现在要申明 PVC,如下:

<span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">PersistentVolumeClaim</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">pvc-test</span><br><span style="line-height: 26px;">spec:</span><br>  <span style="line-height: 26px;">accessModes:</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="color: #a6e22e;line-height: 26px;">ReadWriteOnce</span><br>  <span style="line-height: 26px;">resources:</span><br>    <span style="line-height: 26px;">requests:</span><br>      <span style="line-height: 26px;">storage:</span> <span style="color: #a6e22e;line-height: 26px;">1Gi</span><br>
复制代码

spec 参数说明:(1)、accessModes:主要定义卷所应该拥有的访问模式(2)、resources:主要定义卷应该拥有的最小资源(3)、dataSource:定义如果提供者具有卷快照功能,就会创建卷,并将数据恢复到卷中,反之不创建(4)、selector:定义绑定卷的标签查询(5)、storageClassName:定义的 storageClass 的名字(6)、volumeMode:定义卷的类型(7)、volumeName:需要绑定的 PV 的名称链接

创建过后,查看 PV 和 PVC 的状态,如下:

$ kubectl get pvc<br>NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE<br>pvc-test   Bound    my-pv01   1Gi        RWO                           2s<br>$ kubectl get pv<br>NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE<br>my-pv01   1Gi        RWO            Recycle          Bound    default/pvc-test                           20m<br>
复制代码

我们从上面可以看到 pvc 处于 Bound 状态,Bound 的 VOLUME 是 my-pv01,我们再看 pv 的状态有 Available 变为 Bound,其 CLAIM 是 default/pvc-test,其中 default 为 namespace 名称。

在上面我们创建了一个 PVC,其绑定了我们创建的 PV,如果此时我们再创建一个 PVC,结果又会如何?我们 copy 以下上面的 PVC 文件,将其名称改一下,如下:

<span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">PersistentVolumeClaim</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">pvc-test2</span><br><span style="line-height: 26px;">spec:</span><br>  <span style="line-height: 26px;">accessModes:</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="color: #a6e22e;line-height: 26px;">ReadWriteOnce</span><br>  <span style="line-height: 26px;">resources:</span><br>    <span style="line-height: 26px;">requests:</span><br>      <span style="line-height: 26px;">storage:</span> <span style="color: #a6e22e;line-height: 26px;">1Gi</span><br>
复制代码

然后查看 PVC 的状态,如下

$ kubectl get pvc<br>NAME        STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE<br>pvc-test    Bound     my-pv01   1Gi        RWO                           3m57s<br>pvc-test2   Pending                                                      4s<br>
复制代码

我们可以看到我们刚创建的 pvc-test2 的 STATUS 处于 Pending 状态,这是由于集群里声明的 PV 都使用完了,PVC 在申请的时候没有找到合适的 PV,所以处于这个状态,这时候如果我们创建一个新的并满足要求的 PV,则可以看到这个 PVC 会处于 Bound 状态。如下:

$ kubectl get pv<br>NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM              STORAGECLASS   REASON   AGE<br>my-pv01   1Gi        RWO            Recycle          Bound       default/pvc-test                           27m<br>my-pv02   1Gi        RWO            Recycle          Available                                              5s<br>$ kubectl get pvc<br>NAME        STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE<br>pvc-test    Bound    my-pv01   1Gi        RWO                           6m50s<br>pvc-test2   Bound    my-pv02   1Gi        RWO                           2m57s<br>
复制代码

PVC 也在申领 PV 的时候也不是随意申领的,它需要符合以下要求:(1)PVC 申领的模式要和 PV 匹配上,假如 PVC 的模式是 ReadWriteOnce,而 PV 的模式是 ReadWriteMany,则申领部成功。(2)PVC 申领的容量要小于等于 PV 的容量,否则申请不成功。(3)一个 PV 只能绑定一个 PVC

另外,如果我们的 PVC 需求的容量小于 PV 的可用容量,绑定的容量是 PV 的可用容量。

StorageClass

上面介绍的 PV 和 PVC 模式是需要运维人员先创建好 PV,然后开发人员定义好 PVC 进行一对一的 Bond,但是如果 PVC 请求成千上万,那么就需要创建成千上万的 PV,对于运维人员来说维护成本很高,Kubernetes 提供一种自动创建 PV 的机制,叫 StorageClass,它的作用就是创建 PV 的模板。

具体来说,StorageClass 会定义一下两部分:

  1. PV 的属性 ,比如存储的大小、类型等;

  2. 创建这种 PV 需要使用到的存储插件,比如 Ceph 等;

有了这两部分信息,Kubernetes 就能够根据用户提交的 PVC,找到对应的 StorageClass,然后 Kubernetes 就会调用 StorageClass 声明的存储插件,创建出需要的 PV。

这里我们以 NFS 为例,要使用 NFS,我们就需要一个 nfs-client 的自动装载程序,我们称之为 Provisioner,这个程序会使用我们已经配置好的 NFS 服务器自动创建持久卷,也就是自动帮我们创建 PV。说明:

  • 自动创建的 PV 会以{pvcName}-${pvName}的目录格式放到 NFS 服务器上;

  • 如果这个 PV 被回收,则会以 archieved-{pvcName}-${pvName}这样的格式存放到 NFS 服务器上;

安装 NFS Provisioner

(1)创建 ServiceAccount,为 NFS Provisioner 授权

<span style="color: #75715e;line-height: 26px;">---</span><br><span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">ServiceAccount</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-provisioner</span><br><br><span style="color: #75715e;line-height: 26px;">---</span><br><span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">rbac.authorization.k8s.io/v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">ClusterRole</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-provisioner-clusterrole</span><br><span style="line-height: 26px;">rules:</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">apiGroups:</span> <span style="color: #a6e22e;line-height: 26px;">[""]</span><br>    <span style="line-height: 26px;">resources:</span> <span style="color: #a6e22e;line-height: 26px;">["persistentvolumes"]</span><br>    <span style="line-height: 26px;">verbs:</span> <span style="color: #a6e22e;line-height: 26px;">["get",</span> <span style="color: #a6e22e;line-height: 26px;">"list"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"watch"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"create"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"delete"</span><span style="color: #a6e22e;line-height: 26px;">]</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">apiGroups:</span> <span style="color: #a6e22e;line-height: 26px;">[""]</span><br>    <span style="line-height: 26px;">resources:</span> <span style="color: #a6e22e;line-height: 26px;">["persistentvolumeclaims"]</span><br>    <span style="line-height: 26px;">verbs:</span> <span style="color: #a6e22e;line-height: 26px;">["get",</span> <span style="color: #a6e22e;line-height: 26px;">"list"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"watch"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"update"</span><span style="color: #a6e22e;line-height: 26px;">]</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">apiGroups:</span> <span style="color: #a6e22e;line-height: 26px;">["storage.k8s.io"]</span><br>    <span style="line-height: 26px;">resources:</span> <span style="color: #a6e22e;line-height: 26px;">["storageclasses"]</span><br>    <span style="line-height: 26px;">verbs:</span> <span style="color: #a6e22e;line-height: 26px;">["get",</span> <span style="color: #a6e22e;line-height: 26px;">"list"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"watch"</span><span style="color: #a6e22e;line-height: 26px;">]</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">apiGroups:</span> <span style="color: #a6e22e;line-height: 26px;">[""]</span><br>    <span style="line-height: 26px;">resources:</span> <span style="color: #a6e22e;line-height: 26px;">["events"]</span><br>    <span style="line-height: 26px;">verbs:</span> <span style="color: #a6e22e;line-height: 26px;">["list",</span> <span style="color: #a6e22e;line-height: 26px;">"watch"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"create"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"update"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"patch"</span><span style="color: #a6e22e;line-height: 26px;">]</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">apiGroups:</span> <span style="color: #a6e22e;line-height: 26px;">[""]</span><br>    <span style="line-height: 26px;">resources:</span> <span style="color: #a6e22e;line-height: 26px;">["endpoints"]</span><br>    <span style="line-height: 26px;">verbs:</span> <span style="color: #a6e22e;line-height: 26px;">["create",</span> <span style="color: #a6e22e;line-height: 26px;">"delete"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"get"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"list"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"watch"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"patch"</span><span style="color: #a6e22e;line-height: 26px;">,</span> <span style="color: #a6e22e;line-height: 26px;">"update"</span><span style="color: #a6e22e;line-height: 26px;">]</span><br><br><span style="color: #75715e;line-height: 26px;">---</span><br><span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">rbac.authorization.k8s.io/v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">ClusterRoleBinding</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-provisioner-clusterrolebinding</span><br><span style="line-height: 26px;">subjects:</span><br><span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">ServiceAccount</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-provisioner</span><br>  <span style="line-height: 26px;">namespace:</span> <span style="color: #a6e22e;line-height: 26px;">default</span><br><span style="line-height: 26px;">roleRef:</span><br>  <span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">ClusterRole</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-provisioner-clusterrole</span><br>  <span style="line-height: 26px;">apiGroup:</span> <span style="color: #a6e22e;line-height: 26px;">rbac.authorization.k8s.io</span><br>
复制代码

(2)创建 NFS Provisioner

<span style="color: #75715e;line-height: 26px;">---</span><br><span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">apps/v1</span> <br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">Deployment</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-prosioner</span><br><span style="line-height: 26px;">spec:</span><br>  <span style="line-height: 26px;">replicas:</span> <span style="line-height: 26px;">1</span><br>  <span style="line-height: 26px;">strategy:</span><br>    <span style="line-height: 26px;">type:</span> <span style="color: #a6e22e;line-height: 26px;">Recreate</span><br>  <span style="line-height: 26px;">selector:</span><br>    <span style="line-height: 26px;">matchLabels:</span><br>      <span style="line-height: 26px;">app:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-prosioner</span><br>  <span style="line-height: 26px;">template:</span><br>    <span style="line-height: 26px;">metadata:</span><br>      <span style="line-height: 26px;">labels:</span><br>        <span style="line-height: 26px;">app:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-prosioner</span><br>    <span style="line-height: 26px;">spec:</span><br>      <span style="line-height: 26px;">serviceAccountName:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-provisioner</span><br>      <span style="line-height: 26px;">containers:</span><br>      <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-prosioner</span><br>        <span style="line-height: 26px;">image:</span> <span style="color: #a6e22e;line-height: 26px;">registry.cn-hangzhou.aliyuncs.com/rookieops/nfs-client-provisioner:4.0</span><br>        <span style="line-height: 26px;">imagePullPolicy:</span> <span style="color: #a6e22e;line-height: 26px;">IfNotPresent</span><br>        <span style="line-height: 26px;">volumeMounts:</span><br>        <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-root</span><br>          <span style="line-height: 26px;">mountPath:</span> <span style="color: #a6e22e;line-height: 26px;">/data/pv</span><br>        <span style="line-height: 26px;">env:</span><br>        <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">PROVISIONER_NAME</span><br>          <span style="line-height: 26px;">value:</span> <span style="color: #a6e22e;line-height: 26px;">rookieops/nfs</span><br>        <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">NFS_SERVER</span><br>          <span style="line-height: 26px;">value:</span> <span style="line-height: 26px;">192.168</span><span style="line-height: 26px;">.205</span><span style="line-height: 26px;">.128</span><br>        <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">NFS_PATH</span><br>          <span style="line-height: 26px;">value:</span> <span style="color: #a6e22e;line-height: 26px;">/data/k8s</span><br>      <span style="line-height: 26px;">volumes:</span><br>      <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-client-root</span><br>        <span style="line-height: 26px;">nfs:</span><br>          <span style="line-height: 26px;">server:</span> <span style="line-height: 26px;">192.168</span><span style="line-height: 26px;">.205</span><span style="line-height: 26px;">.128</span><br>          <span style="line-height: 26px;">path:</span> <span style="color: #a6e22e;line-height: 26px;">/data/k8s</span><br>
复制代码

执行完成后,查看 NFS Provisioner 的状态,如下:

$ kubectl get po<br>NAME                                    READY   STATUS    RESTARTS   AGE<br>nfs-client-prosioner-54d64dfc85-b4ht4   1/1     Running   0          10s<br>
复制代码

使用 StorageClass

上面已经创建好 NFS Provisioner,现在我们可以直接创建 StroageClass,如下:

<span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">storage.k8s.io/v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">StorageClass</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs</span><br><span style="line-height: 26px;">provisioner:</span> <span style="color: #a6e22e;line-height: 26px;">rookieops/nfs</span><br>
复制代码

每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。

在配置 StorageClass 的时候,如果没有指定 reclaimPolicy,则默认是 Delete,除此之外,还有 Retain。

StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。

使用kubectl apply -f sc.yaml创建 StorageClass,创建完成过后如下:

$ kubectl get sc<br>NAME   PROVISIONER     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE<br>nfs    rookieops/nfs   Delete          Immediate           <span style="color: #f92672;font-weight: bold;line-height: 26px;">false</span>                  9m41s<br>
复制代码

现在,我们就可以使用动态存储申领 PVC,如下:

<span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">PersistentVolumeClaim</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">pvc-from-sc</span><br><span style="line-height: 26px;">spec:</span><br>  <span style="line-height: 26px;">accessModes:</span><br>    <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="color: #a6e22e;line-height: 26px;">ReadWriteOnce</span><br>  <span style="line-height: 26px;">storageClassName:</span> <span style="color: #a6e22e;line-height: 26px;">nfs</span><br>  <span style="line-height: 26px;">resources:</span><br>    <span style="line-height: 26px;">requests:</span><br>      <span style="line-height: 26px;">storage:</span> <span style="color: #a6e22e;line-height: 26px;">1Gi</span><br>
复制代码

使用kubectl apply -f pvc-from-sc.yaml,查看 PVC 创建情况,如下:

$ kubectl get pvc<br>NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE<br>pvc-from-sc   Bound    pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a   1Gi        RWO            nfs            8s<br>$ kubectl get pv<br>NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE<br>pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a   1Gi        RWO            Delete           Bound    default/pvc-from-sc   nfs                     86s<br>
复制代码

可以看到自动创建了一个 PV,然后和 PVC 进行绑定。

为了方便使用,有时候会给集群默认设置一个 StorageClass,以便在需要使用动态存储,但是未声明的情况下使用默认的动态存储。设置方式如下:

$ kubectl patch storageclass nfs -p <span style="color: #a6e22e;line-height: 26px;">'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'</span><br>
复制代码

通过向其添加 storageclass.kubernetes.io/is-default-class 注解来将特定的 StorageClass 标记为默认。当集群中存在默认的 StorageClass 并且用户创建了一个未指定 storageClassName 的 PersistentVolumeClaim 时, DefaultStorageClass 准入控制器会自动向其中添加指向默认存储类的 storageClassName 字段。

请注意,集群上最多只能有一个 默认 存储类,否则无法创建没有明确指定 storageClassName 的 PersistentVolumeClaim。

如果要取消默认 StorageClass,只需要把注解设置为 flase 即可,如下:

<span style="color: #a6e22e;line-height: 26px;">$</span> <span style="color: #a6e22e;line-height: 26px;">kubectl</span> <span style="color: #a6e22e;line-height: 26px;">patch</span> <span style="color: #a6e22e;line-height: 26px;">storageclass</span> <span style="color: #a6e22e;line-height: 26px;">nfs</span> <span style="color: #a6e22e;line-height: 26px;">-p</span> <span style="color: #a6e22e;line-height: 26px;">'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'</span><br>
复制代码

如果我们要在 Pod 中使用 PVC,则直接如下声明:

<span style="line-height: 26px;">apiVersion:</span> <span style="color: #a6e22e;line-height: 26px;">v1</span><br><span style="line-height: 26px;">kind:</span> <span style="color: #a6e22e;line-height: 26px;">Pod</span><br><span style="line-height: 26px;">metadata:</span><br>  <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nginx</span><br><span style="line-height: 26px;">spec:</span><br>  <span style="line-height: 26px;">containers:</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nginx</span><br>    <span style="line-height: 26px;">image:</span> <span style="color: #a6e22e;line-height: 26px;">nginx</span><br>    <span style="line-height: 26px;">imagePullPolicy:</span> <span style="color: #a6e22e;line-height: 26px;">IfNotPresent</span><br>    <span style="line-height: 26px;">volumeMounts:</span><br>    <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-pvc</span><br>      <span style="line-height: 26px;">mountPath:</span> <span style="color: #a6e22e;line-height: 26px;">/mnt</span><br>  <span style="line-height: 26px;">restartPolicy:</span> <span style="color: #a6e22e;line-height: 26px;">Never</span><br>  <span style="line-height: 26px;">volumes:</span><br>  <span style="color: #a6e22e;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #a6e22e;line-height: 26px;">nfs-pvc</span><br>    <span style="line-height: 26px;">persistentVolumeClaim:</span><br>      <span style="line-height: 26px;">claimName:</span> <span style="color: #a6e22e;line-height: 26px;">pvc-from-sc</span><br>
复制代码

可以进入容器,到挂载目录输出,例如:

$ kubectl <span style="color: #a6e22e;line-height: 26px;">exec</span> -it nginx -- /bin/bash<br>root@nginx:/mnt<span style="color: #75715e;line-height: 26px;"># echo "test" > /mnt/text.txt</span><br>
复制代码

然后到 NFS 对应的目录查看是否一致。

$ <span style="color: #a6e22e;line-height: 26px;">cd</span> /data/k8s/default-pvc-from-sc-pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a<br>$ cat text.txt <br><span style="color: #a6e22e;line-height: 26px;">test</span><br>
复制代码

这表示 Pod 使用持久化成功。

总结

在 Kubernetes 中,虽然我们建议使用无状态应用,但是对于有些特殊应用,数据持久化还是必不可少的。数据持久化的难度不在于创建几个 PV 或者 PVC,而是后端的存储系统,比如 Ceph,如果使用它作为后端存储,你必须对其非常熟悉,方便在出问题的时候好排查,如果你对这些存储系统都不熟悉,在使用的时候可能会出现很多问题。

发布于: 刚刚阅读数: 4
用户头像

乌龟哥哥

关注

正在努力寻找offer的大四小菜鸟 2021-03-16 加入

擅长 Hbuilder、VS Code、MyEclipse、AppServ、PS 等软件的安装与卸载 精通 Html、CSS、JavaScript、jQuery、Java 等单词的拼写 熟悉 Windows、Linux、 等系统的开关机 看–时间过得多快,不说了,去搬砖了

评论

发布
暂无评论
Kubernetes数据持久化管理_三周年连更_乌龟哥哥_InfoQ写作社区