预配和使用混合本地 SSD,以用于原始块存储和临时存储

本文档介绍了如何在 Google Kubernetes Engine (GKE) 中预配和分区本地 SSD,以支持在同一节点上同时需要标准临时存储文件系统和未格式化的原始块存储的工作负载。

此配置适用于有状态应用和高度优化的数据库。 这些工作负载通常需要直接访问磁盘以提高性能,但也需要标准临时文件存储空间来存储边车、日志或容器缓存。通过使用混合本地 SSD 容量,您可以减少集群管理开销并最大限度地提高硬件利用率,因为您无需将单独的节点或完整磁盘专门用于原始块基础架构。

当您创建具有混合本地 SSD 容量的集群或节点池时,GKE 会对每个物理挂接的本地 SSD 进行分区,如下所示:

  • 临时存储空间:请求的临时容量在本地 SSD 之间平均分配,并组合在一起以提供节点的标准临时文件存储空间。当临时容量跨多个本地 SSD 进行分区时,存储空间会合并到 RAID-0 阵列中。您可以使用标准 emptyDir 卷访问临时存储。
  • 原始块存储:每个磁盘上的剩余物理容量会作为不同的未格式化原始块存储设备呈现给节点。

本文档适用于希望在 GKE 中针对专用工作负载优化硬件利用率和存储性能的平台管理员、运维人员和 DevOps。

注意事项

除了 Compute Engine 本地 SSD 限制之外,在配置混合本地 SSD 模式之前,请先查看以下特定于 GKE 的注意事项:

  • Autopilot 兼容性:GKE Autopilot 集群不支持混合原始块和临时存储。如需了解如何在 Autopilot 中使用本地 SSD 进行标准临时存储,请参阅 Autopilot 集群中的本地 SSD。
  • 自定义 ComputeClass:自定义 ComputeClass 不支持混合本地 SSD 配置。
  • 操作系统:仅 Linux 节点映像(Container-Optimized OS 和 Ubuntu)支持混合本地 SSD 模式。不支持 Windows Server 节点池。
  • 维护事件:我们不建议将本地 SSD 用作存储优化 Z3 或 Z4D 虚拟机的原始块存储。这些虚拟机上的数据可能无法在维护事件中保留。如需了解详情,请参阅预配和使用本地 SSD 支持的原始块存储。
  • 共享 I/O:由于临时 RAID-0 阵列和目标原始块分区都依赖于相同的底层物理 SSD,因此对一个逻辑分区的密集操作可能会影响另一个逻辑分区的吞吐量。如需了解吞吐量上限,请参阅本地 SSD 性能。
  • 价格:使用混合模式对本地 SSD 容量进行分区时,您需要为挂接到节点的底层物理本地 SSD 总容量付费,无论为临时存储分配的容量与原始块存储相比是多少。如需了解详情,请参阅本地 SSD 价格。
  • 手动配置:如需使用本地 SSD 支持的原始块存储,您必须手动配置设备,因为 GKE 不会格式化或装载原始块分区。您还负责管理节点容量,并处理共存工作负载可能造成的中断。使用本地 PersistentVolume 对象可提供集成的 Pod 调度。如果您的工作负载不需要在整个 Pod 生命周期内持久保存数据,或者没有特殊的性能需求,请改用完全临时的本地 SSD 配置。完全临时配置适用于大多数使用场景。
  • 节点内存交换:您可以使用启动磁盘配置文件或临时本地 SSD 配置文件,将混合本地 SSD 容量与节点内存交换相结合。专用本地 SSD 配置文件不支持混合本地 SSD 容量。如需了解详情,请参阅具有混合本地 SSD 容量的节点内存交换。

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如需使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。如果您之前安装了 gcloud CLI,请通过运行 gcloud components update 命令来获取最新版本。较早版本的 gcloud CLI 可能不支持运行本文档中的命令。

如需使用此功能,GKE 节点必须运行 1.37.0-gke.3570000 或更高版本。

所需的角色

如需获得创建具有混合本地 SSD 容量的集群和节点池所需的权限,请让您的管理员为您授予项目的 Kubernetes Engine Admin (roles/container.admin) IAM 角色。 如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

创建具有混合本地 SSD 存储空间的集群或节点池

您可以使用 --ephemeral-storage-local-ssd 标志的 capacity 属性分配临时容量并预留原始块分区。

通过使用 --ephemeral-storage-local-ssd 标志指定 capacity 子字段来创建集群或节点池。

如果您的机器类型允许配置本地 SSD 的数量,您还必须指定 count 子字段。如果您的机器类型具有固定数量的本地 SSD(例如 -lssd 类型),则可以省略 count 子字段,因为 GKE 会根据虚拟机形状自动计算磁盘数量。

创建具有混合本地 SSD 存储空间的集群

如需创建新集群,请使用 gcloud container clusters create 命令。 这些设置仅适用于默认节点池。如果后续节点池需要混合本地 SSD 存储,请在创建节点池时指定 --ephemeral-storage-local-ssd 标志。您可以挂接的磁盘数量上限因机器类型和区域而异。

