You need to enable JavaScript to run this app.
导航
云盘持久化存储最佳实践
最近更新时间:2024.07.11 10:52:52首次发布时间:2022.04.12 10:10:07

本文以动态创建存储卷方式为例,介绍如何通过容器服务 VKE 实现云盘持久化存储。

应用场景

通常 Deployment 用于部署无状态服务,StatefulSet 用于部署有状态服务。本节内容主要针对有状态服务挂载块存储实现数据持久化存储。有状态负载 StatefulSet 的应用场景如下:

  • 稳定的部署次序:有序部署或扩展,需要根据定义的顺序依次进行,即从 0 到 N,在下一个 Pod 运行之前,所有之前的 Pod 必须都是 Running 和 Ready 状态。
  • 稳定的缩容次序:有序收缩或删除,需要根据定义的顺序依次进行,即从 N 到 0,在下一个 Pod 执行结束之前 ,所有之前的 Pod 都需要删除完成。
  • 稳定的身份 ID:Pod 重新调度后其 PodName 和 HostName 不变。
  • 持久化的存储:基于 PVC,Pod 重新调度后仍能访问到相同的持久化数据。

StatefulSet 对应 Pod 的存储需要通过 StorageClass 来动态创建。每个 Pod 都会根据 StatefulSet 中定义的 VolumeClaimTemplate 来创建一个对应的 PVC,然后 PVC 通过 StorageClass 自动创建对应的 PV,并挂载给 Pod。因此,需要首先创建 StorageClass,即采用动态创建模式。

云盘类型

云盘又称弹性块存储,根据用途可分为系统盘和数据盘。

  • 系统盘:用于存储操作系统,只能随实例创建,生命周期与挂载的云服务器实例相同。
  • 数据盘:用于存储应用数据,适用于 I/O 密集型应用、关系型数据库、NoSQL 数据库、开发测试等场景。您可以在创建云服务器实例时挂载,也可以创建实例后挂载。

    说明

    云盘规格的详细说明,请参见 云盘规格

使用限制

  • 云盘只能挂载到同一可用区的云服务器实例上,创建后不支持更换可用区,请您创建时谨慎选择。
  • 单台云服务器存在挂载云盘数量的限制,单块云盘根据类型不同存在不同的容量限制和性能水平,请结合您的业务需求合理选择实例规格和云盘类型。
    限制项限制说明
    单台云服务器可挂载的最大数量不同计算规格可挂载的云盘数量有所不同,详细请参考 实例规格介绍
    单块系统盘的容量极速型 SSD:40GiB~500GiB
    单块数据盘的容量极速型 SSD:20GiB~32768GiB
    本地盘云服务器是否支持自定义挂载新本地盘不支持,本地盘随云服务器创建和销毁。

    单个云盘类型的最大使用量

    对于单个云盘类型,每个火山引擎账号在每个地域最大使用 64TiB 容量。
    如果您需要提升配额,您可以 提交工单

存储卷

本章为您简单介绍,创建存储卷涉及的资源类型以及动态存储卷和静态存储卷的差异。

  • PVC:即存储声明,代表 Kubernetes 中一种抽象的存储卷类型,定义某个具体类型存储的数据卷表达,向上能够被应用层所消费,向下能够关联某个具体的 PV。
  • PV:即存储卷,代表 Kubernetes 中一个具体存储类型的卷,例如火山引擎的某个云盘。PV 定义了具体存储的类型和卷参数,Kubernetes 通过引用 PV 中的存储信息执行存储的挂载操作。

从消费存储的逻辑上看,使用时应用层会声明一个对存储的需求(PVC),而 Kubernetes 会通过最佳匹配的方式选择一个满足 PVC 需求的 PV,并与之绑定。而根据 PV 的创建方式可以将存储卷分为动态存储和静态存储卷:

  • 静态存储卷:由管理员手动创建的 PV。
  • 动态存储卷:由 Provisioner 插件自动创建的 PV。其中,Provisioner 代表实现创建 PV 功能的注册插件。

静态存储卷

静态存储卷通常由集群管理员结合集群中存储需求,事先规划好存储介质,并创建对应的 PV 对象提供给 PVC 来消费。如果工作负载中定义了 PVC 需求,Kubernetes 会通过相关规则实现 PVC 和匹配的 PV 进行绑定,实现了应用对存储服务的访问能力。

