tidb-in-action

1.2.3.2.2 Kubernetes 上的持久化存储配置

1. 背景介绍

TiDB 分布式数据库中 PD、TiKV、监控等组件以及 TiDB Binlog 和备份等工具都需要使用将数据持久化的存储。Kubernetes 上的数据持久化需要使用 PersistentVolume (PV)。Kubernetes 提供多种存储类型,主要分为两大类:

PV 一般由系统管理员或 volume provisioner 自动创建,PV 与 Pod 是通过 PersistentVolumeClaim (PVC) 进行关联的。普通用户在使用 PV 时并不需要直接创建 PV,而是通过 PVC 来申请使用 PV,对应的 volume provisioner 根据 PVC 创建符合要求的 PV,并将 PVC 与该 PV 进行绑定。

警告:

为了数据安全,任何情况下都不要直接删除 PV,除非对 volume provisioner 原理非常清楚。

2. TiDB 分布式数据库推荐存储类型

TiKV 自身借助 Raft 实现了数据复制,出现节点故障后,PD 会自动进行数据调度补齐缺失的数据副本,同时 TiKV 要求存储有较低的读写延迟,所以生产环境强烈推荐使用本地 SSD 存储。

PD 同样借助 Raft 实现了数据复制,但作为存储集群元信息的数据库,并不是 IO 密集型应用,所以一般本地普通 SAS 盘或网络 SSD 存储(例如 AWS 上 gp2 类型的 EBS 存储卷,GCP 上的持久化 SSD 盘)就可以满足要求。

监控组件以及 TiDB Binlog、备份等工具,由于自身没有做多副本冗余,所以为保证可用性,推荐用网络存储。其中 TiDB Binlog 的 pump 和 drainer 组件属于 IO 密集型应用,需要较低的读写延迟,所以推荐用高性能的网络存储(例如 AWS 上的 io1 类型的 EBS 存储卷,GCP 上的持久化 SSD 盘)。

在利用 TiDB Operator 部署 TiDB 分布式数据库或者备份工具的时候,需要持久化存储的组件都可以通过 values.yaml 配置文件中对应的 storageClassName 设置存储类型。不设置时默认都使用 local-storage

3. 网络 PV 配置

Kubernetes 1.11 及以上的版本支持网络 PV 的动态扩容,但用户需要为相应的 StorageClass 开启动态扩容支持。

kubectl patch storageclass <storage-class-name> -p '{"allowVolumeExpansion": true}'

开启动态扩容后,通过下面方式对 PV 进行扩容:

(1) 修改 PVC 大小

假设之前 PVC 大小是 10 Gi,现在需要扩容到 100 Gi

```shell
kubectl patch pvc -n <namespace> <pvc-name> -p '{"spec": {"resources": {"requests": {"storage": "100Gi"}}}'
```

(2) 查看 PV 扩容成功

扩容成功后,通过 `kubectl get pvc -n <namespace> <pvc-name>` 显示的大小仍然是初始大小,但查看 PV 大小会显示已经扩容到预期的大小。

```shell
kubectl get pv | grep <pvc-name>
```

4. 本地 PV 配置

Kubernetes 当前支持静态分配的本地存储。可使用 local-static-provisioner 项目中的 local-volume-provisioner 程序创建本地存储对象。创建流程如下:

(1) 参考 Kubernetes 提供的操作文档,在集群节点中预分配本地存储。

(2) 部署 local-volume-provisioner 程序。

```shell
kubectl apply -f https://raw.githubusercontent.com/pingcap/tidb-operator/master/manifests/local-dind/local-volume-provisioner.yaml
```

通过下面命令查看 Pod 和 PV 状态:

```shell
kubectl get po -n kube-system -l app=local-volume-provisioner && \
kubectl get pv | grep local-storage
```

`local-volume-provisioner` 会为发现目录 (discovery directory) 下的每一个挂载点创建一个 PV。注意,在 GKE 上,默认只能创建大小为 375 GiB 的本地卷。

