Kubernetes Kubernetes

持久化存储:Volume、PersistentVolume 与 Claim

用 PVC 让数据在 Pod 重启之后依然存在,并理解 StorageClass 如何把各部分联系在一起。

14 min 中级

容器的文件系统只与容器本身存活一样长。当 Pod 被替换时,它写入的任何内容都会消失。这对无状态的 Web 服务器来说没问题,但对于数据库、上传的文件,以及一切必须存活的东西来说就是问题。本指南展示 Kubernetes 如何给 Pod 挂载持久的存储。

为什么容器存储还不够

容器应当小而可丢弃,所以把数据放在容器内部违背了这一模型。数据必须存放在容器之外、Pod 之外,放在 Pod 启动时由 Kubernetes 挂载的存储上。Kubernetes 对这种存储是什么保持中立:云磁盘、网络共享,或本地驱动器。它也不会为你备份数据。那仍然是你的工作。

存储链

四个对象协同工作:

  • StorageClass 描述一种存储,以及如何按需创建它(例如”来自云服务商的快速 SSD 卷”)。
  • PersistentVolume(PV) 是集群中一块实际的存储。
  • PersistentVolumeClaim(PVC) 是用户或应用对存储的请求:“我需要 1 GiB,一个节点可以对其进行读写。”
  • Pod 把该 claim 作为卷引用,并将其挂载进容器。
Container ─ mountPath ─▶ Pod volume ─▶ PVC ─ binds to ─▶ PV ─▶ real storage
                                                          ▲
                                        StorageClass creates PVs on demand

一个 PVC 恰好绑定到一个 PV。Pod 从不引用 PV,只引用 claim,这正是让应用独立于存储细节的原因。

用 PVC 请求存储

大多数集群都有一个默认的 StorageClass,可以按需创建卷(称为动态供给),minikube 也包含一个。检查你的:

kubectl get storageclass

然后请求存储:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
kubectl apply -f pvc.yaml
kubectl get pvc,pv

该 claim 应该变为 Bound,并出现一个匹配的 PV。有些存储类会等到 Pod 真正使用该 claim 时才创建卷,所以此时的 Pending 不一定是错误。

在 Pod 中挂载它并证明持久性

apiVersion: v1
kind: Pod
metadata:
  name: storage-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: data

写入一个文件,销毁 Pod,重新创建它,然后检查文件是否存活:

kubectl apply -f pod.yaml
kubectl exec storage-demo -- sh -c 'echo persisted > /data/proof.txt'
kubectl delete pod storage-demo
kubectl apply -f pod.yaml
kubectl exec storage-demo -- cat /data/proof.txt

新的 Pod 是一个完全不同的容器,但 claim 以及它背后的数据被重新挂载了。

访问模式、回收策略与静态卷

  • 访问模式说明卷可以如何被挂载。ReadWriteOnce 表示被单个节点读写,所以该节点上的多个 Pod 仍然可以共享它。ReadOnlyMany 和 ReadWriteMany 允许多个节点,如果存储支持的话。ReadWriteOncePod 则限制为单个 Pod。
  • 回收策略决定当 claim 被删除时卷会怎样。动态供给的卷通常默认为 Delete,它会移除底层存储及其数据。当你想保留数据时,使用 Retain。
  • 静态供给意味着由管理员手工创建 PV,claim 绑定到适合的任何一个。hostPath PV,即使用节点上的目录,是常见的演示,但它把数据绑定到一台机器上,只适用于单节点开发。

数据库与 StatefulSet

你可以把一个 claim 挂载到 Deployment 的 Pod 中,但 Deployment 的副本是可互换且完全相同的,而大多数数据库并不是这样工作的。StatefulSet 是为有状态工作负载而构建的。它给每个 Pod 一个稳定的身份(db-0、db-1),按顺序启动并更新它们,并且可以为每个副本创建独立的 PVC:

spec:
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 5Gi

StatefulSet 处理身份和存储,而不是数据复制。让副本之间保持一致是数据库自己的工作,这也是在 Kubernetes 上运行数据库比运行无状态服务更难的原因。许多团队使用托管数据库或数据库 operator,而把无状态应用留在集群中。

热门指南