如果集群的版本低于 1.37.0-gke.3570000,请将以下标志之一添加到以下集群创建命令中:

  • --cluster-version=<var>VERSION</var> 标志,其中 <var>VERSION</var> 设置为 1.37.0-gke.3570000 或更高版本,或者
  • --release-channel=rapid 标志。

对于具有可配置本地 SSD 数量的机器类型,请运行以下命令:

gcloud container clusters create CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd count=NUMBER_OF_DISKS,capacity=CAPACITY

对于具有固定本地 SSD 数量的机器类型,请运行以下命令:

gcloud container clusters create CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd capacity=CAPACITY

替换以下内容:

  • CLUSTER_NAME:您要创建的集群的名称。
  • MACHINE_TYPE:要使用的机器类型。如需查看支持的机器类型的列表,请参阅机器系列支持。您无法将本地 SSD 与默认的 e2-medium 机器类型搭配使用,因为共享核心机器类型不支持本地 SSD。
  • NUMBER_OF_DISKS:对于具有可配置本地 SSD 数量的机器类型,在每个节点上预配的本地 SSD 磁盘数量。
  • CAPACITY:要格式化以用作临时存储的本地 SSD 总容量。此值必须大于 0 且严格小于本地 SSD 物理总容量(磁盘数量 × 每个磁盘的大小),例如 200GB。请注意以下几点:

    • 集群创建后,该值便无法更改。
    • GKE 会预留部分容量供系统使用,因此可分配的临时存储空间小于 CAPACITY。如需了解详情,请参阅本地临时存储预留。

创建具有混合本地 SSD 存储空间的节点池

如需将节点池添加到现有集群,请使用 gcloud container node-pools create 命令。

如果节点池的版本低于 1.37.0-gke.3570000,则必须执行以下操作:

  • 将 --node-version=<var>VERSION</var> 标志添加到以下节点池创建命令,并将 <var>VERSION</var> 设置为 1.37.0-gke.3570000 或更高版本。
  • 确保集群控制平面版本为 1.37.0-gke.3570000 或更高版本。

对于具有可配置本地 SSD 数量的机器类型,请运行以下命令:

gcloud container node-pools create POOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd count=NUMBER_OF_DISKS,capacity=CAPACITY

对于具有固定本地 SSD 数量的机器类型,请运行以下命令:

gcloud container node-pools create POOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd capacity=CAPACITY

替换以下内容:

  • POOL_NAME:要创建的新节点池的名称。
  • CLUSTER_NAME:集群的名称。
  • MACHINE_TYPE:要使用的机器类型。如需查看支持的机器类型的列表,请参阅机器系列支持。您无法将本地 SSD 与默认的 e2-medium 机器类型搭配使用,因为共享核心机器类型不支持本地 SSD。
  • NUMBER_OF_DISKS:对于具有可配置本地 SSD 数量的机器类型,在每个节点上预配的本地 SSD 磁盘数量。
  • CAPACITY:要格式化以用作临时存储的本地 SSD 总容量。此值必须大于 0 且严格小于本地 SSD 物理总容量(磁盘数量 × 每个磁盘的大小),例如 200GB。请注意以下几点:

    • 创建节点池后,该值便无法更改。
    • GKE 会预留部分容量供系统使用,因此可分配的临时存储空间小于 CAPACITY。如需了解详情,请参阅本地临时存储预留。

验证节点标签

预配后,如需验证节点是否具有临时块存储和原始块存储的正确标签,请运行以下命令:

kubectl describe node NODE_NAME

输出应包含以下两个节点标签:

  • cloud.google.com/gke-ephemeral-storage-local-ssd: "true"
  • cloud.google.com/gke-local-nvme-ssd-partition: "true"

访问临时容量

通过 emptyDir 卷访问预留的临时存储空间,就像访问任何其他 GKE 临时卷一样。

访问原始块容量

如需访问原始块容量,您需要创建一个 StorageClass 对象,预配一个本地 PersistentVolume 对象,并使用 PersistentVolumeClaim 对象声明该对象。

创建一个 StorageClass

由于您在没有社区静态预配工具的情况下预配 PersistentVolume 对象,因此我们建议您创建具有 volumeBindingMode: WaitForFirstConsumer 字段的 StorageClass 对象。这会将绑定延迟到 Pod 调度阶段,以确保 Kubernetes 从可运行 Pod 的适当节点中选择本地固态硬盘。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: "local-storage"
provisioner: "kubernetes.io/no-provisioner"
volumeBindingMode: "WaitForFirstConsumer"

创建 PersistentVolume

本地 PersistentVolume 对象在节点升级、伸缩和集群自动伸缩方面存在重要限制。在创建卷之前,请先查看本地 PersistentVolume 的限制,了解生命周期事件对数据的影响。