动态存储卷

动态存储卷是集群管理员配置好存储资源池,并创建存储类,当有 PVC 需要消费 PV 的时候,根据 PVC 定义的需求,结合存储类定义的存储模版信息,由 Provisioner 插件动态创建一个 PV。

两种存储卷的比较

动态存储卷和静态存储卷区别是:动态卷是插件自动创建 PV,而静态卷是集群管理员手动创建 PV。

动态存储卷的优势:

  • 实现了 PV 的自动化生命周期管理,PV 的创建、删除都通过插件完成。
  • 减少了配置复杂度和系统管理员的运维工作量。
  • 能够保障更优的存储容量规划。

存储卷的绑定模式选择

当您定义存储类时,可以选择存储卷的绑定模式,包括

绑定模式说明
Immediate立即创建
WaitForFirstConsumer延迟创建

alt

在以下示例中,在 YAML 文件中使用 volumeBindingMode 字段定义了动态创建 PV 的绑定模式 ,WaitForFirstConsume 代表延迟创建,直到使用该存储卷声明的 Pod 被创建。

allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-ssd
parameters:
  ChargeType: PostPaid
  type: ESSD_PL0
provisioner: ebs.csi.volcengine.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

Immediate 绑定模式

当用户创建 PVC 时,会在集群寻找符合要求的 PV 进行绑定,如果没有找到符合要求的 PV ,则会开启自动创建流程:

  1. 如果新创建的 PVC 定义了存储类名称,那么存储类对应的 Provisioner 插件将开始自动创建 PV。
  2. Provisioner 根据 PVC 定义的参数以及存储类定义的参数创建 PV。
  3. Provisioner 在存储资源池创建数据卷,完成后会结合数据卷信息创建 PV。
  4. PV 创建完成后完成与 PVC 的绑定。

总的来说,如果存储类中定义以 Immediate 绑定模式完成 PV 和 PVC 的绑定,那么当 PVC 创建时会立即绑定现有的合适 PV,若没有合适的,则立即新建一个合适的 PV 以供绑定。

WaitForFirstConsumer 绑定模式

当用户创建 PVC 时,不会立即执行绑定 PV 的动作,而是等待这个 PVC 被 Pod 消费,绑定的流程如下:

  1. 当 Pod 创建并消费到这个PVC 时,系统发现此 PVC 采用的是延迟绑定。
  2. 调度器继续完成调度功能,并把调度结果同步到 PVC 到 metadata 中。
  3. Provisioner 发现 PVC 中写入了调度信息,根据调度信息判断目标存储到相关信息如节点、可用区等。
  4. 触发 PV 的创建过程。

说明

云盘在挂载时,存在可用区的限制,只有可用区一致的的云盘和节点才能挂载在一起,这种限制可能导致使用时存在以下问题。

  • 在 A 可用区创建了云盘,但是发现 A 可用区的节点资源不足,Pod 在 B 可用区的节点启动无法挂载。
  • 当存在多可用区资源时,难以事先评估规划各个可用区合理的 PV 数量。

当存在多个可用区时,可以采用 WaitForFirstConsumer 绑定模式可以规避以上问题。当用户创建 PVC 时,Provisioner 将不再立即进行 PV 的绑定或新建,而是会等待这个 PVC 被 Pod 消费时才会执行创建过程。在此模式下,在 Pod 启动时,先进行 Pod 的调度,当确认 Pod 的运行位置后,再根据 Pod 所在可用区信息进行 PV 和云盘的创建,从而保障云盘 Pod 运行节点在相同可用区。所以,在具备多个可用区的集群环境下,更加推荐使用 WaitForFirstConsumer 绑定模式。

操作步骤

步骤一:创建存储类

  1. 登录 容器服务控制台
  2. 在容器服务的左侧导航栏,选择 集群
  3. 单击目标集群名称,进入集群管理页面。
  4. 在左侧导航栏,单击 存储管理 > 存储类
  5. 单击 创建存储类。填写存储类名称,选择存储类型、可用区、回收策略、绑定模式等信息,单击 确定,完成创建。

    说明

    本操作指引中选择存储类型为云盘,若您需要文件存储类型,可参考 NAS 存储持久化

    alt

