📘 Day 14:StorageClass 与动态供给
🎯 今日目标
🧠 理论精讲(30 分钟)
为什么需要 StorageClass
静态 PV 的问题:每次创建 PVC 前都要手动创建 PV,管理员负担重。
1 2
| 动态供给: PVC 创建 → StorageClass → Provisioner → 自动创建 PV → 绑定 PVC
|
StorageClass 关键参数
| 参数 |
含义 |
provisioner |
供给驱动(如 nfs.csi.k8s.io) |
volumeBindingMode |
Immediate(立即)/ WaitForFirstConsumer(延迟) |
reclaimPolicy |
Delete(默认)/ Retain |
allowVolumeExpansion |
是否允许 PVC 扩容 |
volumeBindingMode
| 模式 |
行为 |
| Immediate |
PVC 创建后立即绑定 PV(不考虑 Pod 调度) |
| WaitForFirstConsumer |
等到 Pod 实际使用时才创建 PV(保证 PV 和 Pod 在同一可用区) |
🔧 动手实操(120 分钟)
练习 14.1:NFS 动态供给环境准备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
sudo apt-get update sudo apt-get install -y nfs-kernel-server
sudo mkdir -p /srv/nfs/k8s sudo chown nobody:nogroup /srv/nfs/k8s sudo chmod 777 /srv/nfs/k8s
echo "/srv/nfs/k8s *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports sudo exportfs -rav sudo systemctl restart nfs-kernel-server
|
练习 14.2:部署 NFS Subdir External Provisioner
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner namespace: default --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: nfs-provisioner-runner rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: nfs-provisioner-runner subjects: - kind: ServiceAccount name: nfs-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io --- apiVersion: apps/v1 kind: Deployment metadata: name: nfs-provisioner spec: replicas: 1 selector: matchLabels: app: nfs-provisioner strategy: type: Recreate template: metadata: labels: app: nfs-provisioner spec: serviceAccountName: nfs-provisioner containers: - name: nfs-provisioner image: registry.cn-hangzhou.aliyuncs.com/google_containers/nfs-subdir-external-provisioner:v4.0.2 env: - name: PROVISIONER_NAME value: k8s-sigs.io/nfs-subdir-external-provisioner - name: NFS_SERVER value: "10.0.0.1" # 替换为你的 NFS 服务器 IP - name: NFS_PATH value: /srv/nfs/k8s volumeMounts: - name: nfs-root mountPath: /persistentvolumes volumes: - name: nfs-root nfs: server: 10.0.0.1 # 替换为你的 NFS 服务器 IP path: /srv/nfs/k8s EOF
cat <<EOF | kubectl apply -f - apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-sc annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: k8s-sigs.io/nfs-subdir-external-provisioner reclaimPolicy: Delete volumeBindingMode: Immediate EOF
kubectl get sc
kubectl get pod -l app=nfs-provisioner
|
练习 14.3:动态创建 PVC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| cat <<EOF | kubectl apply -f - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dynamic-pvc spec: storageClassName: nfs-sc accessModes: - ReadWriteMany resources: requests: storage: 1Gi EOF
kubectl get pvc dynamic-pvc
kubectl get pv
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: dynamic-pod spec: containers: - name: app image: busybox:1.36 command: - sh - -c - | echo "Dynamic provision test" > /data/test.txt cat /data/test.txt sleep 3600 volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: dynamic-pvc EOF
ls /srv/nfs/k8s/
kubectl delete pod dynamic-pod kubectl delete pvc dynamic-pvc
|
练习 14.4:PVC 扩容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| kubectl get sc nfs-sc
kubectl patch sc nfs-sc -p '{"allowVolumeExpansion":true}'
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: expandable-pvc spec: storageClassName: nfs-sc accessModes: ["ReadWriteMany"] resources: requests: storage: 1Gi EOF
kubectl patch pvc expandable-pvc -p '{"spec":{"resources":{"requests":{"storage":"3Gi"}}}}'
kubectl get pvc expandable-pvc -w
kubectl get pvc expandable-pvc kubectl get pv | grep expandable-pvc
kubectl delete pvc expandable-pvc
|
🐛 排错练习(30 分钟)
场景 1:PVC Pending — Provisioner 未运行
1 2 3 4 5 6 7 8 9 10 11 12
|
kubectl get pod -l app=nfs-provisioner
kubectl logs -l app=nfs-provisioner --tail=50
kubectl exec -it <provisioner-pod> -- sh -c "showmount -e <NFS_SERVER>"
kubectl get sc nfs-sc -o yaml | grep provisioner
|
场景 2:扩容失败
1 2 3 4 5 6 7 8
| kubectl get sc <name> -o yaml | grep allowVolumeExpansion
|
🏆 赛题模拟(40 分钟)
⚠️ 严格限时 40 分钟
题目:动态存储与扩容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 【前置条件】 NFS 服务器已部署(IP: 10.0.0.1),共享路径 /srv/nfs/k8s
【操作要求】
1. 部署 NFS Subdir External Provisioner 2. 创建 StorageClass nfs-retain(provisioner 指向 NFS,reclaimPolicy=Retain) 3. 将此 StorageClass 设为默认 4. 创建 PVC data-pvc:5Gi, ReadWriteMany 5. 验证自动创建了 PV 6. 创建 Pod data-app 使用此 PVC,挂载到 /app-data,写入测试数据 7. 将 PVC 扩容到 8Gi 8. 验证数据在扩容后仍然完整 9. 删除 Pod 和 PVC 10. 确认 PV 变为 Released 状态(因为 reclaimPolicy=Retain)
【评分标准】 - Provisioner 部署正确(25 分) - StorageClass 配置正确(20 分) - 动态供给工作正常(20 分) - 扩容成功 + 数据完整(25 分) - ReclaimPolicy 验证(10 分)
|