本文发布时间已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

如何在数据密集型 Kubernetes 环境中处理数据重复

为什么要复制数据?

为每个团队创建应用程序及其状态的副本非常方便。例如,您可能需要一个单独的数据库副本,以测试一些重要的模式更改或开发其他破坏性操作,如批量插入/删除/更新...

复制数据需要花费大量时间。这是因为您首先需要从源块存储提供程序下载所有数据到计算资源,然后再将其发送回存储提供程序。此过程中会使用大量的网络流量和 CPU/RAM。通过将某些昂贵的操作卸载到专用硬件进行硬件加速始终是一个巨大的性能提升。它将完成操作所需的时间减少了几个数量级。

卷快照来救援

Kubernetes 在 1.12 版本中引入了 VolumeSnapshots 作为 alpha 版本,在 1.17 版本中作为 beta 版本,并在 1.20 版本中作为正式发布版本。VolumeSnapshots 使用存储提供商的专用 API 来复制数据卷。

由于数据已经在同一个存储设备(设备阵列)中,因此对于具有本地快照的存储提供商(大多数本地存储提供商)而言,复制数据通常是一个元数据操作。您所需要做的就是将新磁盘指向不可变的快照,并仅保存增量(或让它执行全磁盘复制)。作为在存储后端内部进行的操作,它速度更快,并且通常不涉及通过网络发送流量。公共云存储提供商在底层的工作方式略有不同。他们将快照保存到对象存储,然后在“复制”磁盘时从对象存储复制回块存储。从技术上讲,云提供商侧花费了大量的计算和网络资源,但是从 Kubernetes 用户的角度来看,无论它是本地还是远程快照存储提供商,VolumeSnapshots 的工作方式都相同,并且此操作不涉及计算和网络资源。

听起来我们有解决方案了,对吗?

实际上,VolumeSnapshots 是命名空间的,并且 Kubernetes 保护命名空间的数据不被租户(命名空间)之间共享。此 Kubernetes 限制是一个有意识的设计决策,以便在不同命名空间中运行的 Pod 不能挂载另一个应用程序的 PersistentVolumeClaim (PVC)。

一种解决方法是在一个命名空间中创建具有重复数据的多个卷。但是,您很容易引用错误的副本。

因此,我们的想法是通过命名空间分隔团队/项目,以避免这种情况并通常限制对生产命名空间的访问。

解决方案?在外部创建 Golden 快照

解决此设计限制的另一种方法是在外部(而不是通过 Kubernetes)创建快照。这也称为手动预配置快照。接下来,我将它作为可用于多个命名空间的多租户黄金快照导入。以下说明将针对 AWS EBS(弹性块存储)和 GCE PD(持久磁盘)服务。

准备 Golden 快照的高级计划

  1. 在云提供商中标识您想要使用数据克隆的磁盘(EBS/持久磁盘)
  2. 制作磁盘快照(在云提供商控制台中)
  3. 获取磁盘快照 ID

为每个团队克隆数据的高级计划

  1. 创建命名空间 “sandbox01”
  2. 将磁盘快照(ID)作为 VolumeSnapshotContent 导入到 Kubernetes
  3. 在命名空间 “sandbox01” 中创建映射到 VolumeSnapshotContent 的 VolumeSnapshot
  4. 从 VolumeSnapshot 创建 PersistentVolumeClaim
  5. 使用 PVC 安装 Deployment 或 StatefulSet

步骤 1:标识磁盘

首先,您需要标识您的黄金源。在我的示例中,它是 “production” 命名空间中 PersistentVolumeClaim “postgres-pv-claim” 上的 PostgreSQL 数据库。

kubectl -n <namespace> get pvc <pvc-name> -o jsonpath='{.spec.volumeName}'

输出将类似于

pvc-3096b3ba-38b6-4fd1-a42f-ec99176ed0d90

步骤 2:准备您的黄金源

您需要执行一次此操作,或者每次想要刷新黄金数据时都执行此操作。

制作磁盘快照

转到 AWS EC2 或 GCP Compute Engine 控制台,搜索具有与上次输出匹配的标签的 EBS 卷(在 AWS 上)或持久磁盘(在 GCP 上)。在本例中,我看到了:pvc-3096b3ba-38b6-4fd1-a42f-ec99176ed0d9

单击“创建快照”并为其命名。您可以在控制台中手动执行此操作,在 AWS CloudShell / Google Cloud Shell 中执行此操作,或在终端中执行此操作。要在终端中创建快照,您必须安装并配置 AWS CLI 工具 (aws) 或 Google 的 CLI (gcloud)。

以下是在 GCP 上创建快照的命令

gcloud compute disks snapshot <cloud-disk-id> --project=<gcp-project-id> --snapshot-names=<set-new-snapshot-name> --zone=<availability-zone> --storage-location=<region>
Screenshot of a terminal showing volume snapshot creation on GCP