步骤二:创建有状态工作负载

支持通过控制台或 YAML 文件两种方式创建工作负载。

使用控制台创建工作负载

  1. 在集群管理页面的左侧导航栏中,选择 工作负载 > 有状态负载,单击 创建有状态负载
    alt
  2. 填写有状态负载名称、命名空间、实例个数等信息,单击 下一步:容器配置,进行容器配置。
    alt
  3. 配置容器基本信息。
    alt
  4. 配置容器存储信息,如下图所示。完成后单击 下一步:高级配置
    alt
  5. 如下图所示,填写 Headless 服务、实例配置、调度策略等信息。
    alt
  6. 单击 确定,完成配置。

使用 YAML 文件创建工作负载

您可以通过执行 YAML 文件实现工作负载的创建,示例如下:

apiVersion: v1
kind: Service
metadata:
  name: headless-svc-web
  namespace: default
spec:
  clusterIP: None
  clusterIPs:
  - None
  ports:
  - name: headless-service-port-0
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: web
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: default
spec:
  podManagementPolicy: OrderedReady 
  replicas: 2
  selector:
    matchLabels:
      app: web
  serviceName: headless-svc-web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: cr-demo-cn-beijing.cr.volces.com/xxxx/hg-web-test:v2.0
        name: web
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
        - mountPath: /test
          name: ssd
  volumeClaimTemplates:
  - apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      creationTimestamp: null
      name: ssd
      namespace: default
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 20Gi 
      storageClassName: ebs-ssd 
      volumeMode: Filesystem

步骤三:验证持久化存储有效性

  1. 检测有状态工作负载创建完成情况。

    1. 支持在容器服务控制台 有状态负载 页面查看已创建的有状态工作负载 web
      alt
    2. 单击有状态工作负载的名称,进入详情页面,可以看到实例列表中,web-0web-1 两个 Pod 正在运行。
      alt
    3. 存储管理 > 存储卷声明,可以查看到生成了两个 PVC,ssd-web-0ssd-web-1
      alt
  2. 扩容 Pod,验证 PVC 同步扩容。

    1. 工作负载 > 有状态负载 界面,单击操作,选择更新实例数,将实例数从 2 改为 3。
      alt
    2. 有状态负载详情页 可以看到 Pod 数量扩容为 3。
      alt
    3. 存储管理 > 存储卷声明,查看 PVC 是否同步扩容可以看到存储卷声明同步扩容为 3 个,证明 PVC 可以跟随 Pod 同步扩容。
      alt
  3. 验证 Pod 删除后,PVC 保留不变。

    1. 工作负载 > 有状态负载 界面,将有状态工作负载的实例数从 3 改为 0。
      alt
    2. 有状态负载详情页 可以看到 Pod 数量逐步缩容为 0。
      alt
    3. 存储管理 > 存储卷声明,可以看到在 Pod 完全删除后,所有的 PVC 仍然存在。
      alt
    4. 工作负载 > 有状态负载 界面,将有状态工作负载实例数修改恢复为 3,可以看到 Pod 数量逐步恢复为 3,并且名称保持不变。
      alt
    5. 在任意 Pod 详情页面,查看存储信息,可以看到对应 PVC 已经重新挂载。
      alt
  4. 验证 StatefulSet 服务的持久化存储。

    1. 在 kubectl 中执行以下命令,查看 /test 路径下的文件。
    kubectl exec web-0 ls /test
    

    预期输出:

    lost+found
    
    1. 执行以下命令,在 /test 路径下创建文件 test
    kubectl exec web-0 touch /test/test
    
    1. 执行以下命令,查看 /test 路径下的文件。
    kubectl exec web-0 ls /test
    

    预期输出:

    lost+found
    test
    
    1. 执行以下命令,删除名称为 web-0 的 Pod。
    kubectl delete pod web-0
    

    预期输出:

    pod "web-0" deleted
    
    1. 执行以下命令,查看 /test 路径下的文件。
    kubectl exec web-0 ls /test
    

    预期输出:

    lost+found
    test
    
    1. test 文件在 Pod 删除后仍然存在,说明云盘的数据可持久化保存。