乐趣区

关于云计算:Kubernetes-跨-StorageClass-迁移-Persistent-Volumes-完全指南

大家好,我是米开朗基杨。

KubeSphere 3.3.0(不出意外的话~)本周就要 GA 了,作为一名 KubeSphere 脑残粉,我急不可待地先装置 RC 版尝尝鲜,一顿操作猛如虎开启所有组件,装完之后发现有点难堪:我用错了长久化存储。

我的 K8s 集群中有两个存储类(StorageClass),一个是 OpenEBS 提供的本地存储,另一个是 QingCloud CSI 提供的分布式存储,而且默认的 StorageClass 是 OpenEBS 提供的 local-hostpath,所以 KubeSphere 的有状态组件默认便应用本地存储来保留数据。

失误失误,我原本是想用分布式存储作为默认存储的,然而我遗记将 csi-qingcloud 设置为默认的 StorageClass 了,反正不管怎样,就这么稀里糊涂地搞错了。尽管重装能够解决 99% 的问题,但作为一名成熟的 YAML 工程师,重装是不可能的,必须在不重装的状况下解决这个问题,能力体现出我的气质!

事实上不止我一个人遇到过这种状况,很多人都会稀里糊涂地装完一整套产品之后发现 StorageClass 用错了,这时候再想改回去恐怕就没那么容易了。这不巧了么这不是,本文就是来帮忙大家解决这个问题的。

思路

咱们先来思考一下换 StorageClass 须要做哪几件事件。首先须要将利用的正本数缩减为 0,而后创立一个新的 PVC,将旧 PV 的数据复制到新 PV,而后让利用应用新的 PV,并将正本扩大到原来的数量,最初再将旧 PV 删除。在这整个过程中还要避免删除 PVC 时 Kubernetes 将 PV 也删除了。

当然,有些 CSI 驱动或者存储后端可能会有更便当的数据迁徙技巧,然而本文提供的是一种更加通用的计划,不论后端是什么存储都能够。

KubeSphere 3.3.0 开启所有组件之后应用的长久卷申明(PVC)如下:

本文就以 Elasticsearch 为例,演示如何将 Elasticsearch 的存储从本地存储替换为分布式存储。

备份 PVC 和 PV

首先第一步就是备份 PVC 和 PV,万一前面操作失败了,还有反悔的余地。

$ kubectl -n kubesphere-logging-system get pvc
NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS     AGE
data-elasticsearch-logging-data-0        Bound    pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9   20Gi       RWO            local-hostpath   28h
data-elasticsearch-logging-data-1        Bound    pvc-0851350a-270e-4d4d-af8d-081132c1775b   20Gi       RWO            local-hostpath   28h
data-elasticsearch-logging-discovery-0   Bound    pvc-8f32fc97-3d6e-471a-8121-655991d945a8   4Gi        RWO            local-hostpath   28h

$ kubectl -n kubesphere-logging-system get pv pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9 -o yaml > pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9.yaml
$ kubectl -n kubesphere-logging-system get pv pvc-0851350a-270e-4d4d-af8d-081132c1775b -o yaml > pvc-0851350a-270e-4d4d-af8d-081132c1775b.yaml

$ kubectl -n kubesphere-logging-system get pvc data-elasticsearch-logging-data-0 -o yaml > data-elasticsearch-logging-data-0.yaml
$ kubectl -n kubesphere-logging-system get pvc data-elasticsearch-logging-data-1 -o yaml > data-elasticsearch-logging-data-1.yaml

复制数据

不论 PV 的 accessModes 是 ReadWriteOnce 还是 ReadWriteMany,在复制数据之前都要将利用的正本数量缩减为 0,因为 ReadWriteOne 模式同时只容许挂载一个 Pod,新 Pod 无奈挂载,而 ReadWriteMany 模式如果不将正本数量缩减为 0,在复制数据时可能会有新的数据写入。所以无论如何,都要将正本数量缩为 0。

$ kubectl -n kubesphere-logging-system get sts
NAME                              READY   AGE
elasticsearch-logging-data        2/2     28h
elasticsearch-logging-discovery   1/1     28h

$ kubectl -n kubesphere-logging-system scale sts elasticsearch-logging-data --replicas=0

$ kubectl -n kubesphere-logging-system get sts
NAME                              READY   AGE
elasticsearch-logging-data        0/0     28h
elasticsearch-logging-discovery   1/1     28h

