使用本地盘加速云盘性能

更新时间:
复制 MD 格式

读密集型应用(如推理服务加载模型、数据库高并发查询)的瓶颈通常不在容量,而在单块云盘的 IOPS 与时延。为云盘存储卷开启数据缓存后,热数据由本地盘承接读取,云盘保证持久化,应用无需改造即可降低读时延。

工作原理

数据缓存基于 Linux 内核的 dm-cache 机制实现,由 csi-plugin 在挂载云盘存储卷时自动完成创建,应用侧看到的仍然是一个普通的存储卷,不要求修改应用代码或挂载参数。

  • 缓存层:节点本地盘。csi-plugin 在本地盘挂载目录 /var/alibaba-cloud-csi/data-cache/ 下创建两个缓存文件:<volumeID>.data 存放被缓存的数据块,大小由 dataCacheSize 决定;<volumeID>.meta 存放数据块与云盘位置的映射关系,大小由 csi-plugin 自动确定。<volumeID> 为该存储卷对应的云盘 ID。

  • 数据主体(origin):PVC 绑定的云盘。所有数据最终都落在云盘上,本地盘不承担持久化职责。

  • 缓存设备:csi-plugin 先把两个缓存文件分别附加到 loop 设备,再以云盘为 origin 组合成一个 dm-cache 设备 /dev/mapper/csi-datacache-<volumeID>,最后格式化并挂载该设备而非裸云盘,因此 Pod 的所有 I/O 都经过本地缓存。

    读请求命中缓存时直接由本地盘返回,因此读时延和 IOPS 表现优于直接访问云盘;未命中时回源到云盘读取,并按 dm-cache 的策略填充缓存。写请求的落盘时机由缓存模式决定,两种模式的差异参见缓存模式

当节点不满足数据缓存的运行条件(例如本地盘未挂载到约定目录、节点内核不支持 dm-cache),或缓存文件创建失败时,csi-plugin 不会让 Pod 挂载失败,而是跳过缓存、直接挂载裸云盘,并在 Pod 上记录 DataCacheFallback 事件。此时业务可正常读写,只是没有本地盘加速效果。

image

适用范围

使用数据缓存需同时满足以下条件:

  • csi-plugin 版本为 v1.37.2 及以上。数据缓存是云盘 CSI 驱动的内置能力,无需额外安装插件,但低版本 csi-plugin 会忽略数据缓存参数,存储卷仍按普通云盘挂载。可在集群管理页左侧导航栏选择组件管理,查看 csi-plugin 版本并升级。

  • 节点为 Linux 系统,且内核支持 device-mapper、loop 设备与 dm-cache。缓存文件需要先通过 loop 设备接入 dm-cache,三者缺一都无法创建缓存设备。可在节点上执行以下命令逐项确认:

    # device-mapper:存在该设备文件即表示可用
    ls /dev/mapper/control
    # loop 设备:存在该设备文件即表示可用
    ls /dev/loop-control
    # dm-cache:加载内核模块并确认已加载
    modprobe dm_cache && lsmod | grep dm_cache

    三条命令均有正常输出时,节点满足内核条件。dm-cache 能否使用取决于节点镜像是否包含该内核模块,与内核版本号没有必然对应关系;modprobe dm_cache 报错说明当前节点镜像不支持,需更换节点镜像后重建节点。

  • 节点已将本地盘挂载到 /var/alibaba-cloud-csi/data-cache/。使用数据缓存的 Pod 必须调度到已完成该挂载的节点上,挂载操作参见步骤一:在节点上挂载本地盘

  • 云盘存储卷已可正常使用,即集群中已安装 csi-plugin 与 csi-provisioner 组件,云盘类型与节点实例规格匹配。云盘选型、性能与限制信息可参考云盘存储卷

规划缓存容量与缓存模式

数据缓存的容量与写策略由 StorageClass 的 dataCacheSizedataCacheMode 两个参数决定,两项取值需在创建 StorageClass 前完成规划。

重要

