持久化存储:Volume、PersistentVolume 与 Claim
用 PVC 让数据在 Pod 重启之后依然存在,并理解 StorageClass 如何把各部分联系在一起。
容器的文件系统只与容器本身存活一样长。当 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 绑定到适合的任何一个。
hostPathPV,即使用节点上的目录,是常见的演示,但它把数据绑定到一台机器上,只适用于单节点开发。
数据库与 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,而把无状态应用留在集群中。
热门指南
-
Kubernetes 是什么,以及它解决的问题 为什么仅靠容器无法继续扩展,以及 Kubernetes 如何把一组机器变成一个平台。 -
Kubernetes 集群如何工作:控制平面与节点 让集群运转的各个组件,以及从 kubectl apply 到运行中的容器之间发生了什么。 -
Deployment:扩缩、自愈与滚动更新 运行一个 Pod 的多个副本,无停机地更新它们,并在发布出问题时回滚。 -
Service:为短暂的 Pod 提供稳定的网络 给 Pod 一个稳定的地址,在集群内外暴露它们,并限制谁可以与谁通信。 -
调试失败的 Pod:describe、logs、exec 与 port-forward 一份可重复的清单,用来找出 Pod 为什么是 Pending、崩溃或没有响应。