非易失性存储卷是由Intel推出的一种持久化内存PMEM(Persistent Memory)产品,通过经济地扩展内存容量 ,增加对持久数据的低延迟访问,从而将一流的内存和存储特性集成于一个产品中。本文介绍ACK中非易失性存储卷的使用方式和使用示例。
背景信息
PMEM是一类高性能、数据可持久、内存存储的统称。PMEM位于内存总线上,支持像DRAM一样访问数据,具备与DRAM相当的速度和延迟,而且兼具NAND闪存的非易失性。主要优势包括:
- 访问延迟低于闪存SSD的访问延迟。
- 吞吐量提高,超过闪存存储。
- 价格比DRAM便宜,节约成本。
- PMEM可缓存,解决PCIe互连不能在CPU中缓存的问题。
- 可实时访问数据,支持对大型数据集进行超高速访问。
- 断电后,数据仍保留在内存中,例如闪存。
RE6P配合第一代PMEM,RE7P配合第二代PMEM。
第一代PMEM支持的实例规格族有以下两类:
- 持久内存型实例规格族re6p,更多信息,请参见持久内存型实例规格族re6p。
- 持久内存型弹性裸金属服务器实例规格族ebmre6p,更多信息,请参见持久内存型弹性裸金属服务器实例规格族ebmre6p。
非易失性存储卷的使用方式
目前ACK集群已经支持设备的生命周期管理,通过Alibaba Cloud CSI Driver,您可以以声明式的方式对资源进行分配、挂载、使用。
- PMEM-LVM方式(无侵入的块存储使用方式)
您无需修改应用,直接声明即可使用。此方案的原则是把节点上PMEM资源统一成VolumeGroup,然后通过声明PVC类型和容量,使用PMEM-LVM。对无服务器化应用,CICD短生命周期高速临时存储,以及低延迟高吞吐的数据类应用,无需修改应用,即可实现IO性能的2~10倍的提升。更多使用示例,请参考使用非易失性存储提升读写性能。
- PMEM-直接内存(Direct Memory)访问方式
为了达到极致的,可以媲美DRAM的访问性能,对应用的内存分配部分函数做有限的修改,可以实现对PMEM设备的直接访问。对内存数据库Redis、HANA等大内存低成本需求,同时满足对TB级别的DRAM和成本的诉求,降低了内存成本30%~50%。更多使用示例,请参考使用非易失性存储卷的直接内存访问方式部署Redis数据库。
- 逻辑卷管理LVM方式:在ACK环境下,非易失性存储作为块存储或文件系统方式访问,无任何应用侵入和修改,读写性能相比SSD提升2~10倍。
- 直接内存方式:在ACK环境下,非易失性存储作为直接内存访问,需要修改应用适配PMEM SDK做内存分配的代码,达到接近内存访问的吞吐和时延。
方式 | 碎片化 | 在线扩容 | 持久化 | 应用修改 | 时延 (4K/RW) | 吞吐 (4K/RW) | 单盘最大容量(ecs.ebmre6p.26xlarge) |
---|---|---|---|---|---|---|---|
PMEM-LVM | 无 | 支持 | 是 | 否 | 10 us | 10W | 1536 GB |
PMEM-Direct | 是 | 否 | 否 | 是 | 1.2 us | 56W | 768 GB |
SSD | 无 | 支持 | 是 | 否 | 100 us | 1W | 32 TB |
插件部署
- CSI-Plugin:实现PMEM设备初始化,卷的具体创建、删除操作,实现挂载、卸载操作。
- CSI-Provisioner:感知并发起卷的创建、删除操作。
- CSI-Scheduler:容量调度 (ACK调度器内置)。
- 在节点加上标签
pmem.csi.alibabacloud.com
后才会启用设备自运维。 pmem.csi.alibabacloud.com: lvm
表示此节点使用LVM方式提供PV卷。pmem.csi.alibabacloud.com: direct
表示此节点使用内存访问方式提供PV卷。
- 创建ACK集群。ACK集群中添加PMEM类型的ECS节点实例,例如:ecs.ebmre6p.26xlarge。具体操作,请参见创建Kubernetes托管版集群。
- 配置PMEM节点类型。您所添加的节点需要配置上相应的标签,方可启用PMEM CSI的挂载能力。给节点添加以下Label。
或:pmem.csi.alibabacloud.com/type: direct
pmem.csi.alibabacloud.com/type: lvm
- 部署CSI PMEM插件。
apiVersion: storage.k8s.io/v1 kind: CSIDriver metadata: name: localplugin.csi.alibabacloud.com spec: attachRequired: false podInfoOnMount: true --- kind: DaemonSet apiVersion: apps/v1 metadata: name: csi-local-plugin namespace: kube-system spec: selector: matchLabels: app: csi-local-plugin template: metadata: labels: app: csi-local-plugin spec: tolerations: - operator: Exists serviceAccount: admin priorityClassName: system-node-critical hostNetwork: true hostPID: true containers: - name: driver-registrar image: registry.cn-hangzhou.aliyuncs.com/acs/csi-node-driver-registrar:v1.3.0-6e9fff3-aliyun imagePullPolicy: Always args: - "--v=5" - "--csi-address=/csi/csi.sock" - "--kubelet-registration-path=/var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com/csi.sock" env: - name: KUBE_NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName volumeMounts: - name: plugin-dir mountPath: /csi - name: registration-dir mountPath: /registration - name: csi-localplugin securityContext: privileged: true capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true image: registry.cn-hangzhou.aliyuncs.com/acs/csi-plugin:v1.20.6-2be29b1-aliyun imagePullPolicy: "Always" args : - "--endpoint=$(CSI_ENDPOINT)" - "--v=5" - "--nodeid=$(KUBE_NODE_NAME)" - "--driver=localplugin.csi.alibabacloud.com" env: - name: KUBE_NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - name: CSI_ENDPOINT value: unix://var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com/csi.sock volumeMounts: - name: pods-mount-dir mountPath: /var/lib/kubelet mountPropagation: "Bidirectional" - mountPath: /dev mountPropagation: "HostToContainer" name: host-dev - mountPath: /var/log/ name: host-log volumes: - name: plugin-dir hostPath: path: /var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com type: DirectoryOrCreate - name: registration-dir hostPath: path: /var/lib/kubelet/plugins_registry type: DirectoryOrCreate - name: pods-mount-dir hostPath: path: /var/lib/kubelet type: Directory - name: host-dev hostPath: path: /dev - name: host-log hostPath: path: /var/log/ updateStrategy: rollingUpdate: maxUnavailable: 10% type: RollingUpdate
kind: Deployment apiVersion: apps/v1 metadata: name: csi-local-provisioner namespace: kube-system spec: selector: matchLabels: app: csi-local-provisioner replicas: 2 template: metadata: labels: app: csi-local-provisioner spec: tolerations: - operator: "Exists" affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: node-role.kubernetes.io/master operator: Exists priorityClassName: system-node-critical serviceAccount: admin hostNetwork: true containers: - name: external-local-provisioner image: registry.cn-hangzhou.aliyuncs.com/acs/csi-provisioner:v1.6.0-b6f763a43-ack args: - "--csi-address=$(ADDRESS)" - "--feature-gates=Topology=True" - "--volume-name-prefix=disk" - "--strict-topology=true" - "--timeout=150s" - "--extra-create-metadata=true" - "--enable-leader-election=true" - "--leader-election-type=leases" - "--retry-interval-start=500ms" - "--v=5" env: - name: ADDRESS value: /socketDir/csi.sock imagePullPolicy: "Always" volumeMounts: - name: socket-dir mountPath: /socketDir - name: external-local-resizer image: registry.cn-hangzhou.aliyuncs.com/acs/csi-resizer:v0.3.0 args: - "--v=5" - "--csi-address=$(ADDRESS)" - "--leader-election" env: - name: ADDRESS value: /socketDir/csi.sock imagePullPolicy: "Always" volumeMounts: - name: socket-dir mountPath: /socketDir/ volumes: - name: socket-dir hostPath: path: /var/lib/kubelet/csi-plugins/localplugin.csi.alibabacloud.com type: DirectoryOrCreate
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: csi-pmem-direct provisioner: localplugin.csi.alibabacloud.com mountOptions: - dax parameters: volumeType: PMEM pmemType: "direct" reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: true --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: pmem-lvm provisioner: localplugin.csi.alibabacloud.com mountOptions: - dax parameters: volumeType: PMEM nodeAffinity: "true" pmemType: "lvm" reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: true
使用示例
创建块存储类型数据卷
- 使用以下内容,创建PVC。
apiVersion: v1 kind: PersistentVolumeClaim metadata: annotations: volume.kubernetes.io/selected-node: cn-zhangjiakou.192.168.XX.XX name: pmem-lvm spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: pmem-lvm
为了定向将PVC调度到某个非易失性存储卷节点,可以配置
annotations: volume.kubernetes.io/selected-node
。 - 使用以下内容,创建应用负载。
apiVersion: apps/v1 kind: StatefulSet metadata: name: sts-lvm labels: app: busybox-lvm spec: selector: matchLabels: app: busybox-lvm serviceName: "busybox" template: metadata: labels: app: busybox-lvm spec: containers: - name: busybox image: busybox command: ["sh", "-c"] args: ["sleep 10000"] volumeMounts: - name: pmem-pvc mountPath: "/data" volumes: - name: pmem-pvc persistentVolumeClaim: claimName: pmem-lvm
- 查看结果。
- 执行以下命令查看创建的PVC。
kubectl get pvc
预期输出:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pmem-lvm Bound disk-**** 10Gi RWO pmem-lvm 10m
- 执行以下命令查看创建的Pod。
kubectl get pod
预期输出:
NAME READY STATUS RESTARTS AGE sts-lvm-0 1/1 Running 0 10m
- 执行以下命令查看创建的PVC。
- 执行以下命令进入应用查看挂载路径详情。
kubectl exec -ti sts-lvm-0 -- df /data
预期输出:
Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/pmemvgregion0-disk--**** 10255636 36888 10202364 1% /data
由预期输出可见已经动态创建了一个块存储数据卷,并挂载给Pod使用。
创建直接内存类型数据卷
- 使用以下内容,创建PVC。
apiVersion: v1 kind: PersistentVolumeClaim metadata: annotations: volume.kubernetes.io/selected-node: cn-zhangjiakou.192.168.XX.XX name: pmem-direct spec: accessModes: - ReadWriteOnce resources: requests: storage: 9Gi storageClassName: pmem-direct
为了定向将PVC调度到某个非易失性存储卷节点,可以配置
annotations: volume.kubernetes.io/selected-node
。 - 使用以下内容,创建应用负载。
apiVersion: apps/v1 kind: StatefulSet metadata: name: sts-direct labels: app: busybox-direct spec: selector: matchLabels: app: busybox-direct serviceName: "busybox" template: metadata: labels: app: busybox-direct spec: containers: - name: busybox image: busybox command: ["sh", "-c"] args: ["sleep 1000"] volumeMounts: - name: pmem-pvc mountPath: "/data" volumes: - name: pmem-pvc persistentVolumeClaim: claimName: pmem-direct
- 查看结果。
- 执行以下命令查看PVC。
kubectl get pvc pmem-direct
预期输出:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pmem-direct Bound disk-**** 9Gi RWO pmem-direct 17m
- 执行以下命令查看Pod。
kubectl get pod
预期输出:
NAME READY STATUS RESTARTS AGE sts-direct-0 1/1 Running 0 17m
- 执行以下命令查看PVC。
- 执行以下命令进入应用查看挂载目录详情。
kubectl exec -ti sts-lvm-0 -- df /data
预期输出:
Filesystem 1K-blocks Used Available Use% Mounted on /dev/pmem0 9076344 36888 9023072 1% /data
由预期输出可见已经动态创建了一个PMEM数据卷,并挂载给Pod使用。