StorageClass 创建后参数不可修改。如需调整缓存容量或缓存模式,需新建 StorageClass 并重新创建 PVC。

缓存容量

dataCacheSize 是本地盘上分配给单个存储卷的缓存容量,同时是数据缓存的启用开关:配置非零值时启用数据缓存,不配置或配置为 0 时按普通云盘挂载。取值为 Kubernetes 数量值,如 20Gi,需小于节点本地盘在 /var/alibaba-cloud-csi/data-cache/ 下的可用容量;同一节点上所有启用缓存的存储卷共享该本地盘容量,取值过大可能导致同一节点上其他存储卷无法创建缓存。

仅配置 dataCacheMode 而未配置非零的 dataCacheSize 时,StorageClass 参数校验会失败。

缓存模式

dataCacheMode 决定写请求的落盘时机,也直接影响节点异常时数据的可恢复性:

  • writethrough(默认):每次写入都同步落到本地盘缓存与云盘后才确认,因此只加速读请求,写请求不会因缓存而变快;云盘上始终是一份完整数据。节点故障时,可将云盘解挂并重新挂载到其他节点,Pod 在新节点上继续运行(故障迁移)。适用于绝大多数生产场景,尤其是读多写少的读密集型应用。

  • writeback:写入落到本地盘缓存即确认,再异步刷入云盘,因此读写都能加速。但缓存中存在尚未刷入云盘的数据,也不具备 writethrough 那样的故障迁移能力。仅建议用于可容忍数据丢失的场景,如可重建的缓存数据或临时计算结果。

重要

writeback 模式下,正常卸载存储卷时 csi-plugin 会先把缓存中尚未刷入的数据全部写回云盘,节点重启也不会丢弃缓存内容。只有节点本地盘本身故障时,这部分数据才会丢失。写回耗时取决于待写回的数据量,数据量较大时云盘卸载会明显变慢,进而拖慢工作负载迁移到其他节点的速度。承载持久化数据的存储卷仍建议保持默认的 writethrough

配置数据缓存

步骤一需登录节点操作,每个节点只需执行一次;其余步骤通过 kubectl 在集群内完成。

步骤一:在节点上挂载本地盘

缓存文件固定创建在 /var/alibaba-cloud-csi/data-cache/ 目录下,因此需在每个使用数据缓存的节点上,把本地盘挂载到该目录。

  1. 登录节点,将本地盘格式化并挂载到 /var/alibaba-cloud-csi/data-cache/。以 ext4 文件系统为例,请将 /dev/nvme0n1 替换为实际的本地盘设备名。

    mkfs.ext4 /dev/nvme0n1
    mkdir -p /var/alibaba-cloud-csi/data-cache
    mount /dev/nvme0n1 /var/alibaba-cloud-csi/data-cache
    重要

    格式化会清空本地盘上的原有数据,执行前请确认该盘可用于缓存。如需节点重启后自动挂载,请将该挂载项写入 /etc/fstab

  2. 建议为带本地盘的节点单独建立节点池,在节点池中统一完成上述初始化,再通过节点亲和性把使用数据缓存的 Pod 约束到该节点池,避免 Pod 调度到不具备缓存条件的节点上而失去加速效果。若节点操作系统不支持 dm-cache,需更换为支持 device-mapper/dm-cache 的节点镜像后重建节点。

步骤二:创建启用数据缓存的 StorageClass

