Kubernetes 中实现持久化存储的简单指南

了解如何在 Kubernetes 中为有状态应用程序实现持久化存储。本指南将揭秘持久卷 (PV) 和持久卷声明 (PVC),解释访问模式和存储类 (StorageClasses)。包含用于定义 PVC 和将存储挂载到 Pod 的实用 YAML 示例,从而实现容器化应用程序中可靠的数据持久性。

Kubernetes 持久化存储实现简易指南

Kubernetes 持久化存储解决了一个简单的问题:您的容器可能会消失,但数据库文件、上传内容或队列数据不能丢失。无状态 Pod 可以随时被替换,而有状态应用需要能够承受 Pod 重启、重新调度和节点维护的存储。

本指南将结合实际 YAML 示例,讲解 PersistentVolume(PV)、PersistentVolumeClaim(PVC)、访问模式和 StorageClass,您可以直接将其应用于实际工作负载。

理解 PersistentVolume(PV)和 PersistentVolumeClaim(PVC)

在将存储挂载到 Pod 之前,需要明确每个对象负责什么:

  • PersistentVolume(PV): PV 是由管理员预先制备或通过 StorageClass 动态制备的集群存储资源。PV 是集群资源,类似于节点,其生命周期独立于任何使用它的 Pod。PV 抽象了底层存储实现的细节(例如 NFS、iSCSI、云提供商块存储)。
  • PersistentVolumeClaim(PVC): PVC 是用户对存储的请求。它消耗集群中作为 PV 可用的存储资源。PVC 类似于 Pod,消耗计算资源,并且作用域限定在命名空间内。PVC 指定所需的存储容量、访问模式,以及可选的 StorageClass。

这种分离让平台团队管理存储细节,而应用团队只需请求所需的容量和访问模式。

关键概念:访问模式与 StorageClass

两个设置控制着大多数 PVC 的行为:卷的挂载方式以及由哪个存储后端创建它。

访问模式

访问模式定义了卷如何挂载到 Pod。可用的访问模式包括:

  • ReadWriteOnce(RWO):卷可以被单个节点以读写方式挂载。
  • ReadOnlyMany(ROX):卷可以被多个节点以只读方式挂载。
  • ReadWriteMany(RWX):卷可以被多个节点以读写方式挂载。
  • ReadWriteOncePod(RWOP):卷可以被单个 Pod 以读写方式挂载。当需要更严格的单写入者行为且 CSI 驱动支持时,此模式非常有用。

需要注意的是,这些模式的实际支持情况取决于底层存储提供商。

StorageClass

StorageClass 为管理员提供了一种描述其提供的存储“类别”的方式。不同的类别可能对应不同的服务质量级别、备份策略或集群管理员决定的任意策略。StorageClass 包含一个用于制备存储的制备器(provisioner)以及一组制备器参数。当 PVC 创建时没有指定具体的 PV,并且请求了 StorageClass,Kubernetes 将使用指定的 StorageClass 动态制备一个 PV。

逐步实现持久化存储

让我们通过一个常见场景来演示:为 Pod 请求并使用持久化存储。

步骤 1:定义 PersistentVolumeClaim(PVC)

首先,您需要创建一个 PVC 来指定存储需求。这个 PVC 将作为应用对存储的请求。

示例 pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

在此示例中:

  • name: my-pvc:这是 PVC 的名称。
  • accessModes: - ReadWriteOnce:我们请求的存储可以被单个节点以读写方式挂载。
  • resources.requests.storage: 1Gi:我们请求 1 GB 的存储空间。

应用 PVC:

将上述内容保存为 pvc.yaml 文件,然后应用到集群:

kubectl apply -f pvc.yaml

应用后,您可以检查 PVC 的状态:

kubectl get pvc my-pvc

如果存在合适的 PV 或已动态制备,您应该会看到 PVC 的状态为 Bound

步骤 2:创建使用 PVC 的 Pod

现在,让我们创建一个使用 PVC 所请求存储的 Pod。我们将把 PVC 提供的卷挂载到容器内的特定目录。

示例 pod-with-pv.yaml

apiVersion: v1
kind: Pod
metadata:
  name: my-stateful-pod
spec:
  containers:
    - name: my-container
      image: nginx
      ports:
        - containerPort: 80
      volumeMounts:
        - name: my-persistent-storage
          mountPath: /usr/share/nginx/html
  volumes:
    - name: my-persistent-storage
      persistentVolumeClaim:
        claimName: my-pvc

在此示例中:

  • volumes:我们定义了一个名为 my-persistent-storage 的卷。
  • persistentVolumeClaim.claimName: my-pvc:这将我们的卷链接到之前创建的 PVC。
  • volumeMounts:在容器定义中,我们指定了卷的挂载路径(mountPath: /usr/share/nginx/html)。

应用 Pod:

将上述内容保存为 pod-with-pv.yaml 文件,然后应用:

kubectl apply -f pod-with-pv.yaml

现在,您的 nginx 容器将能够访问由 my-pvc 定义的持久化存储,路径为 /usr/share/nginx/html。只要 PVC 及其底层 PV 存在,写入该路径的任何数据都将在 Pod 被删除并重新创建后持久保存。

使用 StorageClass 进行动态制备

手动创建 PV 可能很繁琐。Kubernetes 提供了动态制备功能,当 PVC 请求的存储无法由现有 PV 满足时,会自动创建 PV。这通过 StorageClass 实现。

大多数云提供商(AWS、GCP、Azure)都提供预配置的 StorageClass。您可以使用以下命令查看:

kubectl get storageclass

要使用动态制备,只需在 PVC 定义中添加 storageClassName 字段:

示例 pvc-dynamic.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-dynamic-pvc
spec:
  storageClassName: standard # 将 'standard' 替换为实际的 StorageClass 名称
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

当您应用此 PVC 时,Kubernetes 将查找名为 standard(或您指定的任何名称)的 StorageClass,并指示其制备器创建一个 5Gi 的新 PV,并将其绑定到此 PVC。

提示与最佳实践

  • 选择合适的访问模式: 仔细考虑应用所需的访问模式。ReadWriteOnce 适用于单副本数据库,而 ReadWriteMany 则用于多个 Pod 共享的文件系统。
  • 了解存储性能: 不同的存储提供商和 StorageClass 提供不同的性能特性(IOPS、吞吐量)。选择满足应用性能需求的 StorageClass。
  • 备份策略: 持久化存储并不意味着自动备份。为持久卷实施稳健的备份策略,尤其是对于关键数据。
  • PV 回收策略: PV 具有 persistentVolumeReclaimPolicy,常见的有 DeleteRetain。动态制备的卷通常使用其 StorageClass 定义的回收策略。旧的 Recycle 策略已弃用,不应在新设置中使用。
  • 命名空间注意事项: PVC 是命名空间作用域的。确保 Pod 和 PVC 位于同一命名空间,以便绑定成功。

总结

Kubernetes 中的持久化存储始于 PVC、Pod 挂载以及与工作负载匹配的 StorageClass。对于单副本数据库,从 ReadWriteOnce 声明开始,确认其状态为 Bound,挂载到容器中,并从第一天起将备份纳入设计。