如需创建 PersistentVolume 对象,请完成以下步骤,找到本地 SSD 分区的符号链接路径,确定其确切的剩余容量,并配置清单。

查找分区容量

在目标节点上运行以下命令,以查找为分区留下的确切原始块容量。

kubectl debug node/NODE_NAME -it --image=ubuntu -- \
    sh -c 'ls -l /host/dev/disk/by-id/ | grep partition; lsblk -o NAME,SIZE,TYPE'

将 NODE_NAME 替换为挂载了本地 SSD 的节点的名称。

输出会显示设备名称与分区符号链接之间的映射,以便您验证哪个分区对应于哪个设备。它还会列出挂接到节点的块存储设备及其大小。

例如,如果您将两个本地 SSD 挂接到节点,并为临时存储空间 (capacity=200GB) 分配 200 GB,则输出类似于以下内容:

google-local-ssd-partition0 -> /dev/nvme0n1p2
google-local-ssd-partition1 -> /dev/nvme0n2p2
NAME          SIZE TYPE
...
nvme0n1       375G disk
|-nvme0n1p1   100G part
| `-md0     199.9G raid0
`-nvme0n1p2   275G part
nvme0n2       375G disk
|-nvme0n2p1   100G part
| `-md0     199.9G raid0
`-nvme0n2p2   275G part

请注意 google-local-ssd-partition 符号链接与底层设备名称之间的映射,以及该分区的大小。您可以在 PersistentVolume 清单中将大小用作 PARTITION_SIZE,将符号链接路径用作 LOCAL_SSD_SYMLINK_PATH。

例如,在前面的输出中,/dev/disk/by-id/google-local-ssd-partition0 符号链接指向 nvme0n1p2 分区,其大小为 275G。

kubectl debug 命令会在默认命名空间中留下一个正在运行的临时 Pod。为防止其消耗资源,请运行以下命令删除此 Pod:

kubectl delete pod DEBUG_POD_NAME

将 DEBUG_POD_NAME 替换为 kubectl debug 命令创建的 Pod 的名称,该名称会在您运行命令时输出。

配置 PersistentVolume 清单

使用您找到的分区大小配置卷清单。nodeAffinity 字段是本地卷的必填字段。这有助于确保 Kubernetes 将声明此卷的 Pod 调度到实际挂接了本地 SSD 的特定节点。以下示例展示了原始块本地 SSD 分区的清单:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: "example-local-pv"
spec:
  capacity:
    storage: PARTITION_SIZE
  accessModes:
  - "ReadWriteOnce"
  volumeMode: "Block"
  persistentVolumeReclaimPolicy: "Retain"
  storageClassName: "local-storage"
  local:
    path: "LOCAL_SSD_SYMLINK_PATH"
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: "kubernetes.io/hostname"
          operator: "In"
          values:
          - "NODE_NAME"

替换以下内容:

  • PARTITION_SIZE:您在第一步中找到的实际剩余原始块容量。使用 Gi 后缀(例如 275Gi),使该值与分区的容量完全匹配。
  • NODE_NAME:挂接本地 SSD 的节点的名称。
  • LOCAL_SSD_SYMLINK_PATH:您在第一步中找到的未格式化本地 SSD 分区的符号链接路径。对于挂接到节点池的每个本地 SSD,主机操作系统都会创建一个符号链接,用于访问剩余的原始块容量。路径遵循从零开始的索引模式。例如,如果您挂接了三个本地 SSD,则分区的原始块路径如下所示:
    • /dev/disk/by-id/google-local-ssd-partition0
    • /dev/disk/by-id/google-local-ssd-partition1
    • /dev/disk/by-id/google-local-ssd-partition2

创建 PersistentVolumeClaim

创建 PersistentVolumeClaim 对象以声明卷。相应的规范如下所示:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: "example-local-pvc"
spec:
  accessModes:
  - "ReadWriteOnce"
  volumeMode: "Block"
  storageClassName: "local-storage"
  resources:
    requests:
      storage: PARTITION_SIZE

在 Pod 中使用卷

您可以通过在 volumeDevices 规范中引用 PersistentVolumeClaim 对象,在 Pod 中使用 PersistentVolume 对象。以下是使用 example-local-pvc 声明的 Pod 清单示例:

apiVersion: v1
kind: Pod
metadata:
  name: "example-pod"
spec:
  containers:
  - name: "app"
    image: "ubuntu:22.04"
    command: ["/bin/sh", "-c"]
    args: ["tail -f /dev/null"]
    volumeDevices:
    - name: "raw"
      devicePath: "/dev/xvda"
  volumes:
  - name: "raw"
    persistentVolumeClaim:
      claimName: "example-local-pvc"

由于您使用的是 kubernetes.io/no-provisioner 预配工具,因此 GKE 不会自动清理本地卷。如果您删除 PersistentVolume 对象,则必须先手动清空底层磁盘中的数据,然后才能安全地重复使用该磁盘。

问题排查

如需了解问题排查说明,请参阅排查 GKE 中的存储问题。

后续步骤