数据缓存通过 StorageClass 参数启用,因此需要单独创建一个 StorageClass,供后续 PVC 引用。

  1. 创建 disk-datacache-sc.yaml

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: alicloud-disk-datacache
    # 驱动类型,使用阿里云云盘CSI插件时固定为此值
    provisioner: diskplugin.csi.alibabacloud.com
    parameters:
      # 云盘类型
      type: cloud_essd
      # 文件系统类型
      fstype: ext4
      # 缓存容量,配置该参数即表示启用云盘数据缓存
      dataCacheSize: "20Gi"
      # 缓存模式,writethrough为默认值
      dataCacheMode: writethrough
    # 绑定模式,建议延迟绑定,确保云盘创建在Pod实际调度到的可用区
    volumeBindingMode: WaitForFirstConsumer
    # 回收策略
    reclaimPolicy: Delete
    # 允许存储卷扩容
    allowVolumeExpansion: true

    数据缓存相关参数说明如下,其余参数的含义与取值参见使用云盘动态存储卷

    参数

    类型

    必填

    默认值

    说明

    dataCacheSize

    string

    启用数据缓存时必填

    本地盘上分配给该存储卷的缓存容量,配置非零值即启用数据缓存。取值为 Kubernetes 数量值,如 20Gi。容量规划方法参见缓存容量

    dataCacheMode

    string

    writethrough

    缓存的写策略,取值为 writethroughwriteback。两者在写性能与故障迁移能力上的差异及选择依据参见缓存模式

  2. 创建 StorageClass。

    kubectl create -f disk-datacache-sc.yaml
  3. 查看 StorageClass 是否创建成功。

    kubectl get sc alicloud-disk-datacache

    预期输出如下,PROVISIONER 为云盘 CSI 插件:

    NAME                      PROVISIONER                       RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
    alicloud-disk-datacache   diskplugin.csi.alibabacloud.com   Delete          WaitForFirstConsumer   true                   10s

    也可以在集群管理页左侧导航栏选择存储 > 存储类,在列表中确认新创建的 StorageClass。

步骤三:创建 PVC 并在应用中挂载

数据缓存对应用透明,PVC 与工作负载的写法与普通云盘存储卷完全一致,只需将 storageClassName 指向上一步创建的 StorageClass。

  1. 创建 disk-datacache-pvc.yaml

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: disk-datacache-pvc
    spec:
      accessModes:
      - ReadWriteOnce
      volumeMode: Filesystem
      resources:
        requests:
          # 申请的存储容量,即云盘大小
          storage: 100Gi
      # 关联启用了数据缓存的StorageClass
      storageClassName: alicloud-disk-datacache
  2. 创建 PVC。

    kubectl create -f disk-datacache-pvc.yaml
  3. 创建 datacache-app.yaml,在应用中挂载该 PVC。

    云盘为非共享存储,未开启多挂载时一次只能被一个 Pod 挂载,因此副本数需为 1。多副本场景使用 StatefulSet,并通过 volumeClaimTemplates 为每个副本单独申请存储卷。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: datacache-app
    spec:
      # 云盘为非共享存储,副本数需为1
      replicas: 1
      selector:
        matchLabels:
          app: datacache-app
      template:
        metadata:
          labels:
            app: datacache-app
        spec:
          containers:
          - name: nginx
            image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6
            volumeMounts:
            # 挂载到容器的/data目录
            - name: datacache-volume
              mountPath: /data
          volumes:
          - name: datacache-volume
            persistentVolumeClaim:
              claimName: disk-datacache-pvc
  4. 部署应用并确认 Pod 处于 Running 状态。

    kubectl create -f datacache-app.yaml
    kubectl get pod -l app=datacache-app

步骤四:验证数据缓存已生效

Pod 正常运行只能说明云盘挂载成功,还需要确认 csi-plugin 已经创建出缓存设备,否则存储卷可能已回退为普通云盘。

  1. 查询存储卷对应的云盘 ID。缓存设备名与缓存文件名均由该 ID 派生。

    # 先查询 PVC 绑定的 PV 名称
    kubectl get pvc disk-datacache-pvc -o jsonpath='{.spec.volumeName}'
    # 再查询该 PV 对应的云盘 ID,将<pv-name>替换为上一条命令的输出
    kubectl get pv <pv-name> -o jsonpath='{.spec.csi.volumeHandle}'

    输出为形如 d-bp1xxxxxxxxxxxxxxxx 的云盘 ID,即下文的 <volumeID>

  2. 登录 Pod 所在节点,确认缓存设备已创建并查看其运行状态。将 <volumeID> 替换为上一步获取的云盘 ID。

    ls -l /dev/mapper/csi-datacache-<volumeID>
    dmsetup status csi-datacache-<volumeID>

    设备存在且 dmsetup status 有输出即表示缓存已生效。输出包含缓存块使用率、命中与未命中次数、脏块数量、I/O 模式与当前缓存策略,正常运行时策略为 mq。提示设备不存在则说明该存储卷未启用数据缓存或已回退为普通云盘。

  3. 查看节点本地盘上的缓存文件。

    ls -lh /var/alibaba-cloud-csi/data-cache/

    正常情况下可以看到 <volumeID>.data<volumeID>.meta 两个文件,其中 .data 的大小与 dataCacheSize 一致。

  4. 查看是否存在缓存回退事件。

    kubectl get events --field-selector reason=DataCacheFallback

    无输出说明未发生回退。若输出中存在 DataCacheFallback 事件,说明节点不满足数据缓存条件,csi-plugin 已跳过缓存直接挂载云盘,需按适用范围逐项核对节点状态。

