第三部分:Kubernetes 存储管理实战
从临时存储到持久化卷的完整存储体系
目录
第5章:Kubernetes 存储抽象与 PV/PVC 机制
5.1 为什么需要持久化存储
容器默认的文件系统是临时的(ephemeral),一旦 Pod 被删除、重启或迁移,数据就会消失。
K8s 提供了三种方式来管理数据:
| 存储类型 | 生命周期 | 用途 | 示例 |
|---|---|---|---|
| EmptyDir | Pod 生命周期内 | 临时缓存、共享空间 | 编译缓存、临时文件 |
| HostPath | 与宿主机目录绑定 | 调试、本地卷 | 日志收集、配置文件 |
| PersistentVolume (PV) | 独立于 Pod | 数据库、日志、业务数据 | MySQL 数据、应用日志 |
5.2 容器文件系统的分层原理
在理解 K8s 存储之前,要先明白容器层文件系统的结构:
容器文件系统:
OverlayFS = 可写层 (Writable) + 镜像层 (Image Layers)
当你执行:
docker run -v /data:/data nginx
实际上挂载的是一个新的卷层叠加到容器可写层。
查看容器挂载:
docker inspect <container_id> | grep Mounts -A 10
5.3 Kubernetes Volume 类型总览
| 类型 | 说明 | 典型场景 | 示例 |
|---|---|---|---|
| emptyDir | Pod 临时存储 | 缓存/中间文件 | 编译缓存 |
| hostPath | 宿主机目录 | 调试/日志/配置 | 日志收集 |
| nfs | 网络文件系统 | 共享数据 | 共享存储 |
| configMap | 挂载配置文件 | 环境配置 | 应用配置 |
| secret | 加密凭据 | 密钥、Token | 数据库密码 |
| persistentVolumeClaim | 持久卷声明 | 数据库、持久存储 | MySQL 数据 |
| local | 节点本地盘 | 高性能存储 | 数据库存储 |
5.4 PV / PVC 的核心机制
Kubernetes 的持久化存储是通过 三层抽象 实现的:
Pod → PVC → PV → 存储后端(NFS、Ceph、Local、EBS...)
5.4.1 PersistentVolume(PV)
系统中定义好的实际存储资源。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data
server: 192.168.1.100
5.4.2 PersistentVolumeClaim(PVC)
Pod 用于"申请"存储的声明。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
Kubernetes 会自动匹配符合条件的 PV。
5.4.3 Pod 使用 PVC
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: mydata
volumes:
- name: mydata
persistentVolumeClaim:
claimName: mypvc
5.5 PV 生命周期与回收策略
| 阶段 | 说明 | 状态 |
|---|---|---|
| Available | 可用 PV | 等待绑定 |
| Bound | 已绑定某个 PVC | 正在使用 |
| Released | PVC 已删除,但数据未清除 | 等待回收 |
| Failed | 无法使用或错误状态 | 需要人工处理 |
回收策略:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| Retain | 手动删除数据 | 数据库卷、重要数据 |
| Recycle | 清空数据(已废弃) | 已废弃 |
| Delete | 自动删除后端卷 | 云盘、临时数据 |
5.6 StorageClass 与动态卷分配
5.6.1 StorageClass 定义
StorageClass 定义了"存储供应方式":
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: Immediate
5.6.2 PVC 自动绑定 StorageClass
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast
resources:
requests:
storage: 10Gi
当 PVC 创建时,系统会:
- 根据 storageClassName 调用 provisioner
- 自动创建 PV
- 自动绑定
5.7 存储访问模式(Access Modes)
| 模式 | 含义 | 适用场景 | 示例 |
|---|---|---|---|
| ReadWriteOnce (RWO) | 单节点读写 | 大多数应用 | 数据库、应用数据 |
| ReadOnlyMany (ROX) | 多节点只读 | 配置共享 | 配置文件、只读数据 |
| ReadWriteMany (RWX) | 多节点读写 | NFS / CephFS | 共享存储、日志 |
| ReadWriteOncePod (RWOP) | 单Pod独占 | 高一致性数据库 | 单实例数据库 |
第9章:存储类型与 CSI 驱动实战
9.1 常见存储类型与实现
| 类型 | 机制 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| HostPath | 挂载宿主机路径 | 简单 | 不可迁移 | 调试、单节点 |
| NFS | 网络文件系统 | 共享读写 | 性能一般 | 共享存储 |
| CephFS / RBD | 分布式存储 | 高可用 | 维护复杂 | 生产环境 |
| Local Path | 本地盘绑定 | 高性能 | 不可调度 | 高性能应用 |
| GlusterFS | 块分布式存储 | 弹性扩展 | 运维成本高 | 大规模存储 |
| EBS / OSS / CSI | 云盘存储 | 自动化 | 受限于云厂商 | 云环境 |
9.2 CSI(Container Storage Interface)机制
CSI 是容器存储的标准接口(类似 CNI 之于网络)。
9.2.1 CSI 架构
Kubelet
↕
CSI Driver (Node + Controller)
↕
存储后端(NFS、Ceph、EBS、NAS...)
9.2.2 工作流程
- Pod 申请 PVC
- kubelet 调用 CSI Node 插件
- 创建卷并挂载到容器
- 卷生命周期与 PVC 绑定
9.2.3 典型 CSI 插件
| 插件 | 存储后端 | 特点 | 适用场景 |
|---|---|---|---|
| csi-hostpath | 本地路径 | 简单 | 开发测试 |
| csi-nfs | NFS | 共享存储 | 小规模生产 |
| csi-cephfs | CephFS | 分布式 | 大规模生产 |
| csi-aws-ebs | AWS EBS | 云存储 | AWS 环境 |
| alibaba-cloud-csi | 阿里云盘 | 云存储 | 阿里云环境 |
存储性能优化与测试
性能测试工具:fio
fio 是专业的 I/O 性能测试工具,支持多种 I/O 模式。
基础 fio 测试
# 随机读测试
fio --name=randread --filename=/data/test.file --bs=4k --iodepth=32 --rw=randread --numjobs=4 --time_based --runtime=60 --group_reporting
# 随机写测试
fio --name=randwrite --filename=/data/test.file --bs=4k --iodepth=32 --rw=randwrite --numjobs=4 --time_based --runtime=60 --group_reporting
# 混合读写测试
fio --name=mixed --filename=/data/test.file --bs=4k --iodepth=32 --rw=randrw --rwmixread=70 --numjobs=4 --time_based --runtime=60 --group_reporting
在 Kubernetes 中运行 fio 测试
apiVersion: v1
kind: Pod
metadata:
name: fio-test
spec:
containers:
- name: fio
image: nixery.dev/shell/fio
command: ["sh", "-c"]
args:
- |
fio --name=randread \
--filename=/data/test.file \
--bs=4k \
--iodepth=32 \
--rw=randread \
--numjobs=4 \
--time_based \
--runtime=60 \
--group_reporting
volumeMounts:
- name: test-volume
mountPath: /data
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: test-pvc
存储性能优化方向
| 优化点 | 建议 | 命令示例 |
|---|---|---|
| IO 性能 | 使用本地 SSD 或 Ceph | fio --rw=randread |
| 网络延迟 | 挂载同可用区节点 | kubectl get nodes -o wide |
| 并发写入 | 避免 RWX 并发数据库 | 使用 RWO 模式 |
| 缓存 | 使用 emptyDir + 内存模式 | emptyDir: { medium: Memory } |
| 文件系统 | 使用 ext4/XFS,禁用 journaling | mkfs.xfs /dev/sdb |
| Cloud Storage | 合理设置 IOPS / 吞吐配额 | 云控制台配置 |
存储排障完整案例
案例1:PVC 一直 Pending
问题描述
PVC 创建后一直处于 Pending 状态,无法绑定到 PV。
排查步骤
#!/bin/bash
# PVC Pending 排查脚本
PVC_NAME="my-pvc"
echo "=== PVC Pending 排查 ==="
# 1. 查看 PVC 状态
echo "1. 查看 PVC 状态:"
kubectl get pvc $PVC_NAME -o yaml
# 2. 查看 PVC 事件
echo "2. 查看 PVC 事件:"
kubectl describe pvc $PVC_NAME
# 3. 查看 StorageClass
echo "3. 查看 StorageClass:"
kubectl get storageclass
kubectl describe storageclass
# 4. 查看可用 PV
echo "4. 查看可用 PV:"
kubectl get pv
# 5. 检查 CSI 驱动状态
echo "5. 检查 CSI 驱动状态:"
kubectl get pods -n kube-system | grep -E "(csi|storage)"
# 6. 查看 CSI 驱动日志
echo "6. 查看 CSI 驱动日志:"
kubectl logs -n kube-system -l app=csi-driver --tail=50
# 7. 检查节点存储
echo "7. 检查节点存储:"
kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}' | xargs -I {} ssh {} "df -h"
# 8. 检查存储配额
echo "8. 检查存储配额:"
kubectl describe quota --all-namespaces
案例2:Pod 挂载失败
问题描述
Pod 启动时无法挂载存储卷,显示挂载失败错误。
排查步骤
#!/bin/bash
# Pod 挂载失败排查脚本
POD_NAME="my-pod"
echo "=== Pod 挂载失败排查 ==="
# 1. 查看 Pod 状态
echo "1. 查看 Pod 状态:"
kubectl get pod $POD_NAME -o wide
# 2. 查看 Pod 事件
echo "2. 查看 Pod 事件:"
kubectl describe pod $POD_NAME
# 3. 查看 Pod 日志
echo "3. 查看 Pod 日志:"
kubectl logs $POD_NAME
# 4. 检查 PVC 状态
echo "4. 检查 PVC 状态:"
kubectl get pvc
kubectl describe pvc
# 5. 检查 PV 状态
echo "5. 检查 PV 状态:"
kubectl get pv
kubectl describe pv
# 6. 检查节点存储
echo "6. 检查节点存储:"
NODE_NAME=$(kubectl get pod $POD_NAME -o jsonpath='{.spec.nodeName}')
kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}' | xargs -I {} ssh {} "df -h"
# 7. 检查挂载点
echo "7. 检查挂载点:"
kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}' | xargs -I {} ssh {} "mount | grep -E '(nfs|ceph|local)'"
# 8. 检查 CSI 驱动日志
echo "8. 检查 CSI 驱动日志:"
kubectl logs -n kube-system -l app=csi-driver --tail=100
案例3:存储性能问题
问题描述
应用访问存储时延迟过高,I/O 性能不满足要求。
排查步骤
#!/bin/bash
# 存储性能问题排查脚本
echo "=== 存储性能问题排查 ==="
# 1. 创建性能测试 Pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: storage-test
spec:
containers:
- name: test
image: nixery.dev/shell/fio
command: ["sh", "-c"]
args:
- |
echo "开始存储性能测试..."
fio --name=randread \
--filename=/data/test.file \
--bs=4k \
--iodepth=32 \
--rw=randread \
--numjobs=4 \
--time_based \
--runtime=30 \
--group_reporting
volumeMounts:
- name: test-volume
mountPath: /data
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: test-pvc
EOF
# 2. 等待 Pod 就绪
kubectl wait --for=condition=Ready pod storage-test --timeout=60s
# 3. 运行性能测试
echo "3. 运行性能测试:"
kubectl exec storage-test -- fio --name=randread --filename=/data/test.file --bs=4k --iodepth=32 --rw=randread --numjobs=4 --time_based --runtime=30 --group_reporting
# 4. 检查节点 I/O 状态
echo "4. 检查节点 I/O 状态:"
kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}' | xargs -I {} ssh {} "iostat -x 1 5"
# 5. 检查网络延迟(NFS)
echo "5. 检查网络延迟:"
kubectl exec storage-test -- ping -c 10 nfs-server
# 6. 检查文件系统类型
echo "6. 检查文件系统类型:"
kubectl exec storage-test -- df -T /data
# 7. 检查 I/O 调度器
echo "7. 检查 I/O 调度器:"
kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}' | xargs -I {} ssh {} "cat /sys/block/sda/queue/scheduler"
# 8. 清理测试 Pod
kubectl delete pod storage-test
实验环境搭建
快速搭建脚本
#!/bin/bash
# Kubernetes 存储实验环境搭建脚本
set -e
echo "开始搭建 Kubernetes 存储实验环境..."
# 1. 创建实验集群
kind create cluster --name storage-test --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/12"
EOF
# 2. 等待集群就绪
kubectl wait --for=condition=Ready node --all --timeout=60s
# 3. 安装 NFS 服务器(用于测试)
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-server
spec:
replicas: 1
selector:
matchLabels:
app: nfs-server
template:
metadata:
labels:
app: nfs-server
spec:
containers:
- name: nfs-server
image: k8s.gcr.io/volume-nfs:0.8
ports:
- containerPort: 2049
volumeMounts:
- name: nfs-data
mountPath: /exports
volumes:
- name: nfs-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: nfs-server
spec:
selector:
app: nfs-server
ports:
- port: 2049
targetPort: 2049
EOF
# 4. 等待 NFS 服务器就绪
kubectl wait --for=condition=Ready pod -l app=nfs-server --timeout=60s
# 5. 创建 NFS PV
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: nfs-server
path: /exports
EOF
# 6. 创建 NFS StorageClass
kubectl apply -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
EOF
# 7. 安装测试工具
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: storage-tools
spec:
replicas: 1
selector:
matchLabels:
app: storage-tools
template:
metadata:
labels:
app: storage-tools
spec:
containers:
- name: tools
image: nixery.dev/shell/fio
command: ["sleep", "3600"]
EOF
echo "Kubernetes 存储实验环境搭建完成!"
echo "运行 'kubectl get pv' 查看 PV 状态"
echo "运行 'kubectl get pvc' 查看 PVC 状态"
测试用例集合
# 创建测试用例
kubectl apply -f - <<EOF
# 测试用例1:基础 PVC 绑定
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc-1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs-storage
---
# 测试用例2:多访问模式 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc-2
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
storageClassName: nfs-storage
---
# 测试用例3:大容量 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc-3
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: nfs-storage
EOF
命令速查表
存储管理命令
| 命令 | 功能 | 示例 |
|---|---|---|
kubectl get pv | 查看 PV | kubectl get pv |
kubectl get pvc | 查看 PVC | kubectl get pvc |
kubectl get storageclass | 查看 StorageClass | kubectl get storageclass |
kubectl describe pv <pv> | 查看 PV 详细信息 | kubectl describe pv nfs-pv |
kubectl describe pvc <pvc> | 查看 PVC 详细信息 | kubectl describe pvc test-pvc |
kubectl describe storageclass <sc> | 查看 StorageClass 详细信息 | kubectl describe storageclass nfs-storage |
存储测试命令
| 命令 | 功能 | 示例 |
|---|---|---|
fio --name=test --filename=/data/test.file --bs=4k --iodepth=32 --rw=randread --numjobs=4 --time_based --runtime=60 --group_reporting | 随机读测试 | 在 Pod 中运行 |
fio --name=test --filename=/data/test.file --bs=4k --iodepth=32 --rw=randwrite --numjobs=4 --time_based --runtime=60 --group_reporting | 随机写测试 | 在 Pod 中运行 |
fio --name=test --filename=/data/test.file --bs=4k --iodepth=32 --rw=randrw --rwmixread=70 --numjobs=4 --time_based --runtime=60 --group_reporting | 混合读写测试 | 在 Pod 中运行 |
dd if=/dev/zero of=/data/test.file bs=1M count=1000 | 顺序写测试 | 在 Pod 中运行 |
dd if=/data/test.file of=/dev/null bs=1M | 顺序读测试 | 在 Pod 中运行 |
系统存储命令
| 命令 | 功能 | 示例 |
|---|---|---|
df -h | 查看磁盘使用情况 | df -h |
lsblk | 查看块设备 | lsblk |
mount | 查看挂载点 | mount |
iostat -x 1 | 查看 I/O 统计 | iostat -x 1 |
iotop | 查看 I/O 进程 | iotop |
du -sh <path> | 查看目录大小 | du -sh /data |
存储排障命令
| 命令 | 功能 | 示例 |
|---|---|---|
kubectl get events --sort-by=.lastTimestamp | 查看事件 | kubectl get events --sort-by=.lastTimestamp |
kubectl logs -n kube-system -l app=csi-driver | 查看 CSI 驱动日志 | kubectl logs -n kube-system -l app=csi-driver |
kubectl describe pod <pod> | 查看 Pod 详细信息 | kubectl describe pod my-pod |
kubectl exec -it <pod> -- sh | 进入 Pod 执行命令 | kubectl exec -it my-pod -- sh |