创立一个新的 PVC 叫 new-data-elasticsearch-logging-data-0,容量和 data-elasticsearch-logging-data-0 一样,并将 storageClassName 指定为新的 StorageClass。

创立一个 Deployment,将新 PV 和旧 PV 都挂载进去,而后再将旧 PV 的数据拷贝到新 PV。

在『工作负载』界面点击『创立』,将上面的 YAML 粘贴进去即可。

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: kubesphere-logging-system
  labels:
    app: datacopy
  name: datacopy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: datacopy
  template:
    metadata:
      labels:
        app: datacopy
    spec:
      containers:
        - name: datacopy
          image: ubuntu
          command:
            - 'sleep'
          args:
            - infinity
          volumeMounts:
            - name: old-pv
              readOnly: false
              mountPath: /mnt/old
            - name: new-pv
              readOnly: false
              mountPath: /mnt/new
      volumes:
        - name: old-pv
          persistentVolumeClaim:
            claimName: data-elasticsearch-logging-data-0
        - name: new-pv
          persistentVolumeClaim:
            claimName: new-data-elasticsearch-logging-data-0

这个 Deployment 将新 PV 和旧 PV 都挂载进去了,稍后咱们会将旧 PV 的数据拷贝到新 PV。

Pod 启动胜利后,点击容器的终端图标进入容器的终端。

在容器中先验证旧 PV 的挂载点是否蕴含利用数据,新 PV 的挂载点是否是空的,之后再执行命令 (cd /mnt/old; tar -cf - .) | (cd /mnt/new; tar -xpf -),以确保所有数据的所有权和权限被继承。

执行实现后,验证新 PV 的挂载点是否蕴含旧 PV 的数据,以及所有权和权限是否被正确继承。

到这里复制数据的工作就实现了,当初咱们须要将 datacopy 的正本数量缩为 0。

迁徙 PVC

迁徙存储的现实状态是应用旧的 PVC,并将其指向新的 PV,这样工作负载的 YAML 配置清单就不须要做任何扭转。但 PVC 和 PV 之间的绑定关系是不可更改的,要想让它们解绑,必须先删除旧的 PVC,再创立同名的 PVC,并将旧的 PV 与它绑定。

须要留神的是,默认状况下 PV 的回收策略是 Delete,一旦删除 PVC,与之绑定的 PV 和 PV 里的数据都会被删除。这是咱们不心愿看到的,所以咱们须要批改回收策略,以便删除 PVC 时 PV 可能保留下来。

事实上能够通过 StorageClass 来设置全局的回收策略(reclaimPolicy),如果不设置,默认就是 Delete。能够通过命令 kubectl describe pv <pv-name> 来查看 PV 的回收策略(reclaimPolicy):

$ kubectl describe pv pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9
Name:              pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9
Labels:            openebs.io/cas-type=local-hostpath
Annotations:       pv.kubernetes.io/provisioned-by: openebs.io/local
Finalizers:        [kubernetes.io/pv-protection]
StorageClass:      local-hostpath
Status:            Bound
Claim:             kubesphere-logging-system/data-elasticsearch-logging-data-0
Reclaim Policy:    Delete
...

$ kubectl describe pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
Name:              pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
Labels:            <none>
Annotations:       pv.kubernetes.io/provisioned-by: disk.csi.qingcloud.com
Finalizers:        [kubernetes.io/pv-protection external-attacher/disk-csi-qingcloud-com]
StorageClass:      csi-qingcloud
Status:            Bound
Claim:             kubesphere-logging-system/new-data-elasticsearch-logging-data-0
Reclaim Policy:    Delete
...

咱们能够通过 patch 命令将新旧 PV 的回收策略设置为 Retain

$ kubectl patch pv pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9 -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
persistentvolume/pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9 patched

$ kubectl patch pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
persistentvolume/pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9 patched

⚠️留神:该命令对 PV 的稳定性和可用性没有任何影响,能够随时执行。

当初能够将新旧 PVC 全副删除,PV 不会受到任何影响。

$ kubectl -n kubesphere-logging-system delete pvc data-elasticsearch-logging-data-0 new-data-elasticsearch-logging-data-0
persistentvolumeclaim "data-elasticsearch-logging-data-0" deleted
persistentvolumeclaim "new-data-elasticsearch-logging-data-0" deleted