更多信息,可参阅 Kubernetes 本地存储local-static-provisioner 文档

最佳实践

更多信息,可参阅 local-static-provisioner 的最佳实践文档

示例

如果监控、TiDB Binlog、备份等组件都使用本地盘存储数据,可以挂载普通 SAS 盘,并分别创建不同的 StorageClass 供这些组件使用,具体操作如下:

盘挂载完成后,需要根据上述磁盘挂载情况修改 local-volume-provisioner yaml 文件,配置发现目录并创建必要的 StorageClass。以下是根据上述挂载修改的 yaml 文件示例:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: "local-storage"
provisioner: "kubernetes.io/no-provisioner"
volumeBindingMode: "WaitForFirstConsumer"
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: "ssd-storage"
provisioner: "kubernetes.io/no-provisioner"
volumeBindingMode: "WaitForFirstConsumer"
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: "shared-ssd-storage"
provisioner: "kubernetes.io/no-provisioner"
volumeBindingMode: "WaitForFirstConsumer"
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: "backup-storage"
provisioner: "kubernetes.io/no-provisioner"
volumeBindingMode: "WaitForFirstConsumer"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: local-provisioner-config
  namespace: kube-system
data:
  nodeLabelsForPV: |
    - kubernetes.io/hostname
  storageClassMap: |
    shared-ssd-storage:
      hostDir: /mnt/sharedssd
      mountDir: /mnt/sharedssd
    ssd-storage:
      hostDir: /mnt/ssd
      mountDir: /mnt/ssd
    local-storage:
      hostDir: /mnt/disks
      mountDir: /mnt/disks
    backup-storage:
      hostDir: /mnt/backup
      mountDir: /mnt/backup
---

......

          volumeMounts:

            ......

            - mountPath: /mnt/ssd
              name: local-ssd
              mountPropagation: "HostToContainer"
            - mountPath: /mnt/sharedssd
              name: local-sharedssd
              mountPropagation: "HostToContainer"
            - mountPath: /mnt/disks
              name: local-disks
              mountPropagation: "HostToContainer"
            - mountPath: /mnt/backup
              name: local-backup
              mountPropagation: "HostToContainer"
      volumes:

        ......

        - name: local-ssd
          hostPath:
            path: /mnt/ssd
        - name: local-sharedssd
          hostPath:
            path: /mnt/sharedssd
        - name: local-disks
          hostPath:
            path: /mnt/disks
        - name: local-backup
          hostPath:
            path: /mnt/backup
......

最后通过 kubectl apply 命令部署 local-volume-provisioner 程序。

kubectl apply -f https://raw.githubusercontent.com/pingcap/tidb-operator/master/manifests/local-dind/local-volume-provisioner.yaml

后续创建 TiDB 集群或备份等组件的时候,再配置相应的 StorageClass 供其使用。

5. 数据安全

一般情况下 PVC 在使用完删除后,与其绑定的 PV 会被 provisioner 清理回收再放入资源池中被调度使用。为避免数据意外丢失,可在全局配置 StorageClass 的回收策略 (reclaim policy) 为 Retain 或者只将某个 PV 的回收策略修改为 RetainRetain 模式下,PV 不会自动被回收。

注意:

TiDB Operator 默认会自动将 PD 和 TiKV 的 PV 保留策略修改为 Retain 以确保数据安全。

PV 保留策略是 Retain 时,如果确认某个 PV 的数据可以被删除,需要通过下面的操作来删除 PV 以及对应的数据:

(1) 删除 PV 对应的 PVC 对象:

```shell
kubectl delete pvc <pvc-name> --namespace=<namespace>
```

(2) 设置 PV 的保留策略为 Delete,PV 会被自动删除并回收:

```shell
kubectl patch pv <pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'
```

要了解更多关于 PV 的保留策略可参考修改 PV 保留策略