Kubernetes 中实现持久化存储的简单指南
在容器编排领域,Kubernetes 擅长管理无状态应用——即那些在重启或扩缩容事件之间不需要保留数据的应用。然而,许多现代应用,例如数据库、消息队列和键值存储,本质上都是有状态的。这些应用需要一种可靠的方式来持久存储和访问数据,即使运行它们的 Pod 被重新调度或替换。这就是 Kubernetes 持久化存储发挥作用的地方。
本指南将阐明 PersistentVolumes (PVs) 和 PersistentVolumeClaims (PVCs) 的概念,它们是 Kubernetes 中管理持久化存储的核心组件。我们将通过实际的 YAML 示例,逐步讲解如何定义、请求存储并将其绑定到您的 Pod,使您能够自信地在 Kubernetes 集群上部署有状态应用。
理解 PersistentVolumes (PVs) 和 PersistentVolumeClaims (PVCs)
在深入实现之前,了解 PV 和 PVC 的作用至关重要:
- PersistentVolume (PV): PV 是集群中的一块存储,它由管理员预先配置或通过 StorageClasses 动态配置。PV 是集群资源,很像节点 (Nodes)。它们具有独立于使用该 PV 的任何单个 Pod 的生命周期。PV 抽象了底层存储的实现细节(例如 NFS、iSCSI、云提供商块存储)。
- PersistentVolumeClaim (PVC): PVC 是用户对存储的请求。它消费集群中可用的 PV 形式的存储资源。PVC 类似于 Pod,它们都消费计算资源,并且作用域限定在命名空间 (namespace) 内。PVC 指定了所需的存储容量、访问模式以及可选的 StorageClass。
这种职责分离使得集群管理员可以独立地配置和管理存储资源,而应用开发者则可以请求存储,而无需了解底层实现细节。
关键概念:访问模式和 StorageClasses
在使用 PV 和 PVC 时,需要掌握的两个重要概念是访问模式 (Access Modes) 和 StorageClasses:
访问模式
访问模式定义了卷如何可以挂载到 Pod。可用的访问模式有:
ReadWriteOnce(RWO):卷可以由单个节点以读写方式挂载。ReadOnlyMany(ROX):卷可以由多个节点以只读方式挂载。ReadWriteMany(RWX):卷可以由多个节点以读写方式挂载。
需要注意的是,这些模式的实际支持取决于底层存储提供商。
StorageClasses
StorageClass 提供了一种方式,让管理员可以描述他们提供的存储“类别”。不同的类别可能对应不同的服务质量 (quality-of-service) 等级、备份策略,或由集群管理员确定的任意策略。一个 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 吉字节的存储空间。
应用 PVC:
将上述内容保存到名为 pvc.yaml 的文件中,并将其应用到您的集群:
kubectl apply -f pvc.yaml
应用后,您可以检查 PVC 的状态:
kubectl get pvc my-pvc
如果存在合适的 PV 或已动态配置 PV,您应该看到输出显示 PVC 处于 Bound(已绑定)状态。
步骤 2:创建一个使用 PVC 的 Pod
现在,让我们创建一个 Pod,它将利用我们 PVC 请求的存储。我们将把 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 容器将可以在 /usr/share/nginx/html 路径访问由 my-pvc 定义的持久化存储。即使 Pod 被删除并重新创建,只要 PVC 及其底层 PV 保持不变,写入此路径的任何数据都将得到持久化。
使用 StorageClasses 进行动态配置
手动创建 PV 可能会很繁琐。Kubernetes 提供了动态配置 (dynamic provisioning),当 PVC 请求的存储无法由现有 PV 满足时,PV 会自动创建。这通过 StorageClasses 实现。
大多数云提供商(AWS、GCP、Azure)都提供了预配置的 StorageClasses。您可以使用以下命令检查它们:
kubectl get storageclass
要使用动态配置,您只需在 PVC 定义中添加 storageClassName 字段:
pvc-dynamic.yaml 示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-dynamic-pvc
spec:
storageClassName: standard # Replace 'standard' with an actual StorageClass name
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
当您应用此 PVC 时,Kubernetes 将查找名为 standard(或您指定的任何名称)的 StorageClass,并指示其配置器创建一个新的 5Gi PV 并将其绑定到此 PVC。
提示和最佳实践
- 选择正确的访问模式: 仔细考虑您的应用程序所需的访问模式。
ReadWriteOnce通常用于单副本数据库,而ReadWriteMany对于多个 Pod 使用的共享文件系统是必需的。 - 理解存储性能: 不同的存储提供商和 StorageClasses 提供不同的性能特征(IOPS、吞吐量)。选择一个符合您应用程序性能需求的 StorageClass。
- 备份策略: 持久化存储并不自动意味着备份。为您的持久卷实施一个可靠的备份策略,尤其是对于关键数据。
- PV 回收策略: PV 具有
reclaimPolicy,可以是Delete(默认)、Retain或Recycle(已弃用)。Retain对于在 PV 被删除但底层存储仍然存在时确保数据不丢失非常有用。 - 命名空间考量: PVC 是命名空间范围的。确保您的 Pod 和 PVC 位于同一命名空间中才能进行绑定。
总结
实现持久化存储是在 Kubernetes 中运行有状态应用程序的基本要求。通过理解和利用 PersistentVolumes 和 PersistentVolumeClaims,以及 StorageClasses 的灵活性,您可以可靠地管理应用程序的数据。本指南提供了入门的基础知识和实践示例,使您能够在 Kubernetes 上部署更复杂、更具弹性的有状态工作负载。