在创立最终的 PVC 之前,咱们必须要确保新创建的 PVC 可能被绑定到新的 PV 上。通过以下命令能够看到新 PV 目前处于开释状态,不能被新 PVC 绑定:

$ kubectl describe pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
Name:              pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
Labels:            <none>
Annotations:       pv.kubernetes.io/provisioned-by: disk.csi.qingcloud.com
Finalizers:        [kubernetes.io/pv-protection external-attacher/disk-csi-qingcloud-com]
StorageClass:      csi-qingcloud
Status:            Released
Claim:             kubesphere-logging-system/new-data-elasticsearch-logging-data-0
Reclaim Policy:    Retain
Access Modes:      RWO
VolumeMode:        Filesystem
Capacity:          20Gi
...

这是因为 PV 在 spec.claimRef 中依然援用了曾经被删除的 PVC:

$ kubectl get pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e -o yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  ...
  name: pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
  ...
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 20Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: new-data-elasticsearch-logging-data-0
    namespace: kubesphere-logging-system
    resourceVersion: "657019"
    uid: f4e96f69-b3be-4afe-bb52-1e8e728ca55e
  ...
  persistentVolumeReclaimPolicy: Retain
  storageClassName: csi-qingcloud
  volumeMode: Filesystem

为了解决这个问题,能够间接通过命令 kubectl edit pv <pv-name> 编辑 PV,将 claimRef 的内容全副删除。而后再查看 PV 曾经处于可用状态(Available):

$ kubectl describe pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
Name:              pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
Labels:            <none>
Annotations:       pv.kubernetes.io/provisioned-by: disk.csi.qingcloud.com
Finalizers:        [kubernetes.io/pv-protection external-attacher/disk-csi-qingcloud-com]
StorageClass:      csi-qingcloud
Status:            Available
Claim:
Reclaim Policy:    Retain
Access Modes:      RWO
VolumeMode:        Filesystem
Capacity:          20Gi

最终咱们须要创立与旧 PVC 同名的新 PVC,而且要尽可能保障与旧 PVC 的参数雷同:

  • 新 PVC 的名字和旧 PVC 的名字雷同;
  • spec.volumeName 指向新 PV;
  • 新 PVC 的 metadata.annotationsmetadata.labels 和旧 PVC 保留雷同,因为这些值可能会影响到利用部署(比方 Helm chart 等)。

最终 PVC 内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app: elasticsearch
    component: data
    release: elasticsearch-logging
    role: data
  name: data-elasticsearch-logging-data-0
  namespace: kubesphere-logging-system
spec:
  storageClassName: csi-qingcloud
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  volumeMode: Filesystem
  volumeName: pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e

在『存储卷申明』页面点击『创立』:

抉择『编辑 YAML』,将下面的 YAML 内容复制粘贴进去,而后点击『创立』:

最终能够看到新的 PVC 和 PV 全部都是 Bound 状态:

$ kubectl -n kubesphere-logging-system get pvc data-elasticsearch-logging-data-0
NAME                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS    AGE
data-elasticsearch-logging-data-0   Bound    pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e   20Gi       RWO            csi-qingcloud   64s

$ kubectl get pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                                         STORAGECLASS    REASON   AGE
pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e   20Gi       RWO            Retain           Bound    kubesphere-logging-system/data-elasticsearch-logging-data-0   csi-qingcloud            11h

再来一遍

到目前为止,咱们只迁徙了 data-elasticsearch-logging-data-0 的数据,对于 data-elasticsearch-logging-data-1,依照下面的步骤再反复一遍就行了,记得将 datacopy 中的 PVC 改为 data-elasticsearch-logging-data-1new-data-elasticsearch-logging-data-0,其余中央的配置内容也要批改为新的。

复原工作负载

当初所有的存储都迁徙实现,PVC 名称放弃不变,PV 应用的是新的存储。

$ kubectl get pv -A|grep elasticsearch-logging-data
pvc-0851350a-270e-4d4d-af8d-081132c1775b   20Gi       RWO            Retain           Released   kubesphere-logging-system/data-elasticsearch-logging-data-1        local-hostpath            40h
pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9   20Gi       RWO            Retain           Released   kubesphere-logging-system/data-elasticsearch-logging-data-0        local-hostpath            40h
pvc-d0acd2e7-ee1d-47cf-8506-69147fe25563   20Gi       RWO            Retain           Bound      kubesphere-logging-system/data-elasticsearch-logging-data-1        csi-qingcloud             9m53s
pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e   20Gi       RWO            Retain           Bound      kubesphere-logging-system/data-elasticsearch-logging-data-0        csi-qingcloud             11h