扩容云盘存储卷

启用数据缓存的存储卷支持云盘扩容,即在 StorageClass 中配置 allowVolumeExpansion: true 后,通过修改 PVC 申请的容量扩容底层云盘,操作方式与普通云盘存储卷一致。扩容时 csi-plugin 会先扩容云盘,再扩展 dm-cache 设备以匹配新容量,最后扩容文件系统,无需手动干预。本地盘上的缓存容量由 dataCacheSize 决定,不随云盘容量自动变化。

使用限制

  • 云盘本身的限制仍然适用:未开启多挂载的云盘只能同时被一个 Pod 挂载,且不支持跨可用区挂载。

  • 数据缓存仅在满足适用范围全部条件的节点上生效,不满足时存储卷回退为普通云盘。

  • 数据缓存主要加速读路径,仅在 writeback 模式下写请求也会加速。对时延不敏感的负载,或一次写入、几乎不重复读取的负载,开启后收益有限。

  • 缓存是节点本地的。Pod 连同存储卷漂移到其他节点后,原节点的缓存文件在卸载时清理,新节点需重新建立并预热缓存,此期间读性能与普通云盘一致。

  • StorageClass 创建后 dataCacheSizedataCacheMode 不可修改,取值需在创建前按规划缓存容量与缓存模式确定。

计费说明

数据缓存不改变云盘的使用方式,作为存储卷的云盘仍采用按量付费。云盘价格可参考块存储价格。本地盘随节点实例规格提供,不单独计费。

常见问题

Pod 正常运行,但找不到缓存设备,怎么办?

说明该存储卷未启用缓存或已回退。先执行 kubectl get events --field-selector reason=DataCacheFallback 确认是否存在回退事件,再按以下顺序排查:StorageClass 中是否配置了 dataCacheSize;PVC 是否引用了该 StorageClass;节点 csi-plugin 版本是否为 v1.37.2 及以上;节点是否已将本地盘挂载到 /var/alibaba-cloud-csi/data-cache/;该目录下的剩余空间是否不小于 dataCacheSize

节点没有挂载本地盘,会影响业务吗?

不会。csi-plugin 在无法建立缓存时会跳过缓存、直接挂载云盘,业务读写正常,只是失去本地盘加速效果,并记录 DataCacheFallback 事件。如需获得加速效果,按步骤一:在节点上挂载本地盘完成挂载,并通过节点亲和性把 Pod 约束到这类节点。

节点操作系统不支持 dm-cache 怎么办?

数据缓存依赖节点内核的 dm-cache 模块,不支持时同样会回退为普通云盘。更换为支持 device-mapper/dm-cache 的节点镜像后重建节点,再重新调度使用该存储卷的 Pod。

可以把 dataCacheMode 改成 writeback 来提升写性能吗?

可以,但 dataCacheMode 在 StorageClass 创建后不可修改,需新建配置为 writeback 的 StorageClass 并重新创建 PVC。该模式下缓存中可能存在尚未刷入云盘的数据,节点本地盘故障时这部分数据会丢失,取舍依据参见缓存模式。承载持久化数据的存储卷建议保持默认的 writethrough

相关文档