GCP 快照创建

GCP 通过其 PVC 名称标识磁盘,因此它是直接映射。在 AWS 中,您首先需要使用 PVC 名称值查找具有 CSIVolumeName AWS 标签的卷,该标签将用于快照创建。

Screenshot of AWS web console, showing EBS volume identification

在 AWS 上标识磁盘 ID

标记完成卷 (volume-id) vol-00c7ecd873c6fb3ec,并在 AWS 控制台中创建 EBS 快照,或使用 aws cli

aws ec2 create-snapshot --volume-id '<volume-id>' --description '<set-new-snapshot-name>' --tag-specifications 'ResourceType=snapshot'

步骤 3:获取您的磁盘快照 ID

在 AWS 中,上面的命令将输出类似的内容

"SnapshotId": "snap-09ed24a70bc19bbe4"

如果您正在使用 GCP 云,则可以通过查询给定名称的快照从 gcloud 命令中获取快照 ID

gcloud compute snapshots --project=<gcp-project-id> describe <new-snapshot-name> | grep id:

您应该获得类似于以下的输出

id: 6645363163809389170

步骤 4:为每个团队创建一个开发环境

现在我有了我的 Golden 快照,它是不可变的数据。每个团队都将获得此数据的副本,并且团队成员可以根据自己的需要对其进行修改,前提是会为每个团队创建一个新的 EBS/持久磁盘。

下面我将为每个命名空间定义一个清单。为了节省时间,您可以使用 sedyq 等工具替换命名空间名称(例如,将 “sandbox01” → “sandbox42”),使用 Kustomize 等 Kubernetes 感知的模板工具,或者在 CI/CD 管道中使用变量替换。

以下是一个清单示例

---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
 name: postgresql-orders-db-sandbox01
 namespace: sandbox01
spec:
 deletionPolicy: Retain
 driver: pd.csi.storage.gke.io
 source:
   snapshotHandle: 'gcp/projects/staging-eu-castai-vt5hy2/global/snapshots/6645363163809389170'
 volumeSnapshotRef:
   kind: VolumeSnapshot
   name: postgresql-orders-db-snap
   namespace: sandbox01
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
 name: postgresql-orders-db-snap
 namespace: sandbox01
spec:
 source:
   volumeSnapshotContentName: postgresql-orders-db-sandbox01

在 Kubernetes 中,VolumeSnapshotContent (VSC) 对象不是命名空间的。但是,我需要为每个不同的命名空间使用单独的 VSC,因此每个 VSC 的 metadata.name 也必须不同。为了使其简单明了,我使用了目标命名空间作为名称的一部分。

现在是时候用安装在您的 K8s 集群中的 CSI(容器存储接口)驱动程序替换驱动程序字段了。主要云提供商为支持 VolumeSnapshots 的块存储提供 CSI 驱动程序,但通常默认情况下未安装 CSI 驱动程序,请咨询您的 Kubernetes 提供商。

上面的清单定义了一个适用于 GCP 的 VSC。在 AWS 上,驱动程序和 SnashotHandle 值可能如下所示

  driver: ebs.csi.aws.com
  source:
    snapshotHandle: "snap-07ff83d328c981c98"

此时,我需要使用 *Retain* 策略,以便 CSI 驱动程序不会尝试删除我手动创建的 EBS 磁盘快照。

对于 GCP,您必须手动构建此字符串 - 添加完整的项目 ID 和快照 ID。对于 AWS,它只是一个普通的快照 ID。

VSC 还要求指定哪个 VolumeSnapshot (VS) 将使用它,因此 VSC 和 VS 相互引用。

现在我可以从上面的 VS 创建 PersistentVolumeClaim。重要的是首先设置此项

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: postgres-pv-claim
 namespace: sandbox01
spec:
 dataSource:
   kind: VolumeSnapshot
   name: postgresql-orders-db-snap
   apiGroup: snapshot.storage.k8s.io
 accessModes:
   - ReadWriteOnce
 resources:
   requests:
     storage: 21Gi

如果默认 StorageClass 具有 WaitForFirstConsumer 策略,则只有在某些 Pod 绑定该 PVC 时,才会从 Golden 快照创建实际的云磁盘。

现在,我将该 PVC 分配给我的 Pod(在我的示例中,它是 Postgresql),就像使用任何其他 PVC 一样。

kubectl -n <namespace> get volumesnapshotContent,volumesnapshot,pvc,pod

VS 和 VSC 都应为 *READYTOUSE* true,PVC 已绑定,并且 Pod(来自 Deployment 或 StatefulSet)正在运行。

要继续使用我的 Golden 快照中的数据,我只需要为下一个命名空间重复此操作即可!无需在复制过程中浪费时间和计算资源。