容器重启数据就没了:为什么要持久化

容器的文件系统是临时的:容器一旦被删除重建,里面写的数据就跟着没了。Pod 被调度到别的节点、滚动发布换了新 Pod、节点故障被重新拉起——这些在 K8s 里再正常不过的操作,都会让"存在容器里"的数据消失。日志、上传文件、数据库数据、缓存,凡是需要跨 Pod 生命周期存活的东西,都必须放在持久化存储里。

理解 K8s 存储,先记住一个核心模型:Pod 使用 Volume(数据卷),数据卷背后是真实的存储。Volume 把存储挂载进容器的某个目录,容器的读写落在真实的磁盘/云盘上,Pod 没了数据还在。接下来从最简单的 Volume 讲到生产级的 PV/PVC/CSI,一层层把"数据到底存在哪"讲清楚。

# 为什么容器数据会丢:容器生命周期 = 文件系统生命周期
kubectl delete pod <pod>          # Pod 删除,容器文件系统一并删除
kubectl rollout restart deploy   # 滚动发布,旧 Pod 被替换

# 结论:要跨生命周期保留的数据,必须放 Volume
本文核心知识点
  • VolumePod 内挂载存储的抽象。
  • PV/PVC把存储资源和申请解耦。
  • StorageClass按需动态创建云盘。
  • StatefulSet给有状态应用稳定的卷。
Kubernetes 存储配图:左侧节点方块集群经流动箭头连到中间堆叠存储磁盘与右侧带插槽存储柜的蓝色扁平科技插画
数据卷是 Pod 与真实存储之间的桥,数据因此能跨过容器生命周期。

Volume 基础:emptyDir、hostPath 与局限

最简单的两个 Volume 类型是 emptyDir 和 hostPath。emptyDir 是 Pod 内的临时目录,跟随 Pod 生命周期,Pod 还在时容器间可共享,但 Pod 一删就没了——适合缓存、临时文件;hostPath 直接挂宿主机的目录,数据能跨 Pod 存活,但只能在本节点用,Pod 被调度到别的节点就找不到数据,而且多副本同时写同一目录会有冲突。

# emptyDir:临时共享目录(Pod 删除即消失)
# spec:
#   template:
#     spec:
#       containers:
#         - name: app
#           volumeMounts:
#             - name: cache
#               mountPath: /var/cache
#       volumes:
#         - name: cache
#           emptyDir: {}

hostPath 在单节点测试或日志采集(比如只读挂载宿主容器日志目录)里还有用,但生产多节点环境千万别拿它当业务数据存储。真正生产级的做法,是用后面讲的 PV/PVC 把存储和节点解耦,让数据跟着卷走而不是跟着节点走。

PV 与 PVC:把存储和 Pod 解耦

PV(PersistentVolume) 是集群里的一块真实存储资源,由管理员或存储系统提供;PVC(PersistentVolumeClaim) 是用户对存储的申请。Pod 只声明"我要一块 10Gi 的读写卷",PVC 负责去匹配或创建 PV,应用不用关心存储到底来自哪块盘。这就是"存储供给"和"存储使用"的解耦。

# PV:管理员提供一块 NFS 存储(示意)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-10g
spec:
  capacity:
    storage: 10Gi
  accessModes: ["ReadWriteMany"]
  nfs:
    server: 192.168.1.10
    path: /data/k8s

# PVC:应用申请 5Gi,读写模式与 PV 匹配则绑定
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 5Gi

PV 还有个关键属性——回收策略:PVC 删除后,PV 是 Retain(保留数据,人工处理)、Delete(连数据一起删)还是 Recycle(清空后复用)。生产环境对重要数据一般用 Retain,宁可让运维手动确认,也别让系统顺手把数据抹了。

StorageClass 与动态供给

手写 PV 的静态供给要运维提前备盘,麻烦且浪费。有了 StorageClass,PVC 可以直接声明"给我一块 SSD 云盘",集群里的 Provisioner(供给器) 自动去云厂商创建磁盘并生成 PV——这叫动态供给,用完释放后按策略删除,容量按需分配,最省心。

# StorageClass:让 PVC 动态创建云盘(示意)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs   # 或对应云厂商 provisioner
parameters:
  type: gp3
  fsType: ext4
reclaimPolicy: Delete

# PVC 指定 storageClassName 即可动态创建
# spec:
#   storageClassName: fast-ssd
#   resources:
#     requests:
#       storage: 20Gi

两个容易被忽略的点:reclaimPolicy 决定 PVC 删掉后云盘是保留还是删除,误配 Delete 会把重要数据一起删掉;默认 StorageClass 会被不带 storageClassName 的 PVC 隐式使用,线上集群要明确哪个是默认,别让应用"随手"申请到不合适的存储类型。