Kubernetes 持久化存储:核心方案与实战指南
在说剩余两种负载之前我们来说说K8s的持久化存储。在 Kubernetes 集群中,Pod 作为应用运行的载体具有临时性——Pod 重启、删除或调度到其他节点时,容器内的本地数据会彻底丢失。对于 MySQL、Redis 等需要持久化数据的应用,必须通过持久化存储将数据脱离 Pod 生命周期独立保存。本文将从核心概念入手,详解 EmptyDir、HostPath、NFS、PV/PVC 及 StorageClass 等主流存储方案的原理与实战配置。
一、K8s 持久化存储核心概念
在展开具体方案前,需要明确K8s存储体系的几个关键接口与组件,它们是实现持久化的基础:
-
CSI(Container Storage Interface):容器存储接口,标准化存储插件的接入方式(如 Ceph、GlusterFS 通过 CSI 与 K8s 集成);
-
CNI(Container Network Interface):容器网络接口,虽不直接关联存储,但负责 Pod 网络通信,确保存储访问的网络可达;
-
CRI(Container Runtime Interface):容器运行时接口,定义容器运行时(如 Docker、Containerd)与 K8s 的交互标准,存储卷挂载需依赖 CRI 协调;
-
Volume(存储卷):K8s 中抽象的存储单元,可挂载到 Pod 内的一个或多个容器,数据存储在 Volume 而非容器本地,实现数据与容器解耦。
二、主流持久化存储方案实战
K8s 支持多种 Volume 类型,不同类型适用于不同场景。以下按 “临时存储→节点级存储→共享存储→自动化存储” 的逻辑,逐一讲解核心方案。
1. EmptyDir:Pod 级临时存储
EmptyDir 是最简单的 Volume 类型,生命周期与 Pod 完全绑定——Pod 被调度到节点时自动创建,Pod 删除时数据永久删除,适用于临时数据存储(如容器间共享临时文件、应用缓存)。
1.1 核心特性
-
无需提前创建存储目录,K8s 自动在节点的
/var/lib/kubelet/pods/<Pod-UID>/volumes/kubernetes.io~empty-dir/路径生成目录; -
支持同一 Pod 内多个容器共享数据;
-
数据仅存于节点本地,Pod 调度到其他节点后,原数据无法访问。
1.2 实战配置
创建一个挂载 EmptyDir 的 Nginx Pod,将临时目录 /cache 挂载到容器内:
# emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-empty
spec:
containers:
- name: container-empty
image: nginx:latest
volumeMounts: # 容器内挂载配置
- mountPath: /cache # 容器内挂载路径
name: cache-volume # 关联下面的 Volume 名称
volumes: # 定义 Volume
- name: cache-volume # Volume 名称(需与上面一致)
emptyDir: {} # 类型为 EmptyDir
1.3 验证
- 创建 Pod 并查看调度节点:
kubectl apply -f emptydir.yaml
kubectl get pods -o wide pod-empty # 假设调度到节点 hd2
2.查看节点上的 EmptyDir 实际路径(需登录节点 hd2):
# 先获取 Pod 的 UID
kubectl get pods pod-empty -o yaml | grep uid
# 查看实际目录(替换 <Pod-UID> 为真实值)
ls /var/lib/kubelet/pods/<Pod-UID>/volumes/kubernetes.io~empty-dir/cache-volume/
2. HostPath:节点级持久存储
HostPath 直接挂载节点本地的目录或文件到 Pod,数据存储在节点磁盘上,Pod 删除后数据仍保留在节点中。适用于单节点场景(如测试环境、节点级日志存储),但不支持跨节点共享。
2.1 核心特性
- 数据生命周期与节点绑定,Pod 重新调度到其他节点后无法访问原数据;
- 支持预创建目录(
Directory)、自动创建目录(DirectoryOrCreate)等类型; - 同一节点上的多个 Pod 可通过 HostPath 共享节点本地数据。
2.2 实战配置
创建一个挂载 HostPath 的 Pod,同时运行 Nginx 和 Tomcat 容器,共享节点的 /data1 目录:
# hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
containers:
# Nginx 容器
- name: test-nginx
image: nginx:latest
volumeMounts:
- mountPath: /test-nginx # Nginx 容器内挂载路径
name: test-volume
# Tomcat 容器
- name: test-tomcat
image: tomcat:8.5-jre8-alpine
volumeMounts:
- mountPath: /test-tomcat # Tomcat 容器内挂载路径
name: test-volume
# 定义 HostPath Volume
volumes:
- name: test-volume
hostPath:
path: /data1 # 节点上的目录
type: DirectoryOrCreate # 节点无此目录时自动创建
2.3 验证
- 创建 Pod 并查看调度节点:
kubectl apply -f hostpath.yaml
kubectl get pods -o wide test-hostpath # 假设调度到节点 hd2
2. 验证节点目录与容器共享:
# 登录节点 hd2,在 /data1 下创建目录 aa
ssh hd2 "mkdir /data1/aa"
# 进入 Nginx 容器,查看 /test-nginx 是否有 aa 目录
kubectl exec -it test-hostpath -c test-nginx -- ls /test-nginx # 输出 aa
# 进入 Tomcat 容器,验证共享
kubectl exec -it test-hostpath -c test-tomcat -- ls /test-tomcat # 输出 aa
2.4 缺点与解决
HostPath 存在单点故障:若 Pod 调度到其他节点,原节点的 /data1 数据无法访问。解决此问题需使用分布式共享存储(如 NFS、CephFS)。
3. NFS:跨节点共享存储
NFS(Network File System)是经典的网络共享文件系统,通过网络将服务器的目录共享给多个客户端,K8s 中的 Pod 可跨节点挂载 NFS 共享目录,实现数据跨节点共享。适用于中小规模集群的共享存储场景(如多 Pod 共享配置文件、数据库数据)。
3.1 部署 NFS 服务端
以 K8s 控制节点(IP:192.168.1.11)作为 NFS 服务端:
1.安装 NFS 组件:
yum install nfs-utils -y # CentOS/RHEL
apt install nfs-kernel-server -y # Ubuntu
2.创建共享目录并配置权限:
mkdir /data/volumes -p # 创建共享目录
# 编辑 NFS 配置文件,允许 192.168.1.0/24 网段访问
cat >> /etc/exports << EOF
/data/volumes 192.168.1.0/24(rw,no_root_squash)
EOF
rw:读写权限;no_root_squash:客户端以 root 身份访问时,保留 root 权限(避免权限问题)。
3.启动 NFS 服务并生效配置:
systemctl start nfs
systemctl enable nfs
exportfs -arv # 重新加载配置
3.2 配置 NFS 客户端(所有 K8s 节点)
所有需要挂载 NFS 的节点需安装 NFS 客户端:
yum install nfs-utils -y # 所有节点执行
3.3 实战:Pod 挂载 NFS
创建一个 Nginx Pod,挂载 NFS 共享目录 /data/volumes 到容器的 /usr/share/nginx/html(Nginx 网页根目录):
# nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-nfs-volume
spec:
containers:
- name: test-nfs
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nfs-volumes
mountPath: /usr/share/nginx/html # 挂载到 Nginx 网页目录
volumes:
- name: nfs-volumes
nfs: # 类型为 NFS
server: 192.168.1.11 # NFS 服务端 IP
path: /data/volumes # NFS 共享目录
3.4 验证
- 创建 Pod 并查看调度节点:
kubectl apply -f nfs.yaml
kubectl get pods -o wide test-nfs-volume # 假设调度到节点 hd3
2.在 NFS 服务端创建测试页面,验证 Pod 挂载:
# NFS 服务端(192.168.1.11)创建 index.html
echo "Hello NFS Storage" > /data/volumes/index.html
# 访问 Pod 的 IP,验证页面内容
kubectl get pods test-nfs-volume -o jsonpath='{.status.podIP}' # 获取 Pod IP
curl <Pod-IP> # 输出 "Hello NFS Storage"
3.5 缺点
NFS 服务端存在单点故障:若 NFS 服务器宕机,所有挂载该 NFS 的 Pod 无法访问数据。生产环境需使用高可用 NFS 或分布式存储(如 CephFS、GlusterFS)。
4. PV/PVC:存储资源的 “申请 - 分配” 模型
PersistentVolume(PV)和 PersistentVolumeClaim(PVC)是 K8s 中解耦存储管理与应用使用的核心机制:
-
PV:由集群管理员创建的 “存储资源”,对应实际的存储设备(如 NFS 目录、Ceph 块存储),生命周期独立于 Pod;
-
PVC:由应用开发者创建的 “存储请求”,声明需要的存储大小、访问模式(如读写、只读),K8s 会自动匹配符合条件的 PV 并绑定。
4.1 核心概念
| 术语 | 作用 |
|---|---|
| PV | 集群级存储资源,定义存储类型、大小、访问模式、回收策略等 |
| PVC | 命名空间级存储请求,声明需要的存储资源,与 PV 一对一绑定 |
| 访问模式 | PV/PVC 支持的访问方式:RWO(单节点读写)、RWX(多节点读写)、ROX(多节点只读) |
| 回收策略 | PV 被释放后的处理方式:Retain(保留数据,手动清理)、Delete(删除 PV 及数据) |
4.2 实战:基于 NFS 的 PV/PVC 配置
步骤 1:准备 NFS 共享目录(扩展)
在 NFS 服务端(192.168.1.11)创建 10 个独立目录,用于创建 10 个 PV:
mkdir /data/volume_test/v{1..10} -p
# 更新 NFS 配置,共享这些目录
cat >> /etc/exports << EOF
/data/volume_test/v1 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v2 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v3 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v4 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v5 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v6 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v7 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v8 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v9 192.168.1.0/24(rw,no_root_squash)
/data/volume_test/v10 192.168.1.0/24(rw,no_root_squash)
EOF
exportfs -arv # 生效配置
步骤 2:创建 PV
定义 10 个 PV,分别对应 NFS 的 10 个目录,配置不同的大小和访问模式:
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: v1 # PV 名称
spec:
capacity:
storage: 1Gi # PV 大小
accessModes: ["ReadWriteOnce"] # 单节点读写
nfs:
path: /data/volume_test/v1 # 关联 NFS 目录
server: 192.168.1.11
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
spec:
capacity:
storage: 2Gi
accessModes: ["ReadWriteMany"] # 多节点读写
nfs:
path: /data/volume_test/v2
server: 192.168.1.11
# 省略 v3-v10 配置(类似 v1/v2,调整 name、storage、path)
创建 PV 并查看状态:
kubectl apply -f pv.yaml
kubectl get pv # 状态为 Available,表示可用
步骤 3:创建 PVC
创建一个 PVC,请求 2Gi 大小、支持多节点读写(RWX)的存储:
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc # PVC 名称
spec:
accessModes: ["ReadWriteMany"] # 与 PV 的访问模式匹配
resources:
requests:
storage: 2Gi # 请求的存储大小
创建 PVC 并查看绑定状态:
kubectl apply -f pvc.yaml
kubectl get pvc # 状态为 Bound,表示已与 PV 绑定(此处绑定 v2)
kubectl get pv v2 # 状态变为 Bound,关联 PVC default/my-pvc
步骤 4:Pod 挂载 PVC
创建 Nginx Pod,挂载 my-pvc 这个 PVC:
# pod_pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pvc
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: nginx-html
mountPath: /usr/share/nginx/html # 挂载到 Nginx 网页目录
volumes:
- name: nginx-html
persistentVolumeClaim:
claimName: my-pvc # 关联 PVC 名称
验证数据持久化:
kubectl apply -f pod_pvc.yaml
# 在 NFS 服务端的 v2 目录创建测试页面
echo "Hello PV/PVC" > /data/volume_test/v2/index.html
# 访问 Pod,验证内容
curl $(kubectl get pods pod-pvc -o jsonpath='{.status.podIP}') # 输出 "Hello PV/PVC"
4.3 回收策略验证(Retain)
默认回收策略为 Retain,删除 PVC 后 PV 状态变为 Released,数据保留:
kubectl delete pvc my-pvc
kubectl get pv v2 # 状态为 Released
# 若需重新使用,需手动删除 PV(数据仍在 NFS 目录)
kubectl delete pv v2
# 重新创建 PVC 会再次匹配可用 PV(或新建 PV)
5. StorageClass:动态生成 PV 的自动化方案
PV/PVC 模式需管理员手动创建大量 PV,运维成本高。StorageClass 作为 “PV 模板”,可动态生成 PV—— 当 PVC 请求存储时,K8s 调用 StorageClass 关联的 “存储供应者(Provisioner)” 自动创建 PV,实现存储管理自动化。
5.1 核心组件
-
Provisioner:存储供应者,负责根据 StorageClass 配置创建实际存储(如 NFS Provisioner 自动创建 NFS 目录并生成 PV);
-
orageClass:定义 PV 的模板参数(如存储类型、回收策略、挂载选项),关联 Provisioner;
-
动态供给:PVC 引用 StorageClass 后,K8s 自动触发 Provisioner 创建 PV 并完成绑定。
5.2 实战:基于 NFS 的 StorageClass 配置
以 NFS 为例,需先部署 NFS Provisioner(作为存储供应者),再创建 StorageClass,最终通过 PVC 动态生成 PV。
步骤 1:部署 NFS Provisioner
NFS Provisioner 是一个容器化工具,负责监听 PVC 请求,自动在 NFS 服务端创建目录并生成 PV。
-
创建 ServiceAccount(授权用)
Provisioner 需要权限操作 PV/PVC 资源,需创建专用 ServiceAccount 并授权:
# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner # 服务账号名称
kubectl apply -f serviceaccount.yaml
# 绑定集群管理员权限(简化测试,生产环境需最小权限)
kubectl create clusterrolebinding nfs-provisioner-binding \
--clusterrole=cluster-admin \
--serviceaccount=default:nfs-provisioner
2. 准备 NFS 共享目录
在 NFS 服务端(192.168.1.11)创建 Provisioner 专用共享目录:
mkdir /data/nfs_pro -p
# 更新 NFS 配置,允许集群网段访问
cat >> /etc/exports << EOF
/data/nfs_pro 192.168.1.0/24(rw,no_root_squash)
EOF
exportfs -arv # 生效配置
3.部署 NFS Provisioner 容器
使用现成的 NFS Provisioner 镜像(如 registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0):
# nfs-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-provisioner
spec:
replicas: 1
selector:
matchLabels:
app: nfs-provisioner
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccount: nfs-provisioner # 关联服务账号
containers:
- name: nfs-provisioner
image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
env:
- name: PROVISIONER_NAME # Provisioner 名称(后续 StorageClass 需引用)
value: example.com/nfs
- name: NFS_SERVER # NFS 服务端 IP
value: 192.168.1.11
- name: NFS_PATH # NFS 共享目录
value: /data/nfs_pro
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes # Provisioner 工作目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.1.11
path: /data/nfs_pro
kubectl apply -f nfs-deployment.yaml
# 验证 Provisioner 运行状态(需为 Running)
kubectl get pods -l app=nfs-provisioner
步骤 2:创建 StorageClass
定义 StorageClass,关联上述 Provisioner,指定默认回收策略:
# nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-sc # StorageClass 名称(后续 PVC 需引用)
provisioner: example.com/nfs # 必须与 Provisioner 的 PROVISIONER_NAME 一致
reclaimPolicy: Delete # 回收策略:删除 PVC 时自动删除 PV 及 NFS 目录
allowVolumeExpansion: true # 允许后续扩容 PVC
kubectl apply -f nfs-storageclass.yaml
# 验证 StorageClass(默认存储类可加 annotation: storageclass.kubernetes.io/is-default-class: "true")
kubectl get sc # 简写,全称 storageclass
步骤 3:创建 PVC 并动态生成 PV
创建 PVC 时引用 nfs-sc 这个 StorageClass,K8s 会自动触发 Provisioner 生成 PV:
# pvc-dynamic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc-dynamic
spec:
accessModes: ["ReadWriteMany"] # 多节点读写
resources:
requests:
storage: 1Gi # 请求 1Gi 存储
storageClassName: nfs-sc # 引用 StorageClass 名称
kubectl apply -f pvc-dynamic.yaml
# 验证:PVC 状态变为 Bound,且自动生成 PV
kubectl get pvc test-pvc-dynamic # STATUS: Bound
kubectl get pv # 新增一个 PV,名称格式为 pvc-<PVC-UID>,关联该 PVC
步骤 4:Pod 挂载动态 PVC
与挂载静态 PVC 一致,直接引用动态生成的 PVC 名称:
# pod-dynamic.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-dynamic-storage
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: dynamic-volume
mountPath: /usr/share/nginx/html
volumes:
- name: dynamic-volume
persistentVolumeClaim:
claimName: test-pvc-dynamic # 引用动态 PVC
kubectl apply -f pod-dynamic.yaml
# 验证数据写入:在 NFS 自动创建的目录中写文件,Pod 内可访问
echo "Dynamic PV from StorageClass" > /data/nfs_pro/default-test-pvc-dynamic-pvc-xxx/index.html
curl $(kubectl get pods pod-dynamic-storage -o jsonpath='{.status.podIP}') # 输出写入内容
5.3 StorageClass 核心优势
- 自动化运维:无需手动创建 PV,PVC 提交后自动生成,降低运维成本;
- 标准化配置:通过 StorageClass 统一管理 PV 模板(如存储类型、权限),避免配置混乱;
- 支持扩容:开启
allowVolumeExpansion: true后,可直接编辑 PVC 扩容存储(仅支持扩容,不支持缩容)。
三、总结:存储方案选型建议
| 存储方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| EmptyDir | 临时数据、容器间共享临时文件(如缓存) | 无需配置,随 Pod 自动创建 | 数据不持久,Pod 删除后丢失 |
| HostPath | 单节点测试、节点级日志存储 | 配置简单,数据在节点本地持久化 | 不支持跨节点,存在单点故障 |
| NFS | 中小规模集群、多 Pod 共享数据(如配置文件) | 跨节点共享,部署简单 | 服务端单点故障(需高可用 NFS 集群) |
| PV/PVC | 生产环境固定存储需求(如数据库) | 解耦存储与应用,支持权限控制 | 需手动创建 PV,运维成本高 |
| StorageClass | 生产环境大规模集群、动态存储需求 | 自动化生成 PV,支持标准化配置、扩容 | 需部署 Provisioner,初始配置稍复杂 |
生产环境中,推荐优先使用 StorageClass + 分布式存储(如 CephFS、GlusterFS),兼顾自动化运维与高可用性;中小规模集群或测试场景,可使用 NFS 或 HostPath 简化配置。
注:点赞+关注+收藏,下期我们接着讲剩余两种工作负载,不见不散。
更多推荐



所有评论(0)