$ kubectl -n kubesphere-logging-system get pvc|grep elasticsearch-logging-data
data-elasticsearch-logging-data-0        Bound    pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e   20Gi       RWO            csi-qingcloud    27m
data-elasticsearch-logging-data-1        Bound    pvc-d0acd2e7-ee1d-47cf-8506-69147fe25563   20Gi       RWO            csi-qingcloud    3m49s

将工作负载的正本复原到之前的数量:

$ kubectl -n kubesphere-logging-system scale sts elasticsearch-logging-data --replicas=2
statefulset.apps/elasticsearch-logging-data scaled

$ kubectl -n kubesphere-logging-system get pod -l app=elasticsearch,component=data
NAME                           READY   STATUS    RESTARTS   AGE
elasticsearch-logging-data-0   1/1     Running   0          4m12s
elasticsearch-logging-data-1   1/1     Running   0          3m42s

完满!

最初还有一点收尾工作,咱们须要将所有新 PV 的回收策略从新设置为 Delete

$ kubectl patch pv pvc-d0acd2e7-ee1d-47cf-8506-69147fe25563 -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'
persistentvolume/pvc-d0acd2e7-ee1d-47cf-8506-69147fe25563 patched

$ kubectl patch pv pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'
persistentvolume/pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e patched

$ kubectl get pv -A|grep elasticsearch-logging-data
pvc-0851350a-270e-4d4d-af8d-081132c1775b   20Gi       RWO            Retain           Released   kubesphere-logging-system/data-elasticsearch-logging-data-1        local-hostpath            40h
pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9   20Gi       RWO            Retain           Released   kubesphere-logging-system/data-elasticsearch-logging-data-0        local-hostpath            40h
pvc-d0acd2e7-ee1d-47cf-8506-69147fe25563   20Gi       RWO            Delete           Bound      kubesphere-logging-system/data-elasticsearch-logging-data-1        csi-qingcloud             15m
pvc-f4e96f69-b3be-4afe-bb52-1e8e728ca55e   20Gi       RWO            Delete           Bound      kubesphere-logging-system/data-elasticsearch-logging-data-0        csi-qingcloud             11h

最初的最初,就能够将旧 PV 全副删除了:

$ kubectl delete pv pvc-0851350a-270e-4d4d-af8d-081132c1775b
persistentvolume "pvc-0851350a-270e-4d4d-af8d-081132c1775b" deleted

$ kubectl delete pv pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9
persistentvolume "pvc-9aed3d1b-09a6-4fe3-8adc-9195a2bbb2b9" deleted

更简略的计划

下面的计划尽管完满解决了问题,但步骤比拟繁琐,有没有更简洁的办法呢?

能够试试青云推出的云原生备份容灾 SaaS 服务,无需部署、保护本地备份基础架构,即可轻松实现多云异构环境下数据的自在迁徙,从而实现多地、按需的数据保护与利用的高可用。而且价格比拟亲民,对白嫖党敌对,提供了 100GB 的收费存储,迁徙几个 PV 齐全够用了。

应用起来非常简单,先注册账户,而后导入 Kubernetes 集群。如果抉择通过代理连贯 Kubernetes 集群,须要执行红色方框内命令在 Kubernetes 集群中装置代理。

而后新建托管仓库。

接下来间接创立备份打算,抉择 间接复制

备份胜利之后,将集群中的 PVC 和 PV 删除,并将工作负载的正本数缩减为 0。最初创立复原打算,留神将源存储类型名称为 local-hostpath 的指标存储类型名称设置为你想迁徙的存储,这样复原后的 PV 应用的就是新的 StorageClass。

完了。

总结

本文介绍了如何将 Kubernetes 集群中现有 PV 的数据迁徙到新的 PV,并创立同名的 PVC 来指向新的 PV,这样就实现了利用的数据迁徙而不须要对利用的配置清单做任何更改。最初还介绍了如何通过云原生备份容灾 SaaS 服务来简化迁徙过程。

本文由博客一文多发平台 OpenWrite 公